@@ -21,28 +21,249 @@ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
2121
2222function match ( graph , s , p , o ) {
2323 var matches = graph . statementsMatching ( s ? $rdf . sym ( s ) : undefined , $rdf . sym ( p ) , $rdf . sym ( o ) ) ;
24- console . log ( matches )
25- return matches . map ( function ( triple ) {
26- triple . subject . toString = function ( ) {
27- return triple . subject . uri
24+ return matches
25+ }
26+
27+ function ACL ( opts ) {
28+ var self = this
29+ opts = opts || { }
30+ if ( opts . store && opts . store . graph && ! opts . fetch ) {
31+ self . fetch = opts . store . graph . bind ( opts . store )
32+ }
33+ self . fetch = self . fetch || opts . fetch
34+ self . match = opts . match || match
35+ self . suffix = opts . suffix || '.acl'
36+ }
37+
38+ ACL . prototype . isAcl = function ( resource ) {
39+ return ! ! S ( resource ) . endsWith ( this . suffix )
40+ }
41+
42+ ACL . prototype . can = function ( user , mode , resource , callback , options ) {
43+ debug ( 'Can ' + user + ' ' + mode + ' ' + resource + '?' )
44+ var self = this
45+ var accessType = 'accessTo'
46+ var acls = possibleACLs ( resource , self . suffix )
47+ options = options || { }
48+
49+ // If it is an ACL, only look for control this resource
50+ if ( self . isAcl ( resource ) ) {
51+ mode = 'Control'
52+ }
53+
54+ async . eachSeries (
55+ acls ,
56+ // Looks for ACL, if found, looks for a rule
57+ function ( acl , next ) {
58+ debug ( 'Check if acl exist: ' + acl )
59+
60+ // Let's see if there is a file..
61+ self . fetch ( acl , function ( err , graph ) {
62+ if ( err || ! graph || graph . statements . length === 0 ) {
63+ // TODO
64+ // If no file is found and we want to Control,
65+ // we should not be able to do that!
66+ // Control is only to Read and Write the current file!
67+ // if (mode === 'Control') {
68+ // return next(new Error("You can't Control an unexisting file"))
69+ // }
70+ if ( err ) debug ( 'Error: ' + err )
71+ accessType = 'defaultForNew'
72+ return next ( )
73+ }
74+ self . findRule (
75+ graph , // The ACL graph
76+ user , // The webId of the user
77+ mode , // Read/Write/Append
78+ resource , // The resource we want to access
79+ accessType , // accessTo or defaultForNew
80+ acl , // The current Acl file!
81+ function ( err ) {
82+ return next ( ! err || err )
83+ } , options )
84+ } )
85+ } ,
86+ function ( err ) {
87+ if ( err === false || err === null ) {
88+ debug ( 'No ACL resource found - access allowed' )
89+ err = new Error ( 'No Access Control Policy found' )
90+ }
91+
92+ if ( err === true ) {
93+ debug ( 'ACL policy found' )
94+ err = null
95+ }
96+
97+ if ( err ) {
98+ debug ( 'Error: ' + err . message )
99+ if ( ! user || user . length === 0 ) {
100+ debug ( 'Authentication required' )
101+ err . status = 401
102+ err . message = 'Access to ' + resource + ' requires authorization'
103+ } else {
104+ debug ( mode + ' access denied for: ' + user )
105+ err . status = 403
106+ err . message = 'Access denied for ' + user
107+ }
108+ }
109+
110+ return callback ( err )
111+ } )
112+ }
113+
114+ ACL . prototype . findAgentClass = function ( graph , user , mode , resource , acl , callback ) {
115+ var self = this
116+
117+ // Agent class statement
118+ var agentClassStatements = self . match (
119+ graph ,
120+ acl ,
121+ 'http://www.w3.org/ns/auth/acl#agentClass' ,
122+ undefined )
123+
124+ if ( agentClassStatements . length === 0 ) {
125+ return callback ( false )
126+ }
127+
128+ async . some ( agentClassStatements , function ( agentClassTriple , found ) {
129+ // Check for FOAF groups
130+ debug ( 'Found agentClass policy' )
131+ if ( agentClassTriple . object . uri === 'http://xmlns.com/foaf/0.1/Agent' ) {
132+ debug ( mode + ' allowed access as FOAF agent' )
133+ return found ( true )
134+ }
135+
136+ return found ( false )
137+ } , callback )
138+ }
139+
140+ ACL . prototype . findRule = function ( graph , user , mode , resource , accessType , acl , callback , options ) {
141+ var self = this
142+
143+ // TODO check if this is necessary
144+ if ( graph . statements . length === 0 ) {
145+ debug ( 'ACL ' + acl + ' is empty' )
146+ return callback ( new Error ( 'No policy found' ) )
147+ }
148+
149+ debug ( 'Found policies in ' + acl )
150+
151+ // Check for mode
152+ var statements = self . getMode ( graph , mode )
153+ if ( mode === 'Append' ) {
154+ statements = statements
155+ . concat ( self . getMode ( graph , 'Write' ) )
156+ }
157+
158+ async . some (
159+ statements ,
160+ function ( statement , done ) {
161+ var statementSubject = statement . subject . uri
162+
163+ // Check for origin
164+ var matchOrigin = self . matchOrigin ( graph , statementSubject , options . origin )
165+ if ( ! matchOrigin ) {
166+ debug ( 'The request does not match the origin' )
167+ return done ( false )
168+ }
169+
170+ // Check for accessTo/defaultForNew
171+ if ( ! self . isAcl ( resource ) || accessType === 'defaultForNew' ) {
172+ debug ( 'Checking for accessType:' + accessType )
173+ var accesses = self . matchAccessType ( graph , statementSubject , accessType , resource )
174+ if ( accesses ) {
175+ debug ( 'Cannot find accessType ' + accessType )
176+ return done ( false )
28177 }
29- return triple
178+ }
179+
180+ // Check for Agent
181+ var agentStatements = self . match (
182+ graph ,
183+ statementSubject ,
184+ 'http://www.w3.org/ns/auth/acl#agent' ,
185+ user )
186+
187+ if ( agentStatements . length ) {
188+ debug ( mode + ' access allowed (as agent) for: ' + user )
189+ return done ( true )
190+ }
191+
192+ debug ( 'Inspect agentClass' )
193+ // Check for AgentClass
194+ return self . findAgentClass ( graph , user , mode , resource , statementSubject , done )
195+ } ,
196+ function ( found ) {
197+ if ( ! found ) {
198+ return callback ( new Error ( 'Acl found but policy not found' ) )
199+ }
200+ return callback ( null )
201+ } )
202+ }
203+
204+ // TODO maybe these functions can be integrated in the code
205+ ACL . prototype . getMode = function getMode ( graph , mode ) {
206+ var self = this
207+ return self . match (
208+ graph ,
209+ undefined ,
210+ 'http://www.w3.org/ns/auth/acl#mode' ,
211+ 'http://www.w3.org/ns/auth/acl#' + mode )
212+ }
213+
214+ ACL . prototype . matchAccessType = function matchAccessType ( graph , rule , accessType , uri ) {
215+ var self = this
216+ var matches = self . match (
217+ graph ,
218+ rule ,
219+ 'http://www.w3.org/ns/auth/acl#' + accessType ,
220+ uri )
221+
222+ return matches . some ( function ( match ) {
223+ return S ( uri ) . beginsWith ( match )
224+ } )
225+
226+ }
227+
228+ ACL . prototype . matchOrigin = function getOrigins ( graph , rule , origin ) {
229+ var self = this
230+ var origins = self . match (
231+ graph ,
232+ rule ,
233+ 'http://www.w3.org/ns/auth/acl#origin' ,
234+ undefined )
235+
236+ if ( origins . length ) {
237+ return origins . some ( function ( triple ) {
238+ return triple . object . uri === origin
30239 } )
240+ }
241+
242+ return true
243+ }
244+
245+ function possibleACLs ( uri , suffix ) {
246+ var first = S ( uri ) . endsWith ( suffix ) ? uri : uri + '.acl'
247+ var urls = [ first ]
248+ var parsedUri = url . parse ( uri )
249+ var baseUrl = ( parsedUri . protocol ? parsedUri . protocol + '//' : '' ) + ( parsedUri . host || '' )
250+ if ( baseUrl + '/' === uri ) {
251+ return urls
252+ }
253+
254+ var times = parsedUri . pathname . split ( '/' ) . length
255+ for ( var i = 0 ; i < times - 1 ; i ++ ) {
256+ uri = path . dirname ( uri )
257+ urls . push ( uri + ( uri [ uri . length - 1 ] === '/' ? '.acl' : '/.acl' ) )
258+ }
259+ return urls
31260}
32261
33262function fetchDocument ( ldp , baseUri ) {
34263 return function ( uri , callback ) {
35264 var graph = $rdf . graph ( ) ;
36265 async . waterfall ( [
37266 function ( cb ) {
38- // URL is remote
39- if ( ! S ( uri ) . startsWith ( baseUri ) ) {
40- // Fetch remote source
41- var headers = { headers : { 'Accept' : 'text/turtle' } } ;
42- return request . get ( uri , headers , function ( err , response , body ) {
43- return cb ( err , body ) ;
44- } ) ;
45- }
46267 // URL is local
47268 var newPath = S ( uri ) . chompLeft ( baseUri ) . s ;
48269 // TODO prettify this
@@ -58,7 +279,6 @@ function fetchDocument (ldp, baseUri) {
58279 console . log ( err )
59280 return cb ( err , graph ) ;
60281 }
61- graph . length = graph . statements . length
62282 return cb ( null , graph ) ;
63283 }
64284 ] , callback ) ;
0 commit comments