-
Notifications
You must be signed in to change notification settings - Fork 306
Expand file tree
/
Copy patherror-pages.js
More file actions
212 lines (194 loc) · 5.3 KB
/
Copy patherror-pages.js
File metadata and controls
212 lines (194 loc) · 5.3 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
const debug = require('../debug').server
const fs = require('fs')
const util = require('../utils')
const Auth = require('../api/authn')
/**
* Serves as a last-stop error handler for all other middleware.
*
* @param err {Error}
* @param req {IncomingRequest}
* @param res {ServerResponse}
* @param next {Function}
*/
function handler (err, req, res, next) {
debug('Error page because of:', err)
const locals = req.app.locals
const authMethod = locals.authMethod
const ldp = locals.ldp
// If the user specifies this function,
// they can customize the error programmatically
if (ldp.errorHandler) {
debug('Using custom error handler')
return ldp.errorHandler(err, req, res, next)
}
const statusCode = statusCodeFor(err, req, authMethod)
switch (statusCode) {
case 401:
setAuthenticateHeader(req, res, err)
renderLoginRequired(req, res, err)
break
case 403:
renderNoPermission(req, res, err)
break
default:
if (ldp.noErrorPages) {
sendErrorResponse(statusCode, res, err)
} else {
sendErrorPage(statusCode, res, err, ldp)
}
}
}
/**
* Returns the HTTP status code for a given request error.
*
* @param err {Error}
* @param req {IncomingRequest}
* @param authMethod {string}
*
* @returns {number}
*/
function statusCodeFor (err, req, authMethod) {
let statusCode = err.status || err.statusCode || 500
if (authMethod === 'oidc') {
statusCode = Auth.oidc.statusCodeOverride(statusCode, req)
}
return statusCode
}
/**
* Dispatches the writing of the `WWW-Authenticate` response header (used for
* 401 Unauthorized responses).
*
* @param req {IncomingRequest}
* @param res {ServerResponse}
* @param err {Error}
*/
function setAuthenticateHeader (req, res, err) {
const locals = req.app.locals
const authMethod = locals.authMethod
switch (authMethod) {
case 'oidc':
Auth.oidc.setAuthenticateHeader(req, res, err)
break
case 'tls':
Auth.tls.setAuthenticateHeader(req, res)
break
default:
break
}
}
/**
* Sends the HTTP status code and error message in the response.
*
* @param statusCode {number}
* @param res {ServerResponse}
* @param err {Error}
*/
function sendErrorResponse (statusCode, res, err) {
res.status(statusCode)
res.header('Content-Type', 'text/plain;charset=utf-8')
res.send(err.message + '\n')
}
/**
* Sends the HTTP status code and error message as a custom error page.
*
* @param statusCode {number}
* @param res {ServerResponse}
* @param err {Error}
* @param ldp {LDP}
*/
function sendErrorPage (statusCode, res, err, ldp) {
const errorPage = ldp.errorPages + statusCode.toString() + '.html'
return new Promise((resolve) => {
fs.readFile(errorPage, 'utf8', (readErr, text) => {
if (readErr) {
// Fall back on plain error response
return resolve(sendErrorResponse(statusCode, res, err))
}
res.status(statusCode)
res.header('Content-Type', 'text/html')
res.send(text)
resolve()
})
})
}
/**
* Renders the databrowser
*
* @param req {IncomingRequest}
* @param res {ServerResponse}
*/
function renderDataBrowser (req, res) {
res.set('Content-Type', 'text/html')
const ldp = req.app.locals.ldp
const defaultDataBrowser = require.resolve('mashlib/dist/databrowser.html')
const dataBrowserPath = ldp.dataBrowserPath === 'default' ? defaultDataBrowser : ldp.dataBrowserPath
debug(' sending data browser file: ' + dataBrowserPath)
const dataBrowserHtml = fs.readFileSync(dataBrowserPath, 'utf8')
// Note: This must be done instead of sendFile because the test suite doesn't accept 412 responses
res.set('content-type', 'text/html')
res.send(dataBrowserHtml)
}
/**
* Renders a 401 response explaining that a login is required.
*
* @param req {IncomingRequest}
* @param res {ServerResponse}
*/
function renderLoginRequired (req, res, err) {
const currentUrl = util.fullUrlForReq(req)
debug(`Display login-required for ${currentUrl}`)
res.statusMessage = err.message
res.status(401)
if (req.accepts('html')) {
renderDataBrowser(req, res)
} else {
res.send('Not Authenticated')
}
}
/**
* Renders a 403 response explaining that the user has no permission.
*
* @param req {IncomingRequest}
* @param res {ServerResponse}
*/
function renderNoPermission (req, res, err) {
const currentUrl = util.fullUrlForReq(req)
debug(`Display no-permission for ${currentUrl}`)
res.statusMessage = err.message
res.status(403)
if (req.accepts('html')) {
renderDataBrowser(req, res)
} else {
res.send('Not Authorized')
}
}
/**
* Returns a response body for redirecting browsers to a Select Provider /
* login workflow page. Uses either a JS location.href redirect or an
* http-equiv type html redirect for no-script conditions.
*
* @param url {string}
*
* @returns {string} Response body
*/
function redirectBody (url) {
return `<!DOCTYPE HTML>
<meta charset="UTF-8">
<script>
window.location.href = "${url}" + encodeURIComponent(window.location.hash)
</script>
<noscript>
<meta http-equiv="refresh" content="0; url=${url}">
</noscript>
<title>Redirecting...</title>
If you are not redirected automatically,
follow the <a href='${url}'>link to login</a>
`
}
module.exports = {
handler,
redirectBody,
sendErrorPage,
sendErrorResponse,
setAuthenticateHeader
}