Overview
MY DATA Control Technologies Library brings MY DATA Control Technologies to your Java application. Regardless of whether you want to use it locally inside a single application, on-premise or with our cloud services. MY DATA Control Technologies supports four operational modes and provides you a strong API to easily integrate MY DATA Control Technologies in your system.
Operational Modes
The four operational modes:
-
Local: In this mode, PMP, PDP and PEPs reside in the same Java application. PIPs and PXPs can be part of this Java application ("local components") or reside outside the Java application. In the latter case, they will have to be accessible via HTTP and will be registered in the Library as "unmanaged components".
Policies, timers and components are not persisted in this mode. You will have to add and deploy your policies on each application startup. You will also have to provide and register the components on every startup. |
-
Local with File-Sync: This mode is quite similar to the local mode. But in this mode, the set of deployed policies and timers is managed via the filesystem. They are persisted as XML formatted files in a configurable directory and thus will survive an application restart. Nevertheless, components still need to be provided and registered on every startup.
File-Sync relies on custom file extensions to distinguish policies and timers.
Use .mdpx extension for policies and .mdtx extension for timers.
|
-
Local with Cloud-Sync: This mode is quite similar to the local mode. But in this mode, the set of deployed policies and timers is managed by a Cloud-PMP ("Management Service"). You can add, deploy, revoke and delete the policies and timers there and your application instances (we call them "library-clients") will periodically synchronize with the Cloud-PMP. Nevertheless, components still need to be provided and registered on every startup as the whole processing resides in your application instance.
To provide rich information about the available PEP/PIP/PXPs, exactly one library-client can be promoted as "master-library-client". The master-library-client will automatically push any component registration to the Cloud-PMP. This information can be used - for example - by a policy editor to support you specifying policies by suggesting available elements (e.g. events, actions, information sources, available modifiers). |
-
Cloud: In this mode, the Library uses Cloud-PMP and Cloud-PDP. This means: Policy evaluation and decision making is done by remote components. One may ask what benefits come in when choosing this operational mode:
-
persistent policies, timers and component registrations
-
support for multi-tenancy (using Affiliations and Solutions)
-
easier access to the log files
-
scale the PDP according to your needs
-
your application instances will need less resources
-
you can profit from a unified EventHistory (in contrast: when using Local-PDP, each one will have its own "local" database and counting)
-
But these benefits do not come at no cost:
-
need for a steady connection to the cloud
-
increase in network traffic and response time
-
probably sensitive data will be transferred to remote locations (need to secure that communication and trust the remote server)
-
PIPs and PXPs need to be accessible for the Cloud-PMP and Cloud-PDP (e.g. via HTTP) and thus need to be exposed appropriately.
In Cloud mode it makes no sense to use "local components" - indeed, they are only accessible inside your application and the cloud can not communicate with them. When you want to expose a PIP or PXP, you can use our mechanism for "managed components" (see Component Registration).
You do not necessarily need to bundle all the PIPs and PXPs with your application. Subsequent registrations of the same component will override the existing registration. Just make sure, that they are available and registered at runtime. When you want to scale your PIP/PXPs, you will have to provide a load balancer and register its address to the Cloud-PMP (instead of the address of a concrete instance). |
The IMyDataEnvironment
Interface
IMyDataEnvironment
encapsulates the MY DATA Control Technologies components and supports the previously introduced operation modes.
It is a central part of the MY DATA Control Technologies Library and enables developers to implement PEPs, PIPs and PXPs.
To do so, it provides access to the PMP and some really practical methods to ease the use.
You can find the corresponding API-Documentation here: IMyDataEnvironment
The following sections describe how to use it.
About sdk
and sdk.spring
Maven Artifacts
MY DATA Control Technologies Library can be used with or without Spring. If you use Spring, you will benefit from numerous features and simplified functions. For example:
-
Autoconfiguration and Initialization of the Library.
-
Autoregistration and Instantiation of PEPs
-
Autoregistration and Instantiation of PIPs and PXPs.
-
Exposure of PIPs/PXPs as remote components when using cloud mode.
-
Event History feature to enhance the supported expressiveness of policies.
Configuration without Spring
If you choose to use it without Spring, please adapt the following configurations:
In your application pom.xml
:
properties
Section<mydata.version>4.0.0</mydata.version>
dependencies
Section<dependency>
<groupId>de.fraunhofer.iese.mydata</groupId>
<artifactId>sdk</artifactId>
<version>${mydata.version}</version>
</dependency>
Additionally, the pdp
dependency has to be added if your application uses one of our local modes:
<dependency>
<groupId>de.fraunhofer.iese.mydata</groupId>
<artifactId>pdp</artifactId>
<version>${mydata.version}</version>
</dependency>
This artifact contains the components necessary for local policy evaluation and decision-making.
The connectors.rest
dependency has to be added if your application uses the cloud-sync mode, cloud mode, or if it needs remote components accessible via HTTP:
<dependency>
<groupId>de.fraunhofer.iese.mydata</groupId>
<artifactId>connectors.rest</artifactId>
<version>${mydata.version}</version>
</dependency>
This artifact contains the connectors required to communicate with MY DATA Control Technologies components via HTTP.
Configuration with Spring
To use the MY DATA Control Technologies Library in combination with Spring, simply add this dependency (additional to the items from the previous section) to your pom.xml
:
<dependency>
<groupId>de.fraunhofer.iese.mydata</groupId>
<artifactId>sdk.spring</artifactId>
<version>${mydata.version}</version>
</dependency>
If you are using the library in cloud mode and want to benefit from automatic exposure of PIP/PXP components, you also have to add a dependency for spring-boot-starter-web .
|
Working with the Library
Using the Library without Spring
If you decided to use the MY DATA Control Technologies Library without Spring, you have to configure and initialize the
IMyDataEnvironment
by yourself:
Manual Instantiation of the IMyDataEnvironment
Let’s assume that you want to use the MY DATA Control Technologies Library in local mode:
IMyDataEnvironment myDataEnvironment = MyDataEnvironmentManager
.constructDefaultEnvironment()
.initializeLocal(
new SolutionId("urn:solution:my-solution"), // The solutionId
"Europe/Berlin", // Timezone
4, // Number of Threads for Policy Evaluation by the PDP
false, // whether whitelistMode should be enabled
null // instance of IEventRepository to support Event History
);
That’s it, you successfully initialized the MY DATA Control Technologies Library.
Please note that we supply null as a parameter to the initialize method as the Event History is currently only available in combination with Spring.
For further information on how to enable this feature with our sdk.spring , please have a look at: Event History.
|
In order to initialize the MY DATA Control Technologies Library with another operational mode, just choose the appropriate initialize method and provide the required parameters. A more detailed documentation about the available initializer methods and the required arguments can be found in the corresponding Javadoc.
Some more examples:
IMyDataEnvironment myDataEnvironment = MyDataEnvironmentManager
.constructDefaultEnvironment()
.initializeLocalWithFileSync(
new SolutionId("urn:solution:my-solution"), // The solutionId
"Europe/Berlin", // Timezone
"data", // file-sync path
4, // Number of Threads for Policy Evaluation by the PDP
false, // whether whitelistMode should be enabled
null // instance of IEventRepository to support Event History
);
IMyDataEnvironment myDataEnvironment = MyDataEnvironmentManager
.constructDefaultEnvironment()
.initializeLocalWithCloudSync(
new SolutionId("urn:solution:my-solution"), // The solutionId
URI.create("https://management.mydata-control.de"), // Cloud-PMP
new OAuthCredentials(
new ClientId("urn:client:my-solution:my-client"),
"my-client-secret",
URI.create("https://management.mydata-control.de")
),
"Europe/Berlin", // Timezone
true, // enable cache
"policyCachePath.json", // policy cache file
"timerCachePath.json", // timer cache file
"PT30M", // maxPolicyAge: 30 minutes
"0 0/5 * * * ?", // syncSchedule: every 5 minutes
true, // this client is a master-library-client
4, // Number of Threads for Policy Evaluation by the PDP
false, // whether whitelistMode should be enabled
null // instance of IEventRepository to support Event History
);
IMyDataEnvironment myDataEnvironment = MyDataEnvironmentManager
.constructDefaultEnvironment()
.initializeCloud(
new SolutionId("urn:solution:my-solution"), // The solutionId
URI.create("https://management.mydata-control.de"), // Cloud-PMP
new OAuthCredentials(
new ClientId("urn:client:my-solution:my-client"),
"my-client-secret",
URI.create("https://management.mydata-control.de")
),
);
Retrieval of the initialized IMyDataEnvironment
instance
You can retrieve the initialized IMyDataEnvironment
instance from anywhere in the Code via MyDataEnvironmentManager.getDefaultEnvironment()
:
IMyDataEnvironment myDataEnvironment = MyDataEnvironmentManager.getDefaultEnvironment();
Be aware of the fact that trying to retrieve the instance without previously initializing it will result in an IllegalStateException .
|
Implementing Components (e.g. PIPs and PXPs)
In this section will be shown, how to implement MY DATA Control Technologies components (PIPs and PXPs).
Policy Information Point (PIP)
To learn more about Policy Information Points, click here.
public class AuthorityPip {
@ActionDescription
public String getAuthority(
@ActionParameterDescription(name = "username", mandatory = true) String username
) {
// implement your logic here
return "guest"; // and return the information
}
}
Policy Execution Point (PXP)
To learn more about Policy Execution Points, click here.
public class MailPxp {
public MailPxp() {
}
@ActionDescription
public boolean sendPlainMail(
@ActionParameterDescription(name = "recipient", mandatory = true) final String recipient,
@ActionParameterDescription(name = "subject", mandatory = true) final String subject,
@ActionParameterDescription(name = "message", mandatory = true) final String message
) {
// Implementation omitted
return true; // indicate success
}
}
Registration of Components (e.g. PIPs and PXPs)
You will have to pay special attention when registering PIP/PXP components because they need to be accessible to the PDP. Currently we distinguish three modes in which a service component can be registered to the IMyDataEnvironment
:
-
local: component instance will be registered to the MyDataEnvironment, will be accessible to local PDP and component information will be published to PMP; no need to provide further information like URL. →
registerLocalPip(…,…)
andregisterLocalPxp(…,…)
-
managed: component instance will be registered to the MyDataEnvironment, can programmatically be retrieved via
getManagedPip(…)
/getManagedPxp(…)
and the corresponding component information is published to the PMP. →registerManagedPip(…,…,…)
andregisterManagedPxp(…,…,…)
You are in charge to make the component accessible for the PDP under the specified URL. -
unmanaged: component will not be managed by the MyDataEnvironment, only the component information is published to the PMP. →
registerUnmanagedPip(…)
andregisterUnmanagedPxp(…)
You are in charge to make the component accessible for the PDP.
In case of managed and unmanaged component registrations: You are in charge to make the component accessible for the PDP. |
Simple example:
IMyDataEnvironment myDataEnvironment = MyDataEnvironmentManager.getDefaultEnvironment();
try {
final ComponentId componentId = myDataEnvironment.registerLocalPip("authoritypip", new AuthorityPip());
} catch (InvalidEntityException | ResourceUpdateException | ConflictingResourceException | IOException e) {
// place proper exception handling here
LOG.error(e.getMessage(), e);
}
The PIP action can be referenced as urn:info:my-solution:getAuthority
in the policies.
The solution identifier is determined by the configuration of the IMyDataEnvironment.
Analogous the ComponentId of the PIP is composed of the solution of the IMyDataEnvironment and the supplied componentName.
In this example, the resulting componentId will be urn:component:my-solution:pip:authoritypip
.
Deploying Policies
You can deploy policies programmatically by using the PMP:
IMyDataEnvironment myDataEnvironment = MyDataEnvironmentManager.getDefaultEnvironment();
IBasicManagementService pmp = myDataEnvironment.getPmp();
final String mydataPolicyString = "<policy ...>...</policy>";
try {
Policy policy = new Policy(mydataPolicyString)
PolicyId policyId = pmp.addPolicy(policy);
pmp.deployPolicy(policyId);
} catch (IOException | ResourceUpdateException | InvalidEntityException | ConflictingResourceException | NoSuchEntityException e) {
// place proper exception handling here
LOG.error(e.getMessage(), e);
}
To learn more about our policy language, have a look at: MY DATA Control Technologies Policy Language
Enforce Data with a Policy Enforcement Point (PEP)
To enforce the policies you can use a PEP. A PEP roughly works as follows:
-
You call the PEP with the data to enforce.
-
PEP asks PDP for an
AuthorizationDecision
that contains information on how to proceed with the data according to the currently deployed policies. -
PEP applies the
AuthorizationDecision
(the contained instructions) to the data. -
PEP returns modified data or throws an Exception (e.g., in case of inhibition decision).
To learn more about MY DATA Control Technologies PEPs, click here.
Enforce Data using a generic PEP
The IMyDataEnvironment
instance comes with a generic default PEP that you can use to enforce Event
objects.
IMyDataEnvironment myDataEnvironment = MyDataEnvironmentManager.getDefaultEnvironment();
IPolicyEnforcementPoint pep = myDataEnvironment.getPep();
User u = new User("John Doe");
Event event = new EventBuilder("my-solution", "my-action").withParameter("user", u, User.class).getEvent();
try {
pep.enforce(event);
User enforcedUserObject = (User) event.getValueForName("user");
System.out.println(enforcedUserObject.getName());
} catch (InhibitException | EvaluationUndecidableException | IOException e) {
// place proper exception handling here
System.out.println("Access denied");
}
There are several Exceptions that can occur when enforcing the data.
For example an InhibitException will be thrown to signal the decision to inhibit the access.
Make sure you handle these Exceptions appropriately.
|
Enforce Data using a custom reactive PEP
MY DATA Control Technologies Library supports custom reactive (i.e., "home-made") PEPs. You just need to declare its interface with the according annotations:
@Modifiers // add all available modifiers
public interface MyPep {
@EventSpecification(action = "my-action")
Observable<Event> enforceUser(@EventParameter(name = "user") User user);
}
Be aware of the fact that every parameter needs to be annotated with @EventParameter .
|
IMyDataEnvironment myDataEnvironment = MyDataEnvironmentManager.getDefaultEnvironment();
MyPep myPep;
try{
myPep = myDataEnvironment.constructAndRegisterCustomPep("my-pep", MyPep.class);
} catch (InitializationException e) {
// proper exception handling
// rethrow or initialize myPep with a valid fallback
}
// myPep ready to be used
Call the IMyDataEnvironment::constructAndRegisterCustomPep at most once per componentId / componentName / interface. |
MyPep myPep = ...;
User u = new User("John Doe");
final User enforcedUser;
try {
final Observable<Event> eventObservable = myPep.enforceUser(u); // enforce
final Event enforcedEvent = eventObservable.toBlocking().first(); // may throw RuntimeException
enforcedUser = (User) enforcedEvent.getValueForName("user");
} catch (RuntimeException e) {
if (e.getCause() instanceof InhibitException) {
final String msg = e.getCause().getMessage();
throw new AccessPermissionDeniedException("PDP decided to inhibit the access" + (msg != null ? ": " + msg : "."), e.getCause());
} else if (e.getCause() instanceof EvaluationUndecidableException) {
throw new AccessPermissionDeniedException("PDP reported that the evaluation is undecidable. This will result in inhibition.", e.getCause());
} else if (e.getCause() instanceof IOException) {
throw new AccessPermissionDeniedException("There is a problem with the communication channel to the PDP. This will result in inhibition.", e.getCause());
} else {
LOG.error("Unexpected Exception occured", e);
throw new AccessPermissionDeniedException("Internal error, have a look into the log files. For now: This will result in inhibition.");
}
}
System.out.println(enforcedUser.getName());
The checked Exceptions a "normal PEP" declares to throw will become unchecked RuntimeExceptions when leaving the "reactive world" by blocking for a result.
Your IDE will not support you in catching all relevant Exceptions.
To overcome this drawback we provide a helper function (MyDataUtil::checkedBlockingGet ) that enables you to block safely for a result.
To do so, it has several throws declarations to assist you in appropriately declaring the catch clauses.
|
MyPep myPep = ...;
User u = new User("John Doe");
final User enforcedUser;
try {
final Event event = MyDataUtil.checkedBlockingGet(myPep.enforceUser(user));
enforcedUser = (User) enforcedEvent.getValueForName("user");
} catch (IOException e) {
throw new AccessPermissionDeniedException("There is a problem with the communication channel to the PDP. This will result in inhibition.", e);
} catch (InhibitException e) {
final String msg = e.getMessage();
throw new AccessPermissionDeniedException("PDP decided to inhibit the access" + (msg != null ? ": " + msg : "."), e);
} catch (EvaluationUndecidableException e) {
throw new AccessPermissionDeniedException("PDP reported that the evaluation is undecidable. This will result in inhibition.", e);
} catch (RuntimeException e) { // catch any other RuntimeExceptions
LOG.error("Exception occured", e);
throw new AccessPermissionDeniedException("Internal error, have a look into the log files. For now: This will result in inhibition.");
}
System.out.println(enforcedUser.getName());
You may wonder why we provide reactive PEPs when blocking for their result. Sometimes we have to do this to support legacy APIs that do not support Observables and need the enforced data to proceed. When you use reactive programming in your application and you do not have to block for the results, everything is fine. Just keep an eye on appropriate error handling.
Using the Library with Spring
In order to use MY DATA Control Technologies Library with Spring, you need to include the appropriate maven dependency.
Configuration of the Library (application.yml)
You can configure the settings such as the operational mode via the .yml
configuration file.
Property | Relevant for Op-Modes | Default Value | Possible Values | Description |
---|---|---|---|---|
mydata.operational-mode |
* |
local |
local, local-with-file-sync, local-with-cloud-sync, cloud |
Determines the operational mode of the library |
mydata.solution |
* |
urn:solution:default |
urn:solution:* |
Determines the SolutionId of the default MyDataEnvironment |
mydata.timezone |
local, local-with-file-sync, local-with-cloud-sync |
Europe/Berlin |
ZoneId-Strings |
Determines the timezone of the solution of the default MyDataEnvironment |
mydata.pdp.num-threads |
local, local-with-file-sync, local-with-cloud-sync |
4 |
integer, >=1 |
How many threads to use in the PDP |
mydata.pdp.enable-whitelist-mode |
local, local-with-file-sync, local-with-cloud-sync |
false |
true / false |
Whether to enable our whitelist mode |
mydata.sync.file-sync.path |
local-with-file-sync |
path to a folder |
Where to look for policies and timers |
|
mydata.pmp.cloud.url |
local-with-cloud-sync, cloud |
concrete URL (without trailing slash), e.g.: https://management.mydata-control.de |
Endpoint of the Cloud-PMP |
|
mydata.pmp.cloud.client-id |
local-with-cloud-sync, cloud |
urn:client:* |
ClientId |
|
mydata.pmp.cloud.client-secret |
local-with-cloud-sync, cloud |
* |
Corresponding Client Secret |
|
mydata.sync.cloud-sync.cache.enabled |
local-with-cloud-sync |
false |
true / false |
Whether to enable cache |
mydata.sync.cloud-sync.cache.policy-file-path |
local-with-cloud-sync |
pcache.json |
Path for json-File |
Where to store the policy cache |
mydata.sync.cloud-sync.cache.timer-file-path |
local-with-cloud-sync |
tcache.json |
Path for json-File |
Where to store the timer cache |
mydata.sync.cloud-sync.schedule |
local-with-cloud-sync |
0 0/5 * * * ? |
https://www.freeformatter.com/cron-expression-generator-quartz.html |
Schedule/Interval for CloudSynchronizer |
mydata.sync.cloud-sync.master-client |
local-with-cloud-sync |
false |
true / false |
Whether this library-client is a master-library-client |
mydata.sync.cloud-sync.max-age |
local-with-cloud-sync |
Duration accoring to ISO8601, e.g.: PT30M |
How long policies and timers received from Cloud-PMP are valid. |
|
mydata.external-server-url |
cloud |
Determines how the application is accessible from the outside (used for URL generation when auto-configuring PIP/PXP) |
||
mydata.component.path |
cloud |
Used to determine the prefix of PIP/PXP-REST-Controllers |
||
mydata.datasource.* |
analogous to spring.datasource.* |
Configuration for MY DATA Control Technologies Library Persistence (used for the Event History feature) |
||
mydata.jpa.* |
analogous to spring.jpa.* |
Configuration for MY DATA Control Technologies Library Persistence (used for the Event History feature) |
Let’s assume that you want to use the Library in local mode and use the Spring Data JPA feature. Your configuration file may look like this:
application.yml
for local modespring:
datasource:
url: jdbc:h2:file:~/my-app;DB_CLOSE_ON_EXIT=FALSE;AUTO_RECONNECT=TRUE
username: sa
password:
platform: h2
driver-class-name: org.h2.Driver
jpa:
database-platform: org.hibernate.dialect.H2Dialect
show-sql: false
hibernate:
ddl-auto: update
hbm2ddl:
auto: update
mydata:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
show-sql: false
properties:
hibernate:
ddl-auto: update
hbm2ddl:
auto: update
datasource:
jdbc-url: jdbc:h2:file:~/mydata-for-my-app;DB_CLOSE_ON_EXIT=FALSE;AUTO_RECONNECT=TRUE
username: sa
password:
platform: h2
driver-class-name: org.h2.Driver
operational-mode: local # choose your operational mode
timezone: Europe/Berlin # determine the timezone
solution: urn:solution:my-solution # name your solution
pdp:
enable-whitelist-mode: false # enable whiteliste-mode?
num-threads: 4 # how many threads should the PDP use?
MY DATA Control Technologies Library uses a separate database that will not interfere with your application.
More examples:
application.yml
for local with file-sync modespring:
datasource:
url: jdbc:h2:file:~/my-app;DB_CLOSE_ON_EXIT=FALSE;AUTO_RECONNECT=TRUE
username: sa
password:
platform: h2
driver-class-name: org.h2.Driver
jpa:
database-platform: org.hibernate.dialect.H2Dialect
show-sql: false
hibernate:
ddl-auto: update
hbm2ddl:
auto: update
mydata:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
show-sql: false
properties:
hibernate:
ddl-auto: update
hbm2ddl:
auto: update
datasource:
jdbc-url: jdbc:h2:file:~/mydata-for-my-app;DB_CLOSE_ON_EXIT=FALSE;AUTO_RECONNECT=TRUE
username: sa
password:
platform: h2
driver-class-name: org.h2.Driver
operational-mode: local-with-file-sync
sync:
file-sync:
path: data # where are the policies and timers stored?
timezone: Europe/Berlin
solution: urn:solution:my-solution
pdp:
enable-whitelist-mode: false
num-threads: 4
application.yml
for local with cloud-sync modespring:
datasource:
url: jdbc:h2:file:~/my-app;DB_CLOSE_ON_EXIT=FALSE;AUTO_RECONNECT=TRUE
username: sa
password:
platform: h2
driver-class-name: org.h2.Driver
jpa:
database-platform: org.hibernate.dialect.H2Dialect
show-sql: false
hibernate:
ddl-auto: update
hbm2ddl:
auto: update
mydata:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
show-sql: false
properties:
hibernate:
ddl-auto: update
hbm2ddl:
auto: update
datasource:
jdbc-url: jdbc:h2:file:~/mydata-for-my-app;DB_CLOSE_ON_EXIT=FALSE;AUTO_RECONNECT=TRUE
username: sa
password:
platform: h2
driver-class-name: org.h2.Driver
operational-mode: local-with-cloud-sync
sync:
cloud-sync:
cache:
enabled: true # maintain a cache to consult when there is a communication problem with the Cloud-PMP
policy-file-path: pcache.json # where to cache the policies
timer-file-path: tcache.json # where to cache the timers
schedule: 0 0/5 * * * ? # cron expression as sync schedule, here: every 5 minutes
master-client: true # whether this instance is a master-library-client
max-age: PT30M # Duration, how long a received set of policies and timers is valid (ISO 8601 duration format)
timezone: Europe/Berlin
solution: urn:solution:my-solution
pmp:
cloud:
url: https://management.mydata-control.de # address of the Cloud-PMP
client-id: urn:client:my-solution:my-client # my client-id
client-secret: my-client-secret # my client-secret
pdp:
enable-whitelist-mode: false
num-threads: 4
application.yml
for cloud modespring:
datasource:
url: jdbc:h2:file:~/my-app;DB_CLOSE_ON_EXIT=FALSE;AUTO_RECONNECT=TRUE
username: sa
password:
platform: h2
driver-class-name: org.h2.Driver
jpa:
database-platform: org.hibernate.dialect.H2Dialect
show-sql: false
hibernate:
ddl-auto: update
hbm2ddl:
auto: update
mydata:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
show-sql: false
properties:
hibernate:
ddl-auto: update
hbm2ddl:
auto: update
datasource:
jdbc-url: jdbc:h2:file:~/mydata-for-my-app;DB_CLOSE_ON_EXIT=FALSE;AUTO_RECONNECT=TRUE
username: sa
password:
platform: h2
driver-class-name: org.h2.Driver
operational-mode: cloud
solution: urn:solution:my-solution
external-server-url: https://my-application.my-example.de # address under which this application instance can be reached
component:
path: mydata-api # path to use for the exposure of PIP/PXP components, result: https://my-application.my-example.de/mydata-api/...
pmp:
cloud:
url: https://management.mydata-control.de
client-id: urn:client:my-solution:my-client
client-secret: my-client-secret
Autoconfiguration and Initialization of the Library with Spring Boot
The initialization of the MY DATA Control Technologies Library is quite easy with Spring Boot as it happens automatically, based on the settings from the configuration file:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
That’s it.
You can access the initialized IMyDataEnvironment
instance using Spring’s dependency injection mechanism with
@Autowired
:
@Autowired
private IMyDataEnvironment myDataEnvironment;
Enable more features
To enable more features of the Library, just add the corresponding annotations next to @SpringBootApplication
.
Event History
To enable the Event History feature, add the @EnableEventHistory
annotation:
@SpringBootApplication
@EnableEventHistory
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Event History feature uses a database.
Make sure to add a dependency to org.springframework.boot:spring-boot-starter-data-jpa and configure the database connection.
Further, the use of Event History feature is only meaningful in combination with using one of our local modes and therefore requires the pdp dependency (see Dependencies).
|
Autoregistration and Instantiation of PEPs
Similarly to the autowirable IMyDataEnvironment
instance, your PEPs can be exposed as beans.
You just need to apply the @EnablePolicyEnforcementPoint
annotation and provide information where the PEPs should be looked for:
@SpringBootApplication
@EnablePolicyEnforcementPoint(basePackages = "com.acme.myapp.pep")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Inside those packages, the mechanism will look for Interfaces annotated with @PepServiceDescription
:
@PepServiceDescription(componentName = "my-pep")
@Modifiers // add all available modifiers
public interface MyPep {
@EventSpecification(action = "my-action")
Observable<Event> enforceUser(@EventParameter(name = "user") User user);
}
Autoregistration and Instantiation of PIPs
@SpringBootApplication
@EnablePolicyInformationPoint
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
It will look for Classes annotated with @PipService
:
@PipService(componentName = "authoritypip")
public class AuthorityPip {
@ActionDescription
public String getAuthority(
@ActionParameterDescription(name = "username", mandatory = true) String username
) {
// implement your logic here
return "guest"; // and return the information
}
}
Autoregistration and Instantiation of PXPs
@SpringBootApplication
@EnablePolicyExecutionPoint
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
It will look for Classes annotated with @PxpService
:
@PxpService(componentName = "mailpxp")
public class MailPxp {
public MailPxp() {
}
@ActionDescription
public boolean sendPlainMail(
@ActionParameterDescription(name = "recipient", mandatory = true) final String recipient,
@ActionParameterDescription(name = "subject", mandatory = true) final String subject,
@ActionParameterDescription(name = "message", mandatory = true) final String message
) {
// Implementation omitted
return true; // indicate success
}
}
Full-fledged Setup
@SpringBootApplication
@EnableEventHistory
@EnablePolicyEnforcementPoint(basePackages = "com.acme.myapp.pep")
@EnablePolicyInformationPoint
@EnablePolicyExecutionPoint
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Use the Library
Example
@SpringBootApplication
@EnablePolicyEnforcementPoint(basePackages = "com.acme.myapp.pep")
@EnablePolicyInformationPoint
@EnablePolicyExecutionPoint
public class MyApplication {
private static final Logger LOG = LoggerFactory.getLogger(MyApplication.class);
@Autowired
private IMyDataEnvironment myDataEnvironment;
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
// TODO this is a hard coded example, just for demonstration purpose, DO NOT USE THIS IN PRODUCTION
@PostConstruct
public void init() {
LOG.info("INIT:begin");
try {
// TODO this is a hardcoded policy for demonstration purpose
myDataEnvironment.getPmp().deployPolicy(myDataEnvironment.getPmp().addPolicy(new Policy(
"<policy id='urn:policy:my-solution:pxpdemo' xmlns='http://www.mydata-control.de/4.0/mydataLanguage' xmlns:tns='http://www.mydata-control.de/4.0/mydataLanguage' xmlns:parameter='http://www.mydata-control.de/4.0/parameter' xmlns:pip='http://www.mydata-control.de/4.0/pip' xmlns:function='http://www.mydata-control.de/4.0/function' xmlns:event='http://www.mydata-control.de/4.0/event' xmlns:constant='http://www.mydata-control.de/4.0/constant' xmlns:variable='http://www.mydata-control.de/4.0/variable' xmlns:variableDeclaration='http://www.mydata-control.de/4.0/variableDeclaration' xmlns:valueChanged='http://www.mydata-control.de/4.0/valueChanged' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:date='http://www.mydata-control.de/4.0/date' xmlns:time='http://www.mydata-control.de/4.0/time' xmlns:day='http://www.mydata-control.de/4.0/day'>\n" +
" <mechanism event='urn:action:my-solution:my-action'>\n" +
" <if>\n" +
" <constant:true/>\n" +
" <then>\n" +
" <allow/>\n" +
" <execute action='urn:action:my-solution:sendPlainMail'>\n" +
" <parameter:string name='recipient' value='recipient@example.com'/>\n" +
" <parameter:string name='subject' value='Very important incident'/>\n" +
" <parameter:string name='message' value='Something important happened.'/>\n" +
" </execute>\n" +
" </then>\n" +
" </if>\n" +
" </mechanism>\n" +
"</policy>")));
} catch (IOException | ResourceUpdateException | InvalidEntityException | ConflictingResourceException e) {
LOG.error(e.getMessage(), e);
}
LOG.info("INIT:end");
}
}
@Service
public class MyBusinessService {
private final static Logger LOG = LoggerFactory.getLogger(MyBusinessService.class);
private final UserRepository userRepository;
private final MyPep myPep;
@Autowired
public MyBusinessService(UserRepository userRepository, MyPep myPep) {
this.userRepository = userRepository;
this.myPep = myPep;
}
public User getUser(String userId) throws AccessPermissionDeniedException {
User userFromDb = userRepository.getById(userId);
return enforceUser(userFromDb);
}
private User enforceUser(User user) throws AccessPermissionDeniedException {
try {
final Event event = MyDataUtil.checkedBlockingGet(myPep.enforceUser(user));
return event.getParameterValue("user", User.class);
} catch (IOException e) {
throw new AccessPermissionDeniedException("There is a problem with the communication channel to the PDP. This will result in inhibition.", e);
} catch (InhibitException e) {
final String msg = e.getMessage();
throw new AccessPermissionDeniedException("PDP decided to inhibit the access" + (msg != null ? ": " + msg : "."), e);
} catch (EvaluationUndecidableException e) {
throw new AccessPermissionDeniedException("PDP reported that the evaluation is undecidable. This will result in inhibition.", e);
} catch (RuntimeException e) { // catch any other RuntimeExceptions
LOG.error("Exception occured", e);
throw new AccessPermissionDeniedException("Internal error, have a look into the log files. For now: This will result in inhibition.");
}
}
}