1- import { DC , RDF } from '@solid/community-server' ;
1+ import { BadRequestHttpError , DC , RDF } from '@solid/community-server' ;
22import { getLoggerFor } from 'global-logger-factory' ;
3- import { DataFactory , Literal , NamedNode , Quad , Quad_Subject , Store , Writer } from 'n3' ;
4- import { EyeReasoner , ODRLEngineMultipleSteps , ODRLEvaluator } from 'odrl-evaluator'
5- import { createVocabulary } from 'rdf-vocabulary' ;
3+ import { DataFactory , Quad , Store , Writer } from 'n3' ;
4+ import { EyeReasoner , ODRLEngineMultipleSteps , ODRLEvaluator } from 'odrl-evaluator' ;
65import { CLIENTID , WEBID } from '../../credentials/Claims' ;
76import { ClaimSet } from '../../credentials/ClaimSet' ;
87import { basicPolicy } from '../../ucp/policy/ODRL' ;
8+ import { PrioritizeProhibitionStrategy } from '../../ucp/policy/PrioritizeProhibitionStrategy' ;
9+ import { Strategy } from '../../ucp/policy/Strategy' ;
910import { UCPPolicy } from '../../ucp/policy/UsageControlPolicy' ;
1011import { UCRulesStorage } from '../../ucp/storage/UCRulesStorage' ;
1112import { ODRL } from '../../ucp/util/Vocabularies' ;
@@ -33,6 +34,7 @@ const { quad, namedNode, literal, blankNode } = DataFactory
3334export class OdrlAuthorizer implements Authorizer {
3435 protected readonly logger = getLoggerFor ( this ) ;
3536 private readonly odrlEvaluator : ODRLEvaluator ;
37+ private readonly strategy : Strategy ;
3638
3739 /**
3840 * Creates a OdrlAuthorizer enforcing policies using ODRL with the ODRL Evaluator.
@@ -46,13 +48,14 @@ export class OdrlAuthorizer implements Authorizer {
4648 eyePath ?: string ,
4749 ) {
4850 const engine = eyePath ?
49- new ODRLEngineMultipleSteps ( { reasoner : new EyeReasoner ( eyePath , [ "--quiet" , "--nope" , "--pass-only-new" ] ) } ) :
50- new ODRLEngineMultipleSteps ( ) ;
51+ new ODRLEngineMultipleSteps ( { reasoner : new EyeReasoner ( eyePath , [ "--quiet" , "--nope" , "--pass-only-new" ] ) } ) :
52+ new ODRLEngineMultipleSteps ( ) ;
5153 this . odrlEvaluator = new ODRLEvaluator ( engine ) ;
54+ this . strategy = new PrioritizeProhibitionStrategy ( ) ;
5255 }
5356
5457 public async permissions ( claims : ClaimSet , query ?: Permission [ ] ) : Promise < Permission [ ] > {
55- this . logger . info ( `Calculating permissions. ${ JSON . stringify ( { claims, query} ) } ` ) ;
58+ this . logger . info ( `Calculating permissions. ${ JSON . stringify ( { claims, query } ) } ` ) ;
5659 if ( ! query ) {
5760 this . logger . warn ( 'The OdrlAuthorizer can only calculate permissions for explicit queries.' )
5861 return [ ] ;
@@ -67,9 +70,9 @@ export class OdrlAuthorizer implements Authorizer {
6770 // prepare sotw
6871 const sotw = new Store ( ) ;
6972 sotw . add ( quad (
70- namedNode ( 'http://example.com/request/currentTime' ) ,
71- namedNode ( 'http://purl.org/dc/terms/issued' ) ,
72- literal ( new Date ( ) . toISOString ( ) , namedNode ( "http://www.w3.org/2001/XMLSchema#dateTime" ) ) ) ,
73+ namedNode ( 'http://example.com/request/currentTime' ) ,
74+ namedNode ( 'http://purl.org/dc/terms/issued' ) ,
75+ literal ( new Date ( ) . toISOString ( ) , namedNode ( "http://www.w3.org/2001/XMLSchema#dateTime" ) ) ) ,
7376 ) ;
7477
7578 const subject = typeof claims [ WEBID ] === 'string' ? claims [ WEBID ] : 'urn:solidlab:uma:id:anonymous' ;
@@ -91,7 +94,7 @@ export class OdrlAuthorizer implements Authorizer {
9194 // });
9295 }
9396
94- for ( const { resource_id, resource_scopes} of query ) {
97+ for ( const { resource_id, resource_scopes } of query ) {
9598 grantedPermissions [ resource_id ] = [ ] ;
9699 for ( const scope of resource_scopes ) {
97100 // TODO: why is this transformation happening (here)?
@@ -114,9 +117,9 @@ export class OdrlAuthorizer implements Authorizer {
114117 // Adding context triples for the client identifier, if there is one
115118 if ( clientQuads . length > 0 ) {
116119 requestStore . addQuad ( quad (
117- namedNode ( request . ruleIRIs [ 0 ] ) ,
118- namedNode ( 'https://w3id.org/force/sotw#context' ) ,
119- clientSubject ,
120+ namedNode ( request . ruleIRIs [ 0 ] ) ,
121+ namedNode ( 'https://w3id.org/force/sotw#context' ) ,
122+ clientSubject ,
120123 ) ) ;
121124 requestStore . addQuads ( clientQuads ) ;
122125 }
@@ -126,19 +129,19 @@ export class OdrlAuthorizer implements Authorizer {
126129 [ ...policyStore ] ,
127130 [ ...requestStore ] ,
128131 [ ...sotw ] ) ;
129- const reportStore = new Store ( reports ) ;
130132
131- // TODO: handle multiple reports -> possible to be generated
132- // NOTE: current strategy, add all actions of active reports generated by the request
133- // fetch active and attempted
134- const PolicyReportNodes = reportStore . getSubjects ( RDF . type , CR . PolicyReport , null ) ;
135- for ( const policyReportNode of PolicyReportNodes ) {
136- const policyReport = parseComplianceReport ( policyReportNode , reportStore )
137- const activeReports = policyReport . ruleReport . filter (
138- ( report ) => report . activationState === ActivationState . Active ) ;
139- if ( activeReports . length > 0 && activeReports [ 0 ] . type === RuleReportType . PermissionReport ) {
140- grantedPermissions [ resource_id ] . push ( scope ) ;
141- }
133+ // handle potential conflicts with a strategy
134+ const allowed = await this . strategy . handleSafe ( {
135+ request : {
136+ request : [ ...requestStore ] ,
137+ identifier : namedNode ( request . policyIRI )
138+ } ,
139+ policies : [ ...policyStore ] ,
140+ reports : reports
141+ } )
142+
143+ if ( allowed ) {
144+ grantedPermissions [ resource_id ] . push ( scope ) ;
142145 }
143146 }
144147 }
@@ -147,156 +150,13 @@ export class OdrlAuthorizer implements Authorizer {
147150 resource_id => permissions . push ( {
148151 resource_id,
149152 resource_scopes : grantedPermissions [ resource_id ] ,
150- } ) ) ;
153+ } ) ) ;
151154 return permissions ;
152155 }
153156}
154157const scopeCssToOdrl : Map < string , string > = new Map ( ) ;
155- scopeCssToOdrl . set ( 'urn:example:css:modes:read' , 'http://www.w3.org/ns/odrl/2/read' ) ;
156- scopeCssToOdrl . set ( 'urn:example:css:modes:append' , 'http://www.w3.org/ns/odrl/2/append' ) ;
157- scopeCssToOdrl . set ( 'urn:example:css:modes:create' , 'http://www.w3.org/ns/odrl/2/create' ) ;
158- scopeCssToOdrl . set ( 'urn:example:css:modes:delete' , 'http://www.w3.org/ns/odrl/2/delete' ) ;
159- scopeCssToOdrl . set ( 'urn:example:css:modes:write' , 'http://www.w3.org/ns/odrl/2/write' ) ;
160-
161- const scopeOdrlToCss : Map < string , string > = new Map ( Array . from ( scopeCssToOdrl , entry => [ entry [ 1 ] , entry [ 0 ] ] ) ) ;
162-
163- type PolicyReport = {
164- id : NamedNode ;
165- created : Literal ;
166- request : NamedNode ;
167- policy : NamedNode ;
168- ruleReport : RuleReport [ ] ;
169- }
170- type RuleReport = {
171- id : NamedNode ;
172- type : RuleReportType ;
173- activationState : ActivationState
174- rule : NamedNode ;
175- requestedRule : NamedNode ;
176- premiseReport : PremiseReport [ ]
177- }
178-
179- type PremiseReport = {
180- id : NamedNode ;
181- type :PremiseReportType ;
182- premiseReport : PremiseReport [ ] ;
183- satisfactionState : SatisfactionState
184- }
185-
186- // is it possible to just use CR.namespace + "term"?
187- // https://github.com/microsoft/TypeScript/issues/40793
188- enum RuleReportType {
189- PermissionReport = 'https://w3id.org/force/compliance-report#PermissionReport' ,
190- ProhibitionReport = 'https://w3id.org/force/compliance-report#ProhibitionReport' ,
191- ObligationReport = 'https://w3id.org/force/compliance-report#ObligationReport' ,
192- }
193- enum SatisfactionState {
194- Satisfied = 'https://w3id.org/force/compliance-report#Satisfied' ,
195- Unsatisfied = 'https://w3id.org/force/compliance-report#Unsatisfied' ,
196- }
197-
198- enum PremiseReportType {
199- ConstraintReport = 'https://w3id.org/force/compliance-report#ConstraintReport' ,
200- PartyReport = 'https://w3id.org/force/compliance-report#PartyReport' ,
201- TargetReport = 'https://w3id.org/force/compliance-report#TargetReport' ,
202- ActionReport = 'https://w3id.org/force/compliance-report#ActionReport' ,
203- }
204-
205- enum ActivationState {
206- Active = 'https://w3id.org/force/compliance-report#Active' ,
207- Inactive = 'https://w3id.org/force/compliance-report#Inactive' ,
208- }
209-
210- /**
211- * Parses an ODRL Compliance Report Model into a {@link PolicyReport}.
212- * @param identifier
213- * @param store
214- */
215- function parseComplianceReport ( identifier : Quad_Subject , store : Store ) : PolicyReport {
216- const exists = store . getQuads ( identifier , RDF . type , CR . PolicyReport , null ) . length === 1 ;
217- if ( ! exists ) { throw Error ( `No Policy Report found with: ${ identifier } .` ) ; }
218- const ruleReportNodes = store . getObjects ( identifier , CR . ruleReport , null ) as NamedNode [ ] ;
219-
220- return {
221- id : identifier as NamedNode ,
222- created : store . getObjects ( identifier , DC . namespace + "created" , null ) [ 0 ] as Literal ,
223- policy : store . getObjects ( identifier , CR . policy , null ) [ 0 ] as NamedNode ,
224- request : store . getObjects ( identifier , CR . policyRequest , null ) [ 0 ] as NamedNode ,
225- ruleReport : ruleReportNodes . map ( ruleReportNode => parseRuleReport ( ruleReportNode , store ) )
226- }
227- }
228-
229- /**
230- * Parses Rule Reports from a Compliance Report, including its premises
231- * @param identifier
232- * @param store
233- */
234- function parseRuleReport ( identifier : Quad_Subject , store : Store ) : RuleReport {
235- const premiseNodes = store . getObjects ( identifier , CR . premiseReport , null ) as NamedNode [ ] ;
236- return {
237- id : identifier as NamedNode ,
238- type : store . getObjects ( identifier , RDF . type , null ) [ 0 ] . value as RuleReportType ,
239- activationState : store . getObjects ( identifier , CR . activationState , null ) [ 0 ] . value as ActivationState ,
240- requestedRule : store . getObjects ( identifier , CR . ruleRequest , null ) [ 0 ] as NamedNode ,
241- rule : store . getObjects ( identifier , CR . rule , null ) [ 0 ] as NamedNode ,
242- premiseReport : premiseNodes . map ( ( prem ) => parsePremiseReport ( prem , store ) )
243- }
244- }
245-
246- /**
247- * Parses Premise Reports, including premises of a Premise Report itself.
248- * Note that if for some reason there are circular premise reports, this will result into an infinite loop
249- * @param identifier
250- * @param store
251- */
252- function parsePremiseReport ( identifier : Quad_Subject , store : Store ) : PremiseReport {
253- const nestedPremises = store . getObjects ( identifier , CR . PremiseReport , null ) as NamedNode [ ] ;
254- return {
255- id : identifier as NamedNode ,
256- type : store . getObjects ( identifier , RDF . type , null ) [ 0 ] . value as PremiseReportType ,
257- premiseReport : nestedPremises . map ( ( prem ) => parsePremiseReport ( prem , store ) ) ,
258- satisfactionState : store . getObjects ( identifier , CR . satisfactionState , null ) [ 0 ] . value as SatisfactionState
259- }
260- }
261- const CR = createVocabulary ( 'https://w3id.org/force/compliance-report#' ,
262- 'PolicyReport' ,
263- 'RuleReport' ,
264- 'PermissionReport' ,
265- 'ProhibitionReport' ,
266- 'DutyReport' ,
267- 'PremiseReport' ,
268- 'ConstraintReport' ,
269- 'PartyReport' ,
270- 'ActionReport' ,
271- 'TargetReport' ,
272- 'ActivationState' ,
273- 'Active' ,
274- 'Inactive' ,
275- 'AttemptState' ,
276- 'Attempted' ,
277- 'NotAttempted' ,
278- 'PerformanceState' ,
279- 'Performed' ,
280- 'Unperformed' ,
281- 'Unknown' ,
282- 'DeonticState' ,
283- 'NonSet' ,
284- 'Violated' ,
285- 'Fulfilled' ,
286- 'SatisfactionState' ,
287- 'Satisfied' ,
288- 'Unsatisfied' ,
289- 'policy' ,
290- 'policyRequest' ,
291- 'ruleReport' ,
292- 'conditionReport' ,
293- 'premiseReport' ,
294- 'rule' ,
295- 'ruleRequest' ,
296- 'activationState' ,
297- 'attemptState' ,
298- 'performanceState' ,
299- 'deonticState' ,
300- 'constraint' ,
301- 'satisfactionState' ,
302- )
158+ scopeCssToOdrl . set ( 'urn:example:css:modes:read' , 'http://www.w3.org/ns/odrl/2/read' ) ;
159+ scopeCssToOdrl . set ( 'urn:example:css:modes:append' , 'http://www.w3.org/ns/odrl/2/append' ) ;
160+ scopeCssToOdrl . set ( 'urn:example:css:modes:create' , 'http://www.w3.org/ns/odrl/2/create' ) ;
161+ scopeCssToOdrl . set ( 'urn:example:css:modes:delete' , 'http://www.w3.org/ns/odrl/2/delete' ) ;
162+ scopeCssToOdrl . set ( 'urn:example:css:modes:write' , 'http://www.w3.org/ns/odrl/2/write' ) ;
0 commit comments