diff --git a/dashboard/src/js/client.js b/dashboard/src/js/client.js index f888f16f9..2334c8fe0 100644 --- a/dashboard/src/js/client.js +++ b/dashboard/src/js/client.js @@ -1937,6 +1937,57 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout }); }; + Client.prototype.getOidcClients = function (callback) { + get('/api/v1/oidc/clients', null, function (error, data, status) { + if (error) return callback(error); + if (status !== 200) return callback(new ClientError(status, data)); + + callback(null, data.clients); + }); + }; + + Client.prototype.addOidcClient = function (id, secret, loginRedirectUri, logoutRedirectUri, callback) { + var data = { + id: id, + secret: secret, + loginRedirectUri: loginRedirectUri + }; + + if (logoutRedirectUri) data.logoutRedirectUri = logoutRedirectUri; + + post('/api/v1/oidc/clients', data, null, function (error, data, status) { + if (error) return callback(error); + if (status !== 201) return callback(new ClientError(status, data)); + + callback(null); + }); + }; + + Client.prototype.updateOidcClient = function (id, secret, loginRedirectUri, logoutRedirectUri, callback) { + var data = { + secret: secret, + loginRedirectUri: loginRedirectUri + }; + + if (logoutRedirectUri) data.logoutRedirectUri = logoutRedirectUri; + + post('/api/v1/oidc/clients/' + id, data, null, function (error, data, status) { + if (error) return callback(error); + if (status !== 201) return callback(new ClientError(status, data)); + + callback(null); + }); + }; + + Client.prototype.delOidcClient = function (id, callback) { + del('/api/v1/oidc/clients/' + id, null, function (error, data, status) { + if (error) return callback(error); + if (status !== 204) return callback(new ClientError(status, data)); + + callback(null); + }); + }; + Client.prototype.addAppPassword = function (identifier, name, callback) { var data = { identifier: identifier, diff --git a/dashboard/src/js/index.js b/dashboard/src/js/index.js index 66aef80f5..a7605c3f5 100644 --- a/dashboard/src/js/index.js +++ b/dashboard/src/js/index.js @@ -92,6 +92,9 @@ app.config(['$routeProvider', function ($routeProvider) { }).when('/notifications', { controller: 'NotificationsController', templateUrl: 'views/notifications.html?<%= revision %>' + }).when('/oidc', { + controller: 'OidcController', + templateUrl: 'views/oidc.html?<%= revision %>' }).when('/settings', { controller: 'SettingsController', templateUrl: 'views/settings.html?<%= revision %>' diff --git a/dashboard/src/views/oidc.html b/dashboard/src/views/oidc.html new file mode 100644 index 000000000..e5f8ae78b --- /dev/null +++ b/dashboard/src/views/oidc.html @@ -0,0 +1,140 @@ + + + + + + + +
+ +
+

OpenID Provider

+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
Discovery URLhttps://{{ config.adminDomain }}/.well-known/openid-configuration
Auth Endpointhttps://{{ config.adminFqdn }}/openid/auth
Token Endpointhttps://{{ config.adminFqdn }}/openid/token
Keys Endpointhttps://{{ config.adminFqdn }}/openid/jwks
Profile Endpointhttps://{{ config.adminFqdn }}/openid/me
+
+
+
+
+ +
+ +
+

Clients

+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + +
Client IDClient Secret{{ 'main.actions' | tr }}
No clients yet
+ {{ client.id }} + + {{ client.secret }} + + +
+
+
+
+
+
diff --git a/dashboard/src/views/oidc.js b/dashboard/src/views/oidc.js new file mode 100644 index 000000000..cd03e8694 --- /dev/null +++ b/dashboard/src/views/oidc.js @@ -0,0 +1,104 @@ +'use strict'; + +/* global angular */ +/* global $ */ + +angular.module('Application').controller('OidcController', ['$scope', '$location', 'Client', function ($scope, $location, Client) { + Client.onReady(function () { if (!Client.getUserInfo().isAtLeastAdmin) $location.path('/'); }); + + $scope.user = Client.getUserInfo(); + $scope.config = Client.getConfig(); + $scope.clients = []; + + $scope.refreshClients = function () { + Client.getOidcClients(function (error, result) { + if (error) return console.error('Failed to load oidc clients', error); + + $scope.clients = result; + }); + }; + + $scope.clientAdd = { + busy: false, + error: {}, + id: '', + secret: '', + loginRedirectUri: '', + logoutRedirectUri: '', + + show: function () { + $scope.clientAdd.id = ''; + $scope.clientAdd.secret = ''; + $scope.clientAdd.loginRedirectUri = ''; + $scope.clientAdd.logoutRedirectUri = ''; + $scope.clientAdd.busy = false; + $scope.clientAdd.error = null; + $scope.clientAddForm.$setPristine(); + + $('#clientAddModal').modal('show'); + }, + + submit: function () { + $scope.clientAdd.busy = true; + $scope.clientAdd.error = {}; + + Client.addOidcClient($scope.clientAdd.id, $scope.clientAdd.secret, $scope.clientAdd.loginRedirectUri, $scope.clientAdd.logoutRedirectUri, function (error) { + if (error) { + if (error.statusCode === 409) { + $scope.clientAdd.error.id = 'Client ID already exists'; + $('#clientId').focus(); + } else { + console.error('Unable to add openid client.', error); + } + + $scope.clientAdd.busy = false; + + return; + } + + $scope.refreshClients(); + $scope.clientAdd.busy = false; + + $('#clientAddModal').modal('hide'); + }); + } + }; + + $scope.deleteClient = { + busy: false, + error: {}, + id: '', + + show: function (clientId) { + $scope.deleteClient.busy = false; + $scope.deleteClient.id = clientId; + + $('#clientDeleteModal').modal('show'); + }, + + submit: function () { + Client.delOidcClient($scope.deleteClient.id, function (error) { + $scope.deleteClient.busy = false; + + if (error) return console.error('Failed to delete openid client', error); + + $scope.refreshClients(); + + $('#clientDeleteModal').modal('hide'); + }); + } + }; + + Client.onReady(function () { + $scope.refreshClients(); + }); + + // setup all the dialog focus handling + ['clientAddModal', 'clientEditModal'].forEach(function (id) { + $('#' + id).on('shown.bs.modal', function () { + $(this).find('[autofocus]:first').focus(); + }); + }); + + $('.modal-backdrop').remove(); +}]); diff --git a/src/oidc.js b/src/oidc.js index edbec1012..8f1fa77c5 100644 --- a/src/oidc.js +++ b/src/oidc.js @@ -43,7 +43,7 @@ async function clientsAdd(id, secret, loginRedirectUri, logoutRedirectUri) { assert.strictEqual(typeof loginRedirectUri, 'string'); assert.strictEqual(typeof logoutRedirectUri, 'string'); - const query = 'INSERT INTO oidcClients (id, secret, loginRedirectUri, logoutRedirectUri) VALUES (?, ?, ?)'; + const query = 'INSERT INTO oidcClients (id, secret, loginRedirectUri, logoutRedirectUri) VALUES (?, ?, ?, ?)'; const args = [ id, secret, loginRedirectUri, logoutRedirectUri ]; const [error] = await safe(database.query(query, args));