487 lines
19 KiB
JavaScript
487 lines
19 KiB
JavaScript
'use strict';
|
|
|
|
/* global angular */
|
|
/* global Clipboard */
|
|
/* global $, TASK_TYPES */
|
|
|
|
angular.module('Application').controller('UserSettingsController', ['$scope', '$location', '$translate', '$timeout', 'Client', function ($scope, $location, $translate, $timeout, Client) {
|
|
Client.onReady(function () { if (!Client.getUserInfo().isAtLeastAdmin) $location.path('/'); });
|
|
|
|
$scope.ldapProvider = [
|
|
{ name: 'Active Directory', value: 'ad' },
|
|
{ name: 'Cloudron', value: 'cloudron' },
|
|
{ name: 'Jumpcloud', value: 'jumpcloud' },
|
|
{ name: 'Okta', value: 'okta' },
|
|
{ name: 'Univention Corporate Server (UCS)', value: 'univention' },
|
|
{ name: 'Other', value: 'other' },
|
|
{ name: 'Disabled', value: 'noop' }
|
|
];
|
|
|
|
$translate(['users.externalLdap.providerOther', 'users.externalLdap.providerDisabled']).then(function (tr) {
|
|
if (tr['users.externalLdap.providerOther']) $scope.ldapProvider.find(function (p) { return p.value === 'other'; }).name = tr['users.externalLdap.providerOther'];
|
|
if (tr['users.externalLdap.providerDisabled']) $scope.ldapProvider.find(function (p) { return p.value === 'noop'; }).name = tr['users.externalLdap.providerDisabled'];
|
|
});
|
|
|
|
$scope.ready = false;
|
|
$scope.config = Client.getConfig();
|
|
$scope.userInfo = Client.getUserInfo();
|
|
$scope.adminDomain = null;
|
|
$scope.oidcClients = [];
|
|
|
|
$scope.profileConfig = {
|
|
editableUserProfiles: true,
|
|
mandatory2FA: false,
|
|
errorMessage: '',
|
|
|
|
refresh: function () {
|
|
Client.getProfileConfig(function (error, result) {
|
|
if (error) return console.error('Unable to get directory config.', error);
|
|
|
|
$scope.profileConfig.editableUserProfiles = !result.lockUserProfiles;
|
|
$scope.profileConfig.mandatory2FA = !!result.mandatory2FA;
|
|
});
|
|
},
|
|
|
|
submit: function () {
|
|
$scope.profileConfig.error = '';
|
|
$scope.profileConfig.busy = true;
|
|
$scope.profileConfig.success = false;
|
|
|
|
var data = {
|
|
lockUserProfiles: !$scope.profileConfig.editableUserProfiles,
|
|
mandatory2FA: $scope.profileConfig.mandatory2FA
|
|
};
|
|
|
|
Client.setProfileConfig(data, function (error) {
|
|
if (error) $scope.profileConfig.errorMessage = error.message;
|
|
|
|
$scope.profileConfig.success = true;
|
|
|
|
$scope.profileConfigForm.$setUntouched();
|
|
$scope.profileConfigForm.$setPristine();
|
|
|
|
Client.refreshConfig(); // refresh the $scope.config
|
|
|
|
$timeout(function () {
|
|
$scope.profileConfig.busy = false;
|
|
|
|
// prevent the current user from getting locked out. if user ignores this, they have to use cloudron-support --admin-login
|
|
if ($scope.profileConfig.mandatory2FA && !$scope.userInfo.twoFactorAuthenticationEnabled) {
|
|
if ($scope.userInfo.source && $scope.config.external2FA) return; // no need for warning if 2fa is external
|
|
Client.notify('', $translate.instant('users.settings.require2FAWarning'), true, 'error', '#/profile');
|
|
}
|
|
}, 500);
|
|
});
|
|
}
|
|
};
|
|
|
|
$scope.userDirectoryConfig = {
|
|
enabled: false,
|
|
secret: '',
|
|
allowlist: '',
|
|
error: null,
|
|
|
|
refresh: function () {
|
|
Client.getUserDirectoryConfig(function (error, result) {
|
|
if (error) return console.error('Unable to get exposed ldap config.', error);
|
|
|
|
$scope.userDirectoryConfig.enabled = !!result.enabled;
|
|
$scope.userDirectoryConfig.allowlist = result.allowlist;
|
|
$scope.userDirectoryConfig.secret = result.secret;
|
|
});
|
|
},
|
|
|
|
submit: function () {
|
|
$scope.userDirectoryConfig.error = null;
|
|
$scope.userDirectoryConfig.busy = true;
|
|
$scope.userDirectoryConfig.success = false;
|
|
|
|
var data = {
|
|
enabled: $scope.userDirectoryConfig.enabled,
|
|
secret: $scope.userDirectoryConfig.secret,
|
|
allowlist: $scope.userDirectoryConfig.allowlist
|
|
};
|
|
|
|
Client.setUserDirectoryConfig(data, function (error) {
|
|
$scope.userDirectoryConfig.busy = false;
|
|
|
|
if (error && error.statusCode === 400) {
|
|
if (error.message.indexOf('secret') !== -1) return $scope.userDirectoryConfig.error = { secret: error.message };
|
|
else return $scope.userDirectoryConfig.error = { allowlist: error.message };
|
|
}
|
|
if (error) return $scope.userDirectoryConfig.error = { generic: error.message };
|
|
|
|
$scope.userDirectoryConfigForm.$setUntouched();
|
|
$scope.userDirectoryConfigForm.$setPristine();
|
|
|
|
$scope.userDirectoryConfig.success = true;
|
|
});
|
|
}
|
|
};
|
|
|
|
$scope.externalLdap = {
|
|
busy: false,
|
|
percent: 0,
|
|
message: '',
|
|
errorMessage: '', // last task error
|
|
tasks: [],
|
|
|
|
error: {}, // save error
|
|
saveBusy: false,
|
|
|
|
// fields
|
|
provider: 'noop',
|
|
autoCreate: true,
|
|
url: '',
|
|
acceptSelfSignedCerts: false,
|
|
baseDn: '',
|
|
filter: '',
|
|
groupBaseDn: '',
|
|
bindDn: '',
|
|
bindPassword: '',
|
|
usernameField: '',
|
|
|
|
currentConfig: {},
|
|
|
|
init: function () {
|
|
Client.getExternalLdapConfig(function (error, result) {
|
|
if (error) return console.error('Unable to get external ldap config.', error);
|
|
$scope.externalLdap.currentConfig = result;
|
|
$scope.externalLdap.refreshTasks();
|
|
});
|
|
},
|
|
|
|
refreshTasks: function () {
|
|
Client.getTasksByType(TASK_TYPES.TASK_SYNC_EXTERNAL_LDAP, function (error, tasks) {
|
|
if (error) return console.error(error);
|
|
$scope.externalLdap.tasks = tasks.slice(0, 10);
|
|
if ($scope.externalLdap.tasks.length && $scope.externalLdap.tasks[0].active) $scope.externalLdap.updateStatus();
|
|
});
|
|
},
|
|
|
|
updateStatus: function () {
|
|
var taskId = $scope.externalLdap.tasks[0].id;
|
|
|
|
Client.getTask(taskId, function (error, data) {
|
|
if (error) return window.setTimeout($scope.externalLdap.updateStatus, 5000);
|
|
|
|
if (!data.active) {
|
|
$scope.externalLdap.busy = false;
|
|
$scope.externalLdap.message = '';
|
|
$scope.externalLdap.percent = 100; // indicates that 'result' is valid
|
|
$scope.externalLdap.errorMessage = data.success ? '' : data.error.message;
|
|
|
|
$scope.externalLdap.refreshTasks(); // update the tasks list dropdown
|
|
|
|
return;
|
|
}
|
|
|
|
$scope.externalLdap.busy = true;
|
|
$scope.externalLdap.percent = data.percent;
|
|
$scope.externalLdap.message = data.message;
|
|
window.setTimeout($scope.externalLdap.updateStatus, 500);
|
|
});
|
|
},
|
|
|
|
show: function () {
|
|
$scope.externalLdap.busy = false;
|
|
$scope.externalLdap.error = {};
|
|
|
|
$scope.externalLdap.provider = $scope.externalLdap.currentConfig.provider;
|
|
$scope.externalLdap.url = $scope.externalLdap.currentConfig.url;
|
|
$scope.externalLdap.acceptSelfSignedCerts = $scope.externalLdap.currentConfig.acceptSelfSignedCerts;
|
|
$scope.externalLdap.baseDn = $scope.externalLdap.currentConfig.baseDn;
|
|
$scope.externalLdap.filter = $scope.externalLdap.currentConfig.filter;
|
|
$scope.externalLdap.syncGroups = $scope.externalLdap.currentConfig.syncGroups;
|
|
$scope.externalLdap.groupBaseDn = $scope.externalLdap.currentConfig.groupBaseDn;
|
|
$scope.externalLdap.groupFilter = $scope.externalLdap.currentConfig.groupFilter;
|
|
$scope.externalLdap.groupnameField = $scope.externalLdap.currentConfig.groupnameField;
|
|
$scope.externalLdap.bindDn = $scope.externalLdap.currentConfig.bindDn;
|
|
$scope.externalLdap.bindPassword = $scope.externalLdap.currentConfig.bindPassword;
|
|
$scope.externalLdap.usernameField = $scope.externalLdap.currentConfig.usernameField;
|
|
$scope.externalLdap.autoCreate = $scope.externalLdap.currentConfig.autoCreate;
|
|
|
|
$('#externalLdapModal').modal('show');
|
|
},
|
|
|
|
sync: function () {
|
|
$scope.externalLdap.busy = true;
|
|
$scope.externalLdap.percent = 0;
|
|
$scope.externalLdap.message = '';
|
|
$scope.externalLdap.errorMessage = '';
|
|
|
|
Client.startExternalLdapSync(function (error) {
|
|
if (error) {
|
|
console.error(error);
|
|
$scope.externalLdap.errorMessage = error.message;
|
|
$scope.externalLdap.busy = false;
|
|
} else {
|
|
$scope.externalLdap.refreshTasks();
|
|
}
|
|
});
|
|
},
|
|
|
|
submit: function () {
|
|
$scope.externalLdap.saveBusy = true;
|
|
$scope.externalLdap.error = {};
|
|
|
|
var config = {
|
|
provider: $scope.externalLdap.provider
|
|
};
|
|
|
|
if ($scope.externalLdap.provider === 'cloudron') {
|
|
config.url = $scope.externalLdap.url;
|
|
config.acceptSelfSignedCerts = $scope.externalLdap.acceptSelfSignedCerts;
|
|
config.autoCreate = $scope.externalLdap.autoCreate;
|
|
config.syncGroups = $scope.externalLdap.syncGroups;
|
|
config.bindPassword = $scope.externalLdap.bindPassword;
|
|
|
|
// those values are known and thus overwritten
|
|
config.baseDn = 'ou=users,dc=cloudron';
|
|
config.filter = '(objectClass=inetOrgPerson)';
|
|
config.usernameField = 'username';
|
|
config.groupBaseDn = 'ou=groups,dc=cloudron';
|
|
config.groupFilter = '(objectClass=group)';
|
|
config.groupnameField = 'cn';
|
|
config.bindDn = 'cn=admin,ou=system,dc=cloudron';
|
|
} else if ($scope.externalLdap.provider !== 'noop') {
|
|
config.url = $scope.externalLdap.url;
|
|
config.acceptSelfSignedCerts = $scope.externalLdap.acceptSelfSignedCerts;
|
|
config.baseDn = $scope.externalLdap.baseDn;
|
|
config.filter = $scope.externalLdap.filter;
|
|
config.usernameField = $scope.externalLdap.usernameField;
|
|
config.syncGroups = $scope.externalLdap.syncGroups;
|
|
config.groupBaseDn = $scope.externalLdap.groupBaseDn;
|
|
config.groupFilter = $scope.externalLdap.groupFilter;
|
|
config.groupnameField = $scope.externalLdap.groupnameField;
|
|
config.autoCreate = $scope.externalLdap.autoCreate;
|
|
|
|
if ($scope.externalLdap.bindDn) {
|
|
config.bindDn = $scope.externalLdap.bindDn;
|
|
config.bindPassword = $scope.externalLdap.bindPassword;
|
|
}
|
|
}
|
|
|
|
Client.setExternalLdapConfig(config, function (error) {
|
|
$scope.externalLdap.saveBusy = false;
|
|
|
|
if (error) {
|
|
if (error.statusCode === 424) {
|
|
if (error.code === 'SELF_SIGNED_CERT_IN_CHAIN') $scope.externalLdap.error.acceptSelfSignedCerts = true;
|
|
else $scope.externalLdap.error.url = true;
|
|
$scope.externalLdap.error.generic = error.message;
|
|
} else if (error.statusCode === 400 && error.message === 'invalid baseDn') {
|
|
$scope.externalLdap.error.baseDn = true;
|
|
} else if (error.statusCode === 400 && error.message === 'invalid filter') {
|
|
$scope.externalLdap.error.filter = true;
|
|
} else if (error.statusCode === 400 && error.message === 'invalid groupBaseDn') {
|
|
$scope.externalLdap.error.groupBaseDn = true;
|
|
} else if (error.statusCode === 400 && error.message === 'invalid groupFilter') {
|
|
$scope.externalLdap.error.groupFilter = true;
|
|
} else if (error.statusCode === 400 && error.message === 'invalid groupnameField') {
|
|
$scope.externalLdap.error.groupnameField = true;
|
|
} else if (error.statusCode === 400 && error.message === 'invalid bind credentials') {
|
|
$scope.externalLdap.error.credentials = true;
|
|
} else if (error.statusCode === 400 && error.message === 'invalid usernameField') {
|
|
$scope.externalLdap.error.usernameField = true;
|
|
} else {
|
|
console.error('Failed to set external LDAP config:', error);
|
|
$scope.externalLdap.error.generic = error.message;
|
|
}
|
|
} else {
|
|
$('#externalLdapModal').modal('hide');
|
|
$scope.externalLdap.init();
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
$scope.refreshOIDCClients = function () {
|
|
Client.getOidcClients(function (error, result) {
|
|
if (error) return console.error('Failed to load oidc clients', error);
|
|
|
|
$scope.oidcClients = result;
|
|
});
|
|
};
|
|
|
|
$scope.clientAdd = {
|
|
busy: false,
|
|
error: null,
|
|
name: '',
|
|
loginRedirectUri: '',
|
|
tokenSignatureAlgorithm: '',
|
|
|
|
show: function () {
|
|
$scope.clientAdd.name = '';
|
|
$scope.clientAdd.loginRedirectUri = '';
|
|
$scope.clientAdd.tokenSignatureAlgorithm = 'RS256';
|
|
$scope.clientAdd.busy = false;
|
|
$scope.clientAdd.error = null;
|
|
$scope.clientAddForm.$setPristine();
|
|
|
|
$('#oidcClientAddModal').modal('show');
|
|
},
|
|
|
|
submit: function () {
|
|
$scope.clientAdd.busy = true;
|
|
$scope.clientAdd.error = null;
|
|
|
|
Client.addOidcClient($scope.clientAdd.name, $scope.clientAdd.loginRedirectUri, $scope.clientAdd.tokenSignatureAlgorithm, function (error) {
|
|
if (error) {
|
|
$scope.clientAdd.error = error.message;
|
|
console.error('Unable to add openid client.', error);
|
|
$scope.clientAdd.busy = false;
|
|
return;
|
|
}
|
|
|
|
$scope.refreshOIDCClients();
|
|
$scope.clientAdd.busy = false;
|
|
|
|
$('#oidcClientAddModal').modal('hide');
|
|
});
|
|
}
|
|
};
|
|
|
|
$scope.clientEdit = {
|
|
busy: false,
|
|
error: null,
|
|
id: '',
|
|
secret: '',
|
|
name: '',
|
|
loginRedirectUri: '',
|
|
tokenSignatureAlgorithm: '',
|
|
|
|
show: function (client) {
|
|
$scope.clientEdit.id = client.id;
|
|
$scope.clientEdit.secret = client.secret;
|
|
$scope.clientEdit.name = client.name;
|
|
$scope.clientEdit.loginRedirectUri = client.loginRedirectUri;
|
|
$scope.clientEdit.tokenSignatureAlgorithm = client.tokenSignatureAlgorithm;
|
|
$scope.clientEdit.busy = false;
|
|
$scope.clientEdit.error = null;
|
|
$scope.clientEditForm.$setPristine();
|
|
|
|
$('#oidcClientEditModal').modal('show');
|
|
},
|
|
|
|
submit: function () {
|
|
$scope.clientEdit.busy = true;
|
|
$scope.clientEdit.error = null;
|
|
|
|
Client.updateOidcClient($scope.clientEdit.id, $scope.clientEdit.name, $scope.clientEdit.loginRedirectUri, $scope.clientEdit.tokenSignatureAlgorithm, function (error) {
|
|
if (error) {
|
|
$scope.clientEdit.error = error.message;
|
|
console.error('Unable to edit openid client.', error);
|
|
$scope.clientEdit.busy = false;
|
|
return;
|
|
}
|
|
|
|
$scope.refreshOIDCClients();
|
|
$scope.clientEdit.busy = false;
|
|
|
|
$('#oidcClientEditModal').modal('hide');
|
|
});
|
|
}
|
|
};
|
|
|
|
$scope.deleteClient = {
|
|
busy: false,
|
|
error: {},
|
|
id: '',
|
|
|
|
show: function (client) {
|
|
$scope.deleteClient.busy = false;
|
|
$scope.deleteClient.id = client.id;
|
|
|
|
$('#oidcClientDeleteModal').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.refreshOIDCClients();
|
|
|
|
$('#oidcClientDeleteModal').modal('hide');
|
|
});
|
|
}
|
|
};
|
|
|
|
Client.onReady(function () {
|
|
$scope.externalLdap.init();
|
|
$scope.profileConfig.refresh();
|
|
$scope.userDirectoryConfig.refresh();
|
|
$scope.refreshOIDCClients();
|
|
|
|
Client.getDomains(function (error, result) {
|
|
if (error) return console.error('Unable to list domains.', error);
|
|
$scope.adminDomain = result.filter(function (d) { return d.domain === $scope.config.adminDomain; })[0];
|
|
});
|
|
});
|
|
|
|
// setup all the dialog focus handling
|
|
['oidcClientAddModal', 'oidcClientEditModal'].forEach(function (id) {
|
|
$('#' + id).on('shown.bs.modal', function () {
|
|
$(this).find('[autofocus]:first').focus();
|
|
});
|
|
});
|
|
|
|
new Clipboard('#userDirectoryUrlClipboardButton').on('success', function(e) {
|
|
$('#userDirectoryUrlClipboardButton').tooltip({
|
|
title: 'Copied!',
|
|
trigger: 'manual'
|
|
}).tooltip('show');
|
|
|
|
$timeout(function () { $('#userDirectoryUrlClipboardButton').tooltip('hide'); }, 2000);
|
|
|
|
e.clearSelection();
|
|
}).on('error', function(/*e*/) {
|
|
$('#userDirectoryUrlClipboardButton').tooltip({
|
|
title: 'Press Ctrl+C to copy',
|
|
trigger: 'manual'
|
|
}).tooltip('show');
|
|
|
|
$timeout(function () { $('#userDirectoryUrlClipboardButton').tooltip('hide'); }, 2000);
|
|
});
|
|
|
|
new Clipboard('#clientIdInputClipboardButton').on('success', function(e) {
|
|
$('#clientIdInputClipboardButton').tooltip({
|
|
title: 'Copied!',
|
|
trigger: 'manual'
|
|
}).tooltip('show');
|
|
|
|
$timeout(function () { $('#clientIdInputClipboardButton').tooltip('hide'); }, 2000);
|
|
|
|
e.clearSelection();
|
|
}).on('error', function(/*e*/) {
|
|
$('#clientIdInputClipboardButton').tooltip({
|
|
title: 'Press Ctrl+C to copy',
|
|
trigger: 'manual'
|
|
}).tooltip('show');
|
|
|
|
$timeout(function () { $('#clientIdInputClipboardButton').tooltip('hide'); }, 2000);
|
|
});
|
|
|
|
new Clipboard('#clientSecretInputClipboardButton').on('success', function(e) {
|
|
$('#clientSecretInputClipboardButton').tooltip({
|
|
title: 'Copied!',
|
|
trigger: 'manual'
|
|
}).tooltip('show');
|
|
|
|
$timeout(function () { $('#clientSecretInputClipboardButton').tooltip('hide'); }, 2000);
|
|
|
|
e.clearSelection();
|
|
}).on('error', function(/*e*/) {
|
|
$('#clientSecretInputClipboardButton').tooltip({
|
|
title: 'Press Ctrl+C to copy',
|
|
trigger: 'manual'
|
|
}).tooltip('show');
|
|
|
|
$timeout(function () { $('#clientSecretInputClipboardButton').tooltip('hide'); }, 2000);
|
|
});
|
|
|
|
$('.modal-backdrop').remove();
|
|
}]);
|