-
Notifications
You must be signed in to change notification settings - Fork 306
Expand file tree
/
Copy pathheader.js
More file actions
143 lines (132 loc) · 5.27 KB
/
header.js
File metadata and controls
143 lines (132 loc) · 5.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
module.exports.addLink = addLink
module.exports.addLinks = addLinks
module.exports.parseMetadataFromHeader = parseMetadataFromHeader
module.exports.linksHandler = linksHandler
module.exports.addPermissions = addPermissions
const li = require('li')
const path = require('path')
const metadata = require('./metadata.js')
const debug = require('./debug.js')
const utils = require('./utils.js')
const error = require('./http-error')
const MODES = ['Read', 'Write', 'Append', 'Control']
const PERMISSIONS = MODES.map(m => m.toLowerCase())
function addLink (res, value, rel) {
const oldLink = res.get('Link')
if (oldLink === undefined) {
res.set('Link', '<' + value + '>; rel="' + rel + '"')
} else {
res.set('Link', oldLink + ', ' + '<' + value + '>; rel="' + rel + '"')
}
}
function addLinks (res, fileMetadata) {
if (fileMetadata.isResource) {
addLink(res, 'http://www.w3.org/ns/ldp#Resource', 'type')
}
if (fileMetadata.isSourceResource) {
addLink(res, 'http://www.w3.org/ns/ldp#RDFSource', 'type')
}
if (fileMetadata.isContainer) {
addLink(res, 'http://www.w3.org/ns/ldp#Container', 'type')
}
if (fileMetadata.isBasicContainer) {
addLink(res, 'http://www.w3.org/ns/ldp#BasicContainer', 'type')
}
if (fileMetadata.isDirectContainer) {
addLink(res, 'http://www.w3.org/ns/ldp#DirectContainer', 'type')
}
if (fileMetadata.isStorage) {
addLink(res, 'http://www.w3.org/ns/pim/space#Storage', 'type')
}
}
async function linksHandler (req, res, next) {
const ldp = req.app.locals.ldp
let filename
try {
// Hack: createIfNotExists is set to true for PUT or PATCH requests
// because the file might not exist yet at this point.
// But it will be created afterwards.
// This should be improved with the new server architecture.
({ path: filename } = await ldp.resourceMapper
.mapUrlToFile({ url: req, createIfNotExists: req.method === 'PUT' || req.method === 'PATCH' }))
} catch (e) {
// Silently ignore errors here
// Later handlers will error as well, but they will be able to given a more concrete error message (like 400 or 404)
return next()
}
if (path.extname(filename) === ldp.suffixMeta) {
debug.metadata('Trying to access metadata file as regular file.')
return next(error(404, 'Trying to access metadata file as regular file'))
}
const fileMetadata = new metadata.Metadata()
if (req.path.endsWith('/')) {
// do not add storage header in serverUri
if (req.path === '/') fileMetadata.isStorage = true
fileMetadata.isContainer = true
fileMetadata.isBasicContainer = true
} else {
fileMetadata.isResource = true
}
// Add LDP-required Accept-Post header for OPTIONS request to containers
if (fileMetadata.isContainer && req.method === 'OPTIONS') {
res.header('Accept-Post', '*/*')
}
// Add ACL and Meta Link in header
addLink(res, utils.pathBasename(req.path) + ldp.suffixAcl, 'acl')
addLink(res, utils.pathBasename(req.path) + ldp.suffixMeta, 'describedBy')
// Add other Link headers
addLinks(res, fileMetadata)
next()
}
function parseMetadataFromHeader (linkHeader) {
const fileMetadata = new metadata.Metadata()
if (linkHeader === undefined) {
return fileMetadata
}
const links = linkHeader.split(',')
for (const linkIndex in links) {
const link = links[linkIndex]
const parsedLinks = li.parse(link)
for (const rel in parsedLinks) {
if (rel === 'type') {
if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#Resource') {
fileMetadata.isResource = true
} else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#RDFSource') {
fileMetadata.isSourceResource = true
} else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#Container') {
fileMetadata.isContainer = true
} else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#BasicContainer') {
fileMetadata.isBasicContainer = true
} else if (parsedLinks[rel] === 'http://www.w3.org/ns/ldp#DirectContainer') {
fileMetadata.isDirectContainer = true
} else if (parsedLinks[rel] === 'http://www.w3.org/ns/pim/space#Storage') {
fileMetadata.isStorage = true
}
}
}
}
return fileMetadata
}
// Adds a header that describes the user's permissions
async function addPermissions (req, res, next) {
const { acl, session } = req
if (!acl) return next()
// Turn permissions for the public and the user into a header
const ldp = req.app.locals.ldp
const resource = ldp.resourceMapper.resolveUrl(req.hostname, req.path)
let [publicPerms, userPerms] = await Promise.all([
getPermissionsFor(acl, null, req),
getPermissionsFor(acl, session.userId, req)
])
if (resource.endsWith('.acl') && userPerms === '' && await ldp.isOwner(session.userId, req.hostname)) userPerms = 'control'
debug.ACL(`Permissions on ${resource} for ${session.userId || '(none)'}: ${userPerms}`)
debug.ACL(`Permissions on ${resource} for public: ${publicPerms}`)
res.set('WAC-Allow', `user="${userPerms}",public="${publicPerms}"`)
next()
}
// Gets the permissions string for the given user and resource
async function getPermissionsFor (acl, user, req) {
const accesses = MODES.map(mode => acl.can(user, mode))
const allowed = await Promise.all(accesses)
return PERMISSIONS.filter((mode, i) => allowed[i]).join(' ')
}