oidc: Add clients manage routes
This commit is contained in:
@@ -17,6 +17,7 @@ exports = module.exports = {
|
||||
mailserver: require('./mailserver.js'),
|
||||
network: require('./network.js'),
|
||||
notifications: require('./notifications.js'),
|
||||
oidcclients: require('./oidcclients.js'),
|
||||
profile: require('./profile.js'),
|
||||
provision: require('./provision.js'),
|
||||
services: require('./services.js'),
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
|
||||
exports = module.exports = {
|
||||
get,
|
||||
list,
|
||||
add,
|
||||
update,
|
||||
remove
|
||||
};
|
||||
|
||||
const assert = require('assert'),
|
||||
BoxError = require('../boxerror.js'),
|
||||
oidc = require('../oidc.js'),
|
||||
HttpError = require('connect-lastmile').HttpError,
|
||||
HttpSuccess = require('connect-lastmile').HttpSuccess,
|
||||
safe = require('safetydance');
|
||||
|
||||
async function add(req, res, next) {
|
||||
assert.strictEqual(typeof req.body, 'object');
|
||||
|
||||
if (typeof req.body.id !== 'string' || !req.body.id) return next(new HttpError(400, 'id must be non-empty string'));
|
||||
if (typeof req.body.secret !== 'string' || !req.body.secret) return next(new HttpError(400, 'secret must be non-empty string'));
|
||||
if (typeof req.body.loginRedirectUri !== 'string' || !req.body.loginRedirectUri) return next(new HttpError(400, 'loginRedirectUri must be non-empty string'));
|
||||
if ('logoutRedirectUri' in req.body && (typeof req.body.logoutRedirectUri !== 'string' || !req.body.logoutRedirectUri)) return next(new HttpError(400, 'logoutRedirectUri must be non-empty string if provided'));
|
||||
|
||||
const [error] = await safe(oidc.clients.add(req.body.id, req.body.secret, req.body.loginRedirectUri, req.body.logoutRedirectUri || ''));
|
||||
if (error) return next(BoxError.toHttpError(error));
|
||||
|
||||
next(new HttpSuccess(201, {}));
|
||||
}
|
||||
|
||||
async function get(req, res, next) {
|
||||
assert.strictEqual(typeof req.params.clientId, 'string');
|
||||
|
||||
const [error, client] = await safe(oidc.clients.get(req.params.clientId));
|
||||
if (error) return next(BoxError.toHttpError(error));
|
||||
if (!result) return next(new HttpError(404, 'OpenID connect client not found'));
|
||||
|
||||
next(new HttpSuccess(200, client));
|
||||
}
|
||||
|
||||
async function update(req, res, next) {
|
||||
assert.strictEqual(typeof req.params.clientId, 'string');
|
||||
assert.strictEqual(typeof req.body, 'object');
|
||||
|
||||
if (typeof req.body.secret !== 'string' || !req.body.secret) return next(new HttpError(400, 'secret must be non-empty string'));
|
||||
if (typeof req.body.loginRedirectUri !== 'string' || !req.body.loginRedirectUri) return next(new HttpError(400, 'loginRedirectUri must be non-empty string'));
|
||||
if ('logoutRedirectUri' in req.body && (typeof req.body.logoutRedirectUri !== 'string' || !req.body.logoutRedirectUri)) return next(new HttpError(400, 'logoutRedirectUri must be non-empty string if provided'));
|
||||
|
||||
const [error] = await safe(oidc.clients.update(req.params.clientId, { secret: req.body.secret, loginRedirectUri: req.body.loginRedirectUri, logoutRedirectUri: req.body.logoutRedirectUri || ''}));
|
||||
if (error) return next(BoxError.toHttpError(error));
|
||||
|
||||
next(new HttpSuccess(200, {}));
|
||||
}
|
||||
|
||||
async function list(req, res, next) {
|
||||
const [error, result] = await safe(oidc.clients.list());
|
||||
if (error) return next(BoxError.toHttpError(error));
|
||||
|
||||
next(new HttpSuccess(200, { clients: result }));
|
||||
}
|
||||
|
||||
async function remove(req, res, next) {
|
||||
assert.strictEqual(typeof req.params.clientId, 'string');
|
||||
|
||||
const [error] = await safe(oidc.clients.del(req.params.clientId));
|
||||
if (error) return next(BoxError.toHttpError(error));
|
||||
|
||||
next(new HttpSuccess(204));
|
||||
}
|
||||
@@ -56,9 +56,10 @@ exports = module.exports = {
|
||||
};
|
||||
|
||||
async function setupServer() {
|
||||
await server.start();
|
||||
await database._clear();
|
||||
await database.initialize();
|
||||
await settings._setApiServerOrigin(exports.mockApiServerOrigin);
|
||||
await server.start();
|
||||
}
|
||||
|
||||
async function setup() {
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/* jslint node:true */
|
||||
/* global it:false */
|
||||
/* global describe:false */
|
||||
/* global before:false */
|
||||
/* global after:false */
|
||||
|
||||
'use strict';
|
||||
|
||||
const common = require('./common.js'),
|
||||
expect = require('expect.js'),
|
||||
superagent = require('superagent');
|
||||
|
||||
const CLIENT_0 = {
|
||||
id: 'client0',
|
||||
secret: 'secret0',
|
||||
loginRedirectUri: 'http://foo.bar',
|
||||
logoutRedirectUri: ''
|
||||
};
|
||||
|
||||
const CLIENT_1 = {
|
||||
id: 'client1',
|
||||
secret: 'secret1',
|
||||
loginRedirectUri: 'https://cloudron.io/login',
|
||||
logoutRedirectUri: 'https://cloudron.io/logout'
|
||||
};
|
||||
|
||||
describe('OpenID connect clients API', function () {
|
||||
const { setup, cleanup, serverUrl, owner, user } = common;
|
||||
|
||||
before(setup);
|
||||
after(cleanup);
|
||||
|
||||
it('create fails due to missing token', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/oidc/clients`)
|
||||
.send(CLIENT_0)
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(401);
|
||||
});
|
||||
|
||||
it('create succeeds without logoutRedirectUri', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/oidc/clients`)
|
||||
.query({ access_token: owner.token })
|
||||
.send(CLIENT_0);
|
||||
|
||||
expect(response.statusCode).to.equal(201);
|
||||
});
|
||||
|
||||
it('create fails for already exists', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/oidc/clients`)
|
||||
.query({ access_token: owner.token })
|
||||
.send(CLIENT_0)
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(409);
|
||||
});
|
||||
|
||||
it('can create another client with logoutRedirectUri', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/groups`)
|
||||
.query({ access_token: owner.token })
|
||||
.send({ name: 'group1'});
|
||||
|
||||
expect(response.statusCode).to.equal(201);
|
||||
});
|
||||
|
||||
it('cannot get non-existing client', async function () {
|
||||
const response = await superagent.get(`${serverUrl}/api/v1/oidc/clients/nope`)
|
||||
.query({ access_token: owner.token })
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(404);
|
||||
});
|
||||
|
||||
it('cannot get existing client with normal user', async function () {
|
||||
const response = await superagent.get(`${serverUrl}/api/v1/oidc/clients/${CLIENT_0.id}`)
|
||||
.query({ access_token: user.token })
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(403);
|
||||
});
|
||||
|
||||
it('can get existing client', async function () {
|
||||
const response = await superagent.get(`${serverUrl}/api/v1/oidc/clients/${CLIENT_1.id}`)
|
||||
.query({ access_token: owner.token });
|
||||
|
||||
expect(response.statusCode).to.equal(200);
|
||||
expect(response.body.id).to.equal(CLIENT_1.id);
|
||||
expect(response.body.secret).to.equal(CLIENT_1.secret);
|
||||
expect(response.body.loginRedirectUri).to.equal(CLIENT_1.loginRedirectUri);
|
||||
expect(response.body.logoutRedirectUri).to.equal(CLIENT_1.logoutRedirectUri);
|
||||
});
|
||||
|
||||
it('cannot update non-existent client', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/oidc/clients/nope`)
|
||||
.query({ access_token: owner.token })
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(404);
|
||||
});
|
||||
|
||||
it('cannot list clients without token', async function () {
|
||||
const response = await superagent.get(`${serverUrl}/api/v1/oidc/clients`)
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(401);
|
||||
});
|
||||
|
||||
it('cannot list clients as normal user', async function () {
|
||||
const response = await superagent.get(`${serverUrl}/api/v1/oidc/clients`)
|
||||
.query({ access_token: user.token })
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(403);
|
||||
});
|
||||
|
||||
it('can list clients', async function () {
|
||||
const response = await superagent.get(`${serverUrl}/api/v1/oidc/clients`)
|
||||
.query({ access_token: owner.token });
|
||||
|
||||
expect(response.statusCode).to.equal(200);
|
||||
console.log(response.body)
|
||||
expect(response.body.groups).to.be.an(Array);
|
||||
expect(response.body.groups.length).to.be(2);
|
||||
expect(response.body.groups[0].name).to.eql(group0Object.name);
|
||||
expect(response.body.groups[1].name).to.eql(group1Object.name);
|
||||
});
|
||||
|
||||
it('cannot update client without secret', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/oidc/clients/${CLIENT_0.id}`)
|
||||
.query({ access_token: owner.token })
|
||||
.body({ loginRedirectUri: CLIENT_0.loginRedirectUrl })
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(400);
|
||||
});
|
||||
|
||||
it('cannot update client without loginRedirectUrl', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/oidc/clients/${CLIENT_0.id}`)
|
||||
.query({ access_token: owner.token })
|
||||
.body({ secret: CLIENT_0.secret })
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(400);
|
||||
});
|
||||
|
||||
it('can update client without logoutRedirectUri', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/oidc/clients/${CLIENT_0.id}`)
|
||||
.query({ access_token: owner.token })
|
||||
.body({ secret: 'newsecret', loginRedirectUri: CLIENT_0.loginRedirectUrl })
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(200);
|
||||
|
||||
const response2 = await superagent.get(`${serverUrl}/api/v1/oidc/clients/${CLIENT_0.id}`)
|
||||
.query({ access_token: owner.token });
|
||||
|
||||
expect(response2.statusCode).to.equal(200);
|
||||
expect(response2.body.secret).to.equal('newsecret');
|
||||
});
|
||||
|
||||
it('can update client with logoutRedirectUri', async function () {
|
||||
const response = await superagent.post(`${serverUrl}/api/v1/oidc/clients/${CLIENT_0.id}`)
|
||||
.query({ access_token: owner.token })
|
||||
.body({ secret: 'newsecret', loginRedirectUri: CLIENT_0.loginRedirectUrl, logoutRedirectUri: CLIENT_1.logoutRedirectUri })
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(200);
|
||||
|
||||
const response2 = await superagent.get(`${serverUrl}/api/v1/oidc/clients/${CLIENT_0.id}`)
|
||||
.query({ access_token: owner.token });
|
||||
|
||||
expect(response2.statusCode).to.equal(200);
|
||||
expect(response2.body.secret).to.equal('newsecret');
|
||||
expect(response2.body.loginRedirectUrl).to.equal(CLIENT_0.loginRedirectUrl);
|
||||
expect(response2.body.logoutRedirectUri).to.equal(CLIENT_1.logoutRedirectUri);
|
||||
});
|
||||
|
||||
it('cannot remove without token', async function () {
|
||||
const response = await superagent.del(`${serverUrl}/api/v1/oidc/clients/${CLIENT_0.id}`)
|
||||
.ok(() => true);
|
||||
|
||||
expect(response.statusCode).to.equal(401);
|
||||
});
|
||||
|
||||
it('can remove empty group', async function () {
|
||||
const response = await superagent.del(`${serverUrl}/api/v1/oidc/clients/${CLIENT_0.id}`)
|
||||
.query({ access_token: owner.token });
|
||||
|
||||
expect(response.statusCode).to.equal(204);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user