Skip to content

Commit b3429de

Browse files
CTMBNaraddubykbretg
authored
Add Adding a Java Privacy Module section. (prebid#4808)
* Initial version for `Adding a Java Privacy Module` section. * Fix linter issues. * Add `Bean configuration` and `Trace log` points. * Fix linter issue. * Fix linter issue. * general privacy module background, links * linting fixes * linting fixes --------- Co-authored-by: ddubyk <ddubyk@magnite.com> Co-authored-by: bretg <bgorsline@gmail.com>
1 parent 89ac96c commit b3429de

4 files changed

Lines changed: 422 additions & 2 deletions

File tree

_data/sidebar.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1492,7 +1492,6 @@
14921492
sectionTitle:
14931493
subgroup: 3
14941494

1495-
14961495
- sbSecId: 5
14971496
title: Modules
14981497
link: /prebid-server/pbs-modules/
@@ -1542,6 +1541,14 @@
15421541
sectionTitle:
15431542
subgroup: 5
15441543

1544+
- sbSecId: 5
1545+
title: Building a Privacy Module
1546+
link: /prebid-server/developers/add-a-privacy-module.html
1547+
isHeader: 0
1548+
isSectionHeader: 0
1549+
sectionTitle:
1550+
subgroup: 5
1551+
15451552
- sbSecId: 5
15461553
title: Code Reviews
15471554
link: /prebid-server/developers/code-reviews.html
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
---
2+
layout: page_v2
3+
sidebarType: 5
4+
title: Prebid Server | Developers | Adding a Java Privacy Module
5+
---
6+
7+
# Prebid Server - Adding a Java Privacy Module
8+
{: .no_toc}
9+
10+
* TOC
11+
{:toc }
12+
13+
## Overview
14+
15+
This document details how to make a 'privacy module' for PBS-Java. This type of
16+
module is not related to the [main workflow modules](/prebid-server/pbs-modules/). Rather, it's a specialized type of module that
17+
18+
You will want to be familiar with the following background information:
19+
20+
* [Privacy Modules](/prebid-server/developers/add-a-privacy-module.html)
21+
* [Prebid Server Activity Controls](/prebid-server/features/pbs-activitycontrols.html)
22+
23+
## Coding Standards
24+
25+
The module’s code style should correspond to
26+
the [PBS-Java project code style](https://github.com/prebid/prebid-server-java/blob/master/docs/developers/code-style.md).
27+
28+
### Privacy Module Directory Layout
29+
30+
The source code of your privacy module must be inside packages:
31+
32+
```text
33+
// Privacy Module provider
34+
org.prebid.server.activity.infrastructure.creator.privacy.YOUR_PRIVACY_MODULE_NAME
35+
36+
// Privacy Module implementation
37+
org.prebid.server.activity.infrastructure.privacy.YOUR_PRIVACY_MODULE_NAME
38+
39+
// Account config for your Privacy Module (if presented as a single class, then a separate package isn’t required)
40+
org.prebid.server.settings.model.activity.privacy.YOUR_PRIVACY_MODULE_NAME
41+
```
42+
43+
### Module Code
44+
45+
The quick start is to take a look in three places:
46+
47+
* the [USNat Privacy Module](https://github.com/prebid/prebid-server-java/tree/master/src/main/java/org/prebid/server/activity/infrastructure/privacy/usnat)
48+
* 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)
49+
* 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)
50+
51+
{: .alert.alert-info :}
52+
The 'usnat' code is documented on the website as [usgen](/prebid-server/features/pbs-usgen.html)
53+
54+
## Privacy Module interface
55+
56+
Among the Prebid server processing workflow, there are several 'activities' that require permission from the Activity
57+
Infrastructure to run.
58+
59+
The available activities that the Activity Infrastructure can control can be found in the corresponding
60+
enum: [Activity](https://github.com/prebid/prebid-server-java/blob/master/src/main/java/org/prebid/server/activity/Activity.java).
61+
62+
Whenever a workflow asks permission to perform an activity, the configured rules will be processed. All rules implement
63+
the `Rule` interface. In accordance with this, every privacy module implements `PrivacyModule` interface, which
64+
inherits `Rule` interface, with methods should be implemented:
65+
66+
* `proceed(...)` - returns one of the privacy module answers: `ALLOW`, `DISALLOW`, `ABSTAIN`.
67+
68+
### Privacy Module example
69+
70+
```java
71+
public class MyPrivacyModule implements PrivacyModule {
72+
73+
private final GppModel gppModel;
74+
private final int forbiddenSection;
75+
76+
private MyPrivacyModule(GppModel gppModel, int forbiddenSection) {
77+
this.gppModel = gppModel;
78+
this.forbiddenSection = forbiddenSection;
79+
}
80+
81+
@Override
82+
public Result proceed(ActivityInvocationPayload activityInvocationPayload) {
83+
return gppModel != null && gppModel.hasSection(forbiddenSection)
84+
? Result.DISALLOW
85+
: Result.ABSTAIN;
86+
}
87+
}
88+
```
89+
90+
## Privacy Module Creator Interface
91+
92+
The lifecycle of a privacy module is defined by `PrivacyModuleCreator`. Possible life cycles:
93+
94+
* one for the server - if the creator always returns the same privacy module that was created when the server started
95+
* one for a period of time (cached) - (for example) if the creator will create a new privacy module every time the
96+
associated account is updated
97+
* one per request - if the creator always returns a new privacy module
98+
99+
`PrivacyModuleCreator` consists of methods that must be implemented:
100+
101+
* `qualifier()` - returns privacy module qualifier.
102+
* `from(...)` - returns created privacy module.
103+
104+
### Privacy Module Creator Example
105+
106+
```java
107+
public class MyPrivacyModuleCreator implements PrivacyModuleCreator {
108+
109+
@Override
110+
public PrivacyModuleQualifier qualifier() {
111+
return PrivacyModuleQualifier.MY_PRIVACY_MODULE;
112+
}
113+
114+
@Override
115+
public PrivacyModule from(PrivacyModuleCreationContext creationContext) {
116+
final MyPrivacyModuleConfig moduleConfig = moduleConfig(creationContext);
117+
final GppModel gppModel = creationContext.getGppContext().scope().getGppModel();
118+
119+
final List<PrivacyModule> innerPrivacyModules = SetUtils.emptyIfNull(moduleConfig.getForbiddenSections())
120+
.stream()
121+
.filter(Objects::nonNull)
122+
.map(forbiddenSection -> new MyPrivacyModule(gppModel, forbiddenSection))
123+
.toList();
124+
125+
return new AndPrivacyModules(innerPrivacyModules);
126+
}
127+
128+
private static MyPrivacyModuleConfig moduleConfig(PrivacyModuleCreationContext creationContext) {
129+
return (MyPrivacyModuleConfig) creationContext.getPrivacyModuleConfig();
130+
}
131+
}
132+
```
133+
134+
## Privacy Module Qualifier
135+
136+
`PrivacyModuleQualifier` is an enumeration containing all possible unique names of the privacy modules supported by this
137+
server instance.
138+
139+
### Privacy Module Qualifier Example
140+
141+
```java
142+
public enum PrivacyModuleQualifier {
143+
144+
// other qualifiers
145+
146+
@JsonProperty(Names.MY_PRIVACY_MODULE) // required when adding MY_PRIVACY_MODULE
147+
MY_PRIVACY_MODULE(Names.MY_PRIVACY_MODULE); // required when adding MY_PRIVACY_MODULE
148+
149+
// fields and methods
150+
151+
public static class Names {
152+
153+
// other names
154+
155+
public static final String MY_PRIVACY_MODULE = "privacy.my-module"; // required when adding MY_PRIVACY_MODULE
156+
}
157+
}
158+
159+
```
160+
161+
## Privacy Module Account Configuration
162+
163+
Any privacy module must be configured in the account configuration to affect Prebid server processing workflow.
164+
165+
When adding a new privacy module, it is important to create an appropriate configuration class. The configuration class
166+
must implement the `AccountPrivacyModuleConfig` interface, with methods should be implemented:
167+
168+
* `getCode()` - returns privacy module qualifier.
169+
* `enabled()` - returns boolean. `null` or `true` means that this privacy module is 'on'.
170+
171+
IMPORTANT. Because the Prebid server can merge account configurations from different locations, make sure:
172+
173+
```text
174+
deserializeFromJson(serializeToJson(config object)) == config object
175+
```
176+
177+
### Privacy Module Account Configuration Example
178+
179+
```java
180+
@Value(staticConstructor = "of")
181+
public class MyPrivacyModuleConfig implements AccountPrivacyModuleConfig {
182+
183+
@Accessors(fluent = true)
184+
Boolean enabled;
185+
186+
Set<Integer> forbiddenSections;
187+
188+
@Override
189+
public PrivacyModuleQualifier getCode() {
190+
return PrivacyModuleQualifier.MY_PRIVACY_MODULE;
191+
}
192+
}
193+
```
194+
195+
```java
196+
package org.prebid.server.settings.model.activity.privacy;
197+
198+
199+
@JsonSubTypes({
200+
// other privacy modules
201+
202+
@JsonSubTypes.Type( // relationship between configuration class and privacy module name
203+
value = MyPrivacyModuleConfig.class, // configuration class
204+
name = PrivacyModuleQualifier.Names.MY_PRIVACY_MODULE)}) // privacy module name
205+
public sealed interface AccountPrivacyModuleConfig permits
206+
// other privacy modules
207+
208+
MyPrivacyModuleConfig { // configuration class must be listed after 'permits' keyword
209+
210+
// methods
211+
}
212+
```
213+
214+
## Privacy Module Bean Configuration
215+
216+
Privacy module beans must be inside the destined configuration class: `ActivityInfrastructureConfiguration.PrivacyModuleCreatorConfiguration`
217+
218+
### Privacy Module Bean Configuration Example
219+
220+
If there is only one bean associated with the privacy module:
221+
222+
```java
223+
@Configuration
224+
static class PrivacyModuleCreatorConfiguration {
225+
226+
// other privacy modules
227+
228+
@Bean
229+
MyPrivacyModuleCreator myPrivacyModuleCreator() {
230+
return new MyPrivacyModuleCreator();
231+
}
232+
}
233+
```
234+
235+
If there are multiple beans associated with the privacy module:
236+
237+
```java
238+
@Configuration
239+
static class PrivacyModuleCreatorConfiguration {
240+
241+
// other privacy modules
242+
243+
@Configuration
244+
static class MyPrivacyModuleCreatorConfiguration {
245+
246+
@Bean
247+
MyPrivacyModuleDependency myPrivacyModuleDependency() {
248+
return new MyPrivacyModuleDependency();
249+
}
250+
251+
@Bean
252+
MyPrivacyModuleCreator myPrivacyModuleCreator(MyPrivacyModuleDependency myPrivacyModuleDependency) {
253+
return new MyPrivacyModuleCreator(myPrivacyModuleDependency);
254+
}
255+
}
256+
}
257+
```
258+
259+
## Adding support for trace log
260+
261+
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.
262+
263+
`Loggable` consists of methods that must be implemented:
264+
265+
* `asLogEntry(...)` - returns `JsonNode` that can represent any desired structure to include in the trace log.
266+
267+
For example:
268+
269+
```java
270+
public class MyPrivacyModule implements PrivacyModule, Loggable {
271+
272+
// privacy module code
273+
274+
@Override
275+
public JsonNode asLogEntry(ObjectMapper mapper) {
276+
return TextNode.valueOf(
277+
"%s forbidding %d section.".formatted(
278+
MyPrivacyModule.class.getSimpleName(),
279+
forbiddenSection));
280+
}
281+
}
282+
```

0 commit comments

Comments
 (0)