diff --git a/_data/sidebar.yml b/_data/sidebar.yml index cf88590299..9bc94921bd 100644 --- a/_data/sidebar.yml +++ b/_data/sidebar.yml @@ -1492,7 +1492,6 @@ sectionTitle: subgroup: 3 - - sbSecId: 5 title: Modules link: /prebid-server/pbs-modules/ @@ -1542,6 +1541,14 @@ sectionTitle: subgroup: 5 +- sbSecId: 5 + title: Building a Privacy Module + link: /prebid-server/developers/add-a-privacy-module.html + isHeader: 0 + isSectionHeader: 0 + sectionTitle: + subgroup: 5 + - sbSecId: 5 title: Code Reviews link: /prebid-server/developers/code-reviews.html diff --git a/prebid-server/developers/add-a-privacy-module-java.md b/prebid-server/developers/add-a-privacy-module-java.md new file mode 100644 index 0000000000..f09bca96f9 --- /dev/null +++ b/prebid-server/developers/add-a-privacy-module-java.md @@ -0,0 +1,282 @@ +--- +layout: page_v2 +sidebarType: 5 +title: Prebid Server | Developers | Adding a Java Privacy Module +--- + +# Prebid Server - Adding a Java Privacy Module +{: .no_toc} + +* TOC +{:toc } + +## Overview + +This document details how to make a 'privacy module' for PBS-Java. This type of +module is not related to the [main workflow modules](/prebid-server/pbs-modules/). Rather, it's a specialized type of module that + +You will want to be familiar with the following background information: + +* [Privacy Modules](/prebid-server/developers/add-a-privacy-module.html) +* [Prebid Server Activity Controls](/prebid-server/features/pbs-activitycontrols.html) + +## Coding Standards + +The module’s code style should correspond to +the [PBS-Java project code style](https://github.com/prebid/prebid-server-java/blob/master/docs/developers/code-style.md). + +### Privacy Module Directory Layout + +The source code of your privacy module must be inside packages: + +```text +// Privacy Module provider +org.prebid.server.activity.infrastructure.creator.privacy.YOUR_PRIVACY_MODULE_NAME + +// Privacy Module implementation +org.prebid.server.activity.infrastructure.privacy.YOUR_PRIVACY_MODULE_NAME + +// Account config for your Privacy Module (if presented as a single class, then a separate package isn’t required) +org.prebid.server.settings.model.activity.privacy.YOUR_PRIVACY_MODULE_NAME +``` + +### Module Code + +The quick start is to take a look in three places: + +* the [USNat Privacy Module](https://github.com/prebid/prebid-server-java/tree/master/src/main/java/org/prebid/server/activity/infrastructure/privacy/usnat) +* the [USNat Privacy Module creator](https://github.com/prebid/prebid-server-java/tree/master/src/main/java/org/prebid/server/activity/infrastructure/creator/privacy/usnat) +* the [account config for USNat Privacy Module](https://github.com/prebid/prebid-server-java/blob/master/src/main/java/org/prebid/server/settings/model/activity/privacy/AccountUSNatModuleConfig.java) + +{: .alert.alert-info :} +The 'usnat' code is documented on the website as [usgen](/prebid-server/features/pbs-usgen.html) + +## Privacy Module interface + +Among the Prebid server processing workflow, there are several 'activities' that require permission from the Activity +Infrastructure to run. + +The available activities that the Activity Infrastructure can control can be found in the corresponding +enum: [Activity](https://github.com/prebid/prebid-server-java/blob/master/src/main/java/org/prebid/server/activity/Activity.java). + +Whenever a workflow asks permission to perform an activity, the configured rules will be processed. All rules implement +the `Rule` interface. In accordance with this, every privacy module implements `PrivacyModule` interface, which +inherits `Rule` interface, with methods should be implemented: + +* `proceed(...)` - returns one of the privacy module answers: `ALLOW`, `DISALLOW`, `ABSTAIN`. + +### Privacy Module example + +```java +public class MyPrivacyModule implements PrivacyModule { + + private final GppModel gppModel; + private final int forbiddenSection; + + private MyPrivacyModule(GppModel gppModel, int forbiddenSection) { + this.gppModel = gppModel; + this.forbiddenSection = forbiddenSection; + } + + @Override + public Result proceed(ActivityInvocationPayload activityInvocationPayload) { + return gppModel != null && gppModel.hasSection(forbiddenSection) + ? Result.DISALLOW + : Result.ABSTAIN; + } +} +``` + +## Privacy Module Creator Interface + +The lifecycle of a privacy module is defined by `PrivacyModuleCreator`. Possible life cycles: + +* one for the server - if the creator always returns the same privacy module that was created when the server started +* one for a period of time (cached) - (for example) if the creator will create a new privacy module every time the + associated account is updated +* one per request - if the creator always returns a new privacy module + +`PrivacyModuleCreator` consists of methods that must be implemented: + +* `qualifier()` - returns privacy module qualifier. +* `from(...)` - returns created privacy module. + +### Privacy Module Creator Example + +```java +public class MyPrivacyModuleCreator implements PrivacyModuleCreator { + + @Override + public PrivacyModuleQualifier qualifier() { + return PrivacyModuleQualifier.MY_PRIVACY_MODULE; + } + + @Override + public PrivacyModule from(PrivacyModuleCreationContext creationContext) { + final MyPrivacyModuleConfig moduleConfig = moduleConfig(creationContext); + final GppModel gppModel = creationContext.getGppContext().scope().getGppModel(); + + final List innerPrivacyModules = SetUtils.emptyIfNull(moduleConfig.getForbiddenSections()) + .stream() + .filter(Objects::nonNull) + .map(forbiddenSection -> new MyPrivacyModule(gppModel, forbiddenSection)) + .toList(); + + return new AndPrivacyModules(innerPrivacyModules); + } + + private static MyPrivacyModuleConfig moduleConfig(PrivacyModuleCreationContext creationContext) { + return (MyPrivacyModuleConfig) creationContext.getPrivacyModuleConfig(); + } +} +``` + +## Privacy Module Qualifier + +`PrivacyModuleQualifier` is an enumeration containing all possible unique names of the privacy modules supported by this +server instance. + +### Privacy Module Qualifier Example + +```java +public enum PrivacyModuleQualifier { + + // other qualifiers + + @JsonProperty(Names.MY_PRIVACY_MODULE) // required when adding MY_PRIVACY_MODULE + MY_PRIVACY_MODULE(Names.MY_PRIVACY_MODULE); // required when adding MY_PRIVACY_MODULE + + // fields and methods + + public static class Names { + + // other names + + public static final String MY_PRIVACY_MODULE = "privacy.my-module"; // required when adding MY_PRIVACY_MODULE + } +} + +``` + +## Privacy Module Account Configuration + +Any privacy module must be configured in the account configuration to affect Prebid server processing workflow. + +When adding a new privacy module, it is important to create an appropriate configuration class. The configuration class +must implement the `AccountPrivacyModuleConfig` interface, with methods should be implemented: + +* `getCode()` - returns privacy module qualifier. +* `enabled()` - returns boolean. `null` or `true` means that this privacy module is 'on'. + +IMPORTANT. Because the Prebid server can merge account configurations from different locations, make sure: + +```text +deserializeFromJson(serializeToJson(config object)) == config object +``` + +### Privacy Module Account Configuration Example + +```java +@Value(staticConstructor = "of") +public class MyPrivacyModuleConfig implements AccountPrivacyModuleConfig { + + @Accessors(fluent = true) + Boolean enabled; + + Set forbiddenSections; + + @Override + public PrivacyModuleQualifier getCode() { + return PrivacyModuleQualifier.MY_PRIVACY_MODULE; + } +} +``` + +```java +package org.prebid.server.settings.model.activity.privacy; + + +@JsonSubTypes({ + // other privacy modules + + @JsonSubTypes.Type( // relationship between configuration class and privacy module name + value = MyPrivacyModuleConfig.class, // configuration class + name = PrivacyModuleQualifier.Names.MY_PRIVACY_MODULE)}) // privacy module name +public sealed interface AccountPrivacyModuleConfig permits + // other privacy modules + + MyPrivacyModuleConfig { // configuration class must be listed after 'permits' keyword + + // methods +} +``` + +## Privacy Module Bean Configuration + +Privacy module beans must be inside the destined configuration class: `ActivityInfrastructureConfiguration.PrivacyModuleCreatorConfiguration` + +### Privacy Module Bean Configuration Example + +If there is only one bean associated with the privacy module: + +```java +@Configuration +static class PrivacyModuleCreatorConfiguration { + + // other privacy modules + + @Bean + MyPrivacyModuleCreator myPrivacyModuleCreator() { + return new MyPrivacyModuleCreator(); + } +} +``` + +If there are multiple beans associated with the privacy module: + +```java +@Configuration +static class PrivacyModuleCreatorConfiguration { + + // other privacy modules + + @Configuration + static class MyPrivacyModuleCreatorConfiguration { + + @Bean + MyPrivacyModuleDependency myPrivacyModuleDependency() { + return new MyPrivacyModuleDependency(); + } + + @Bean + MyPrivacyModuleCreator myPrivacyModuleCreator(MyPrivacyModuleDependency myPrivacyModuleDependency) { + return new MyPrivacyModuleCreator(myPrivacyModuleDependency); + } + } +} +``` + +## Adding support for trace log + +To be able to debug the Activity Infrastructure and be able to track interactions with your privacy module, it is recommended that your `PrivacyModule` implement the `Loggable` interface. + +`Loggable` consists of methods that must be implemented: + +* `asLogEntry(...)` - returns `JsonNode` that can represent any desired structure to include in the trace log. + +For example: + +```java +public class MyPrivacyModule implements PrivacyModule, Loggable { + + // privacy module code + + @Override + public JsonNode asLogEntry(ObjectMapper mapper) { + return TextNode.valueOf( + "%s forbidding %d section.".formatted( + MyPrivacyModule.class.getSimpleName(), + forbiddenSection)); + } +} +``` diff --git a/prebid-server/developers/add-a-privacy-module.md b/prebid-server/developers/add-a-privacy-module.md new file mode 100644 index 0000000000..f4cea9ba6f --- /dev/null +++ b/prebid-server/developers/add-a-privacy-module.md @@ -0,0 +1,131 @@ +--- +layout: page_v2 +sidebarType: 5 +title: Prebid Server | Developers | Adding a Privacy Module + +--- + +# Prebid Server - Adding a Privacy Module +{: .no_toc} + +{: .alert.alert-warning :} +This feature is currently only available in PBS-Java. + +* TOC +{:toc } + +## Overview + +Privacy Modules are different than [Request Modules](/prebid-server/developers/add-a-module.html). They work in this way: + +1. Privacy Modules are called by the [Activity Control System](/prebid-server/features/pbs-activitycontrols.html) +1. They are meant to use aspects of the request to determine whether a particular activity is `allowed`, `disallowed`, or `abstain`. + +Here are the use cases envisioned for Privacy Modules: + +* Prebid will publish privacy modules for major IAB privacy protocols such as the [US National Privacy Specification](/prebid-server/features/pbs-usgen.html). +* PBS host companies can develop custom versions of privacy modules for their publishers that may meet special legal requirements more efficiently. +* Anyone can contribute privacy modules in support of regulations not addressed by Prebid or the IAB. + +### Terminology + +* **PBS**: short for **P**re**b**id **S**erver +* **PBS-core**: The inner workings of Prebid Server -- not part of a module, bid adpater, or analytics adapter +* **PBS-Java**: the Java version of Prebid Server +* **PBS-Go**: the Go-Lang version of Prebid Server +* **Host Company**: the entity running the PBS cluster, e.g. one of the ones on [this list](https://prebid.org/product-suite/managed-services/). +* **Activity Controls**: a [centralized mechanism](/prebid-server/features/pbs-activitycontrols.html) for managing privacy-sensitive activities. +* **Privacy Module**: a block of code that plugs into Prebid Server that enhances the functionality of the Activity Controls. +* **Allow**: If the module returns this value, it has determined that the requested activity in the specified context is allowable. +* **Disallow**: If the module returns this value, it has determined that the requested activity in the specified context is **not** allowable. +* **Abstain**: If the module returns this value, it does not have a definitive answer about whether the requested activity in the specified context is allowable. + +## Building Your Privacy Module + +### 1. Define the Behavior With Your Lawyers + +As with any legally sensitive thing, you should have the desired behavior fully documented and signed off in conjunction with legal counsel. + +{: .alert.alert-warning :} +Prebid cannot guarantee that the Activity Controls and Privacy Modules enable all possible legal scenarios. Please submit an [issue](https://github.com/prebid/prebid-server/issues/new) to discuss +enhancements to this system. + +### 2. Review the Module Rules + +There are a number of things modules in general are not allowed to do +without disclosing prominently on their documentation. Please review +the [Module Rules](/dev-docs/module-rules.html) page. + +Privacy Modules are particularly constrained in what they can do. Basically all they can do is answer `allow`, `disallow`, or `abstain` to a request from an Activity Control. +They cannot make HTTP requests, log analytics, or affect the request/response in any way. + +### 2. Define a Module Code + +The module code is how Activity Control configuration will refer to this +privacy module. For example, if the module is named **host1.publisherA.emea**, +it could be activated in the `privacy` config in any of these ways: + +```javascript +{ + "privacy": { + "allowactivities": { + "ACTIVITY1": { + "privacyreg": ["*"] + }, + "ACTIVITY2": { + "privacyreg": ["host1.*"] + }, + "ACTIVITY3": { + "privacyreg": ["host1.publisherA.*"] + }, + "ACTIVITY4": { + "privacyreg": ["host1.publisherA.emea"] + } + } + } +} +``` + +To choose the name, you should consider how the publisher may want to invoke +the privacy modules that are available. + +* If you're not going to open source the privacy module, we recommend prefixing the name with your host company so it doesn't clash with open source modules as they become available. +* If the module is publisher-specific, we recommend placing the publisher name in the module code. + +### 3. Determine What Should be Configurable + +Your module may not need any configuration, or it may have a complex configuration. +Here are the kind of things to consider: + +* Does it need to identify or prioritize privacy parameters differently? (e.g. which consent and scope strings to use and prefer?) +* Does it need to provide different exceptions? (e.g. if a particular publisher wants to allow or disallow certain scenarios.) + +### 4. Write the Code, Config, and Unit Tests + +The details of the implementation depend on the platform. + +* PBS-Java: see [Adding a PBS-Java Privacy Module](/prebid-server/developers/add-a-privacy-module-java.html) +* PBS-Go: TBD + +If you plan on open sourcing your privacy module, other rules for open source PBS pull request: + +* Unit test coverage must exceed 90%. +* A maintainer email address must be provided and be a group, not an individual. e.g. rather than + +### 5. Write the Module Documentation + +If this is an open source module, fork the [documentation repo](https://github.com/prebid/prebid.github.io) and +create a file in /prebid-server/pbs-modules. You can start by copying one of the existing files. It should contain: + +* A description of the module functionality: why people might be interested in using it. +* Prerequisites: any necessary account activation, other required modules, etc. +* Configuration + +### 6. Submit the Pull Requests + +If open sourcing the module, submit the PRs for review when everything looks good in your test environment. + +## Further Reading + +* [PBS Activity Controls](/prebid-server/features/pbs-activitycontrols.html) +* [US Gen Privacy Module](/prebid-server/features/pbs-usgen.html) diff --git a/prebid-server/features/pbs-activitycontrols.md b/prebid-server/features/pbs-activitycontrols.md index 746ddf6619..09b15cf5d0 100644 --- a/prebid-server/features/pbs-activitycontrols.md +++ b/prebid-server/features/pbs-activitycontrols.md @@ -9,7 +9,7 @@ title: Prebid Server | Features | Actvity Controls {: .no_toc } {: .alert.alert-warning :} -This feature is currently only available in PBS-Java. +PBS-Java fully supports Activity Controls. PBS-Go support for the main request workflow is complete. They are working on support for Activities in modules and analytics. Prebid supports a centralized control mechanism for privacy-sensitive activities. These controls are intended to serve as building blocks for privacy protection mechanisms, allowing publishers to directly specify what should be permitted or avoided in any given regulatory environment.