1
0
Fork 0

Add SyntheticRequest#auth (#8750)

* Add SyntheticRequest#auth

* Fix tests
This commit is contained in:
Alan Plum 2019-04-12 21:18:35 +02:00 committed by Michael Hackstein
parent 3007c928aa
commit b76f8adc0f
5 changed files with 117 additions and 3 deletions

View File

@ -1,6 +1,8 @@
devel
-----
* added req.auth property to Foxx
* added collection.documentId method to derive document id from key
* fixed internal issue #536: ArangoSearch may crash server during term lookup

View File

@ -19,6 +19,18 @@ The request object specifies the following properties:
of the ArangoDB server (e.g. `30102` for version 3.1.2) if no valid header
was provided.
* **auth**: `object | null`
The credentials supplied in the `authorization` header if any.
If the request uses basic authentication, the value is an object like
`{basic: {username: string}}` or
`{basic: {username: string, password: string}}` or
`{basic: {}}` (if the credentials were malformed or empty).
If the request uses bearer authentication, the value is an object like
`{bearer: string}`.
* **baseUrl**: `string`
Root-relative base URL of the service, i.e. the prefix `"/_db/"` followed

View File

@ -448,3 +448,19 @@ The bundled JEMalloc memory allocator used in ArangoDB release packages has been
upgraded from version 5.0.1 to version 5.2.0.
The bundled version of the RocksDB library has been upgraded from 5.16 to 6.0.
Foxx
----
Request credentials are now exposed via the `auth` property:
```js
const tokens = context.collection("tokens");
router.get("/authorized", (req, res) => {
if (!req.auth || !req.auth.bearer || !tokens.exists(req.auth.bearer)) {
res.throw(403, "Not authenticated");
}
// ...
});
```

View File

@ -202,6 +202,33 @@ module.exports =
return this._raw.compatibility;
}
get auth () {
const header = this.get("authorization") || "";
let match = header.match(/^Bearer (.*)$/);
if (match) {
return {bearer: match[1]};
}
match = header.match(/^Basic (.*)$/);
if (match) {
let credentials = "";
try {
credentials = new Buffer(match[1], "base64").toString("utf-8");
} catch (e) {}
if (!credentials) return {basic: {}};
const i = credentials.indexOf(":");
if (i === -1) {
return {basic: {username: credentials}};
}
return {
basic: {
username: credentials.slice(0, i),
password: credentials.slice(i + 1)
}
};
}
return null;
}
get database () {
return this._raw.database;
}

View File

@ -273,14 +273,71 @@ describe('SyntheticRequest', function () {
describe('headers', function () {
it('exposes the headers of the native request', function () {
const headers = {};
const rawReq = createNativeRequest({
headers: headers
});
const rawReq = createNativeRequest({ headers });
const req = new SyntheticRequest(rawReq, {});
expect(req.headers).to.equal(headers);
});
});
describe('auth', function () {
function btoa (str) {
return new Buffer(str).toString("base64");
}
it('recognizes no auth', function () {
const rawReq = createNativeRequest({ headers: {} });
const req = new SyntheticRequest(rawReq, {});
expect(req.auth).to.eql(null);
});
it('recognizes bearer auth', function () {
const headers = {authorization: "Bearer deadbeef"};
const rawReq = createNativeRequest({ headers });
const req = new SyntheticRequest(rawReq, {});
expect(req.auth).to.eql({bearer: "deadbeef"});
});
it('recognizes empty basic auth', function () {
const headers = {authorization: `Basic ${btoa("")}`};
const rawReq = createNativeRequest({ headers });
const req = new SyntheticRequest(rawReq, {});
expect(req.auth).to.eql({basic: {}});
});
it('recognizes malformed basic auth', function () {
const headers = {authorization: `Basic \x00\x01`};
const rawReq = createNativeRequest({ headers });
const req = new SyntheticRequest(rawReq, {});
expect(req.auth).to.eql({basic: {}});
});
it('recognizes basic with no password', function () {
const username = "hello";
const headers = {authorization: `Basic ${btoa(username)}`};
const rawReq = createNativeRequest({ headers });
const req = new SyntheticRequest(rawReq, {});
expect(req.auth).to.eql({basic: {username}});
});
it('recognizes basic with empty password', function () {
const username = "hello";
const headers = {authorization: `Basic ${btoa(`${username}:`)}`};
const rawReq = createNativeRequest({ headers });
const req = new SyntheticRequest(rawReq, {});
expect(req.auth).to.eql({basic: {username, password: ""}});
});
it('recognizes basic with password', function () {
const username = "hello";
const password = "world";
const headers = {authorization: `Basic ${btoa(`${username}:${password}`)}`};
const rawReq = createNativeRequest({ headers });
const req = new SyntheticRequest(rawReq, {});
expect(req.auth).to.eql({basic: {username, password}});
});
it('recognizes basic with colons in password', function () {
const username = "hello";
const password = "w:o:r:l:d";
const headers = {authorization: `Basic ${btoa(`${username}:${password}`)}`};
const rawReq = createNativeRequest({ headers });
const req = new SyntheticRequest(rawReq, {});
expect(req.auth).to.eql({basic: {username, password}});
});
});
describe('method', function () {
it('exposes the method of the native request', function () {
const rawReq = createNativeRequest({