Commit dbf0c519 authored by Sascha Pfeiffer's avatar Sascha Pfeiffer

Fix #38, Fix #33, added share_admin management

Signed-off-by: default avatarSascha Pfeiffer <sascha.pfeiffer@psono.com>
parent 7dc2ff43
......@@ -47,7 +47,6 @@ build-docker-image:
- sh ./var/build-ubuntu.sh
- curl -fSL "https://download.docker.com/linux/static/stable/x86_64/docker-17.12.0-ce.tgz" -o docker.tgz && echo "692e1c72937f6214b1038def84463018d8e320c8eaf8530546c84c2f8f9c767d *docker.tgz" | sha256sum -c - && tar -xzvf docker.tgz && mv docker/* /usr/local/bin/
- docker info
- echo $CI_BUILD_TOKEN | docker login --username=gitlab-ci-token --password-stdin registry.gitlab.com
- echo $artifactory_credentials | docker login --username=gitlab --password-stdin psono-docker.jfrog.io
- docker build -t $CONTAINER_TEST_IMAGE --pull .
- docker push $CONTAINER_TEST_IMAGE
......@@ -150,7 +149,6 @@ release-docker:
- docker:dind
script:
- docker info
- echo $CI_BUILD_TOKEN | docker login --username=gitlab-ci-token --password-stdin registry.gitlab.com
- echo $artifactory_credentials | docker login --username=gitlab --password-stdin psono-docker.jfrog.io
- docker pull $CONTAINER_TEST_IMAGE
- docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
......
......@@ -161,6 +161,7 @@ var build = function(build_path, type) {
"src/common/data/js/controller/modal/NewFolderCtrl.js",
"src/common/data/js/controller/modal/VerifyCtrl.js",
"src/common/data/js/controller/modal/NewGroupCtrl.js",
"src/common/data/js/controller/modal/PickUserCtrl.js",
"src/common/data/js/controller/modal/EditGroupCtrl.js",
"src/common/data/js/controller/modal/ShareEditEntryCtrl.js",
"src/common/data/js/controller/modal/ShareEntryCtrl.js",
......
......@@ -160,6 +160,7 @@
<script src="js/controller/modal/NewFolderCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/VerifyCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/NewGroupCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/PickUserCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/EditGroupCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/ShareEditEntryCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/ShareEntryCtrl.js" type="application/javascript"></script>
......
......@@ -88,6 +88,7 @@
<script src="js/controller/modal/NewFolderCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/VerifyCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/NewGroupCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/PickUserCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/EditGroupCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/ShareEditEntryCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/ShareEntryCtrl.js" type="application/javascript"></script>
......
......@@ -362,6 +362,7 @@
<script src="js/controller/modal/NewFolderCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/VerifyCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/NewGroupCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/PickUserCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/EditGroupCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/ShareEditEntryCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/ShareEntryCtrl.js" type="application/javascript"></script>
......
......@@ -139,7 +139,7 @@
<!--<li><a href="https://www.psono.pw/help" target="_blank">Help</a></li>-->
<li>
<a class="navigationitem" href="https://doc.psono.com/" target="_blank" rel="noopener">
<span class="fa-stack"><i class="fa fa-user-secret fa-fw" aria-hidden="true"></i></span>Documentation
<span class="fa-stack"><i class="fa fa-book fa-fw" aria-hidden="true"></i></span>Documentation
</a>
</li>
<li>
......@@ -450,6 +450,7 @@
<script src="js/controller/modal/NewFolderCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/VerifyCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/NewGroupCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/PickUserCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/EditGroupCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/ShareEditEntryCtrl.js" type="application/javascript"></script>
<script src="js/controller/modal/ShareEntryCtrl.js" type="application/javascript"></script>
......
......@@ -273,7 +273,9 @@
},
item: function () {
return {
'share_right_read': true
'share_right_read': true,
'share_right_create_user_id': group.user_id,
'share_right_create_user_username': group.user_username
};
},
user: function () {
......@@ -281,6 +283,9 @@
'user_id': group.user_id,
'user_username': group.user_username
};
},
hide_user: function () {
return !group.user_id;
}
}
});
......@@ -354,7 +359,9 @@
},
item: function () {
return {
'share_right_read': true
'share_right_read': true,
'share_right_create_user_id': group.user_id,
'share_right_create_user_username': group.user_username
};
},
user: function () {
......@@ -362,6 +369,9 @@
'user_id': group.user_id,
'user_username': group.user_username
};
},
hide_user: function () {
return !group.user_id;
}
}
});
......
......@@ -93,6 +93,9 @@
'user_id': item.share_right_create_user_id,
'user_username': item.share_right_create_user_username
};
},
hide_user: function () {
return false;
}
}
});
......
......@@ -17,9 +17,9 @@
* Controller for the "AcceptShare" modal
*/
angular.module('psonocli').controller('ModalAcceptShareCtrl', ['$scope', '$uibModalInstance', '$uibModal',
'managerDatastoreUser', 'message', 'shareBlueprint', 'title', 'item', 'cryptoLibrary',
'managerDatastoreUser', 'message', 'shareBlueprint', 'title', 'item', 'hide_user', 'cryptoLibrary',
function ($scope, $uibModalInstance, $uibModal,
managerDatastoreUser, message, shareBlueprint, title, item, cryptoLibrary) {
managerDatastoreUser, message, shareBlueprint, title, item, hide_user, cryptoLibrary) {
$scope.cut_breadcrumbs = cut_breadcrumbs;
$scope.clear_breadcrumbs = clear_breadcrumbs;
......@@ -28,6 +28,7 @@
$scope.title = title;
$scope.item = item;
$scope.hide_user = hide_user;
$scope.user_is_trusted = false;
$scope.trust = trust;
......@@ -45,39 +46,41 @@
/**
* identifies trusted users
*/
managerDatastoreUser
.search_user_datastore(item.share_right_create_user_id, item.share_right_create_user_username)
.then(function (user) {
if (user !== null) {
$scope.user_is_trusted = true;
$scope.user = user;
return;
}
var onSuccess = function (data) {
$scope.user = {
data: {
user_search_username: data.data.username,
user_id: data.data.id,
user_username: data.data.username,
user_public_key: data.data.public_key
},
name: data.data.username
if (!hide_user) {
managerDatastoreUser
.search_user_datastore(item.share_right_create_user_id, item.share_right_create_user_username)
.then(function (user) {
if (user !== null) {
$scope.user_is_trusted = true;
$scope.user = user;
return;
}
var onSuccess = function (data) {
$scope.user = {
data: {
user_search_username: data.data.username,
user_id: data.data.id,
user_username: data.data.username,
user_public_key: data.data.public_key
},
name: data.data.username
};
$scope.user_list = [
{name: 'user_search_username', value: data.data.username},
{name: 'user_id', value: data.data.id},
{name: 'user_username', value: data.data.username},
{name: 'user_public_key', value: data.data.public_key}
]
};
$scope.user_list = [
{name: 'user_search_username', value: data.data.username},
{name: 'user_id', value: data.data.id},
{name: 'user_username', value: data.data.username},
{name: 'user_public_key', value: data.data.public_key}
]
};
var onError = function (data) {
//pass
};
managerDatastoreUser.search_user(item.share_right_create_user_username)
.then(onSuccess, onError);
});
var onError = function (data) {
//pass
};
managerDatastoreUser.search_user(item.share_right_create_user_username)
.then(onSuccess, onError);
});
}
}
/**
......
......@@ -9,6 +9,7 @@
* @requires $uibModalInstance
* @requires psonocli.managerGroups
* @requires psonocli.managerDatastoreUser
* @requires psonocli.managerShare
* @requires psonocli.helper
* @requires psonocli.account
*
......@@ -16,9 +17,9 @@
* Controller for the "Edit Group" modal
*/
angular.module('psonocli').controller('ModalEditGroupCtrl', ['$scope', '$uibModal', '$uibModalInstance',
'managerGroups', 'managerDatastoreUser', 'shareBlueprint', 'cryptoLibrary', 'helper', 'account', 'group_id',
'managerGroups', 'managerDatastoreUser', 'managerShare', 'shareBlueprint', 'cryptoLibrary', 'helper', 'account', 'group_id',
function ($scope, $uibModal, $uibModalInstance,
managerGroups, managerDatastoreUser, shareBlueprint, cryptoLibrary, helper, account, group_id) {
managerGroups, managerDatastoreUser, managerShare, shareBlueprint, cryptoLibrary, helper, account, group_id) {
var i;
var original_group_name;
var group;
......@@ -30,6 +31,8 @@
$scope.cancel = cancel;
$scope.toggle_user = toggle_user;
$scope.toggle_group_admin = toggle_group_admin;
$scope.toggle_share_admin = toggle_share_admin;
$scope.delete_share_right = delete_share_right;
$scope.users = [];
......@@ -44,9 +47,11 @@
};
var onSuccess = function(group_details) {
console.log(group_details);
var user_id = account.get_account_detail('user_id');
group = group_details;
$scope.group = group_details;
$scope.group_name = group_details.name;
original_group_name = group_details.name;
......@@ -241,6 +246,7 @@
var onSuccess = function(result) {
user['membership_id'] = result.membership_id;
user['group_admin'] = false;
user['share_admin'] = true;
user['is_current_user'] = false;
_group_member_index[user['id']] = user
......@@ -270,6 +276,7 @@
// pass
delete user['membership_id'];
delete user['group_admin'];
delete user['share_admin'];
delete _group_member_index[user['id']];
};
......@@ -313,7 +320,54 @@
user['group_admin'] = !user['group_admin'];
};
managerGroups.update_membership(user['membership_id'], !user['group_admin']).then(onSuccess, onError)
managerGroups.update_membership(user['membership_id'], !user['group_admin'], user['share_admin']).then(onSuccess, onError)
}
/**
* @ngdoc
* @name psonocli.controller:ModalEditGroupCtrl#toggle_share_admin
* @methodOf psonocli.controller:ModalEditGroupCtrl
*
* @description
* responsible to toggle the share admin right of a user
*/
function toggle_share_admin(user) {
var onError = function(result) {
// pass
console.log(result);
};
var onSuccess = function(result) {
user['share_admin'] = !user['share_admin'];
};
managerGroups.update_membership(user['membership_id'], user['group_admin'], !user['share_admin']).then(onSuccess, onError);
}
/**
* @ngdoc
* @name psonocli.controller:ModalEditGroupCtrl#delete_share_right
* @methodOf psonocli.controller:ModalEditGroupCtrl
*
* @description
* Deletes a share right
*
* @param share_right
*/
function delete_share_right(share_right) {
var onError = function(result) {
// pass
console.log(result);
};
var onSuccess = function(result) {
helper.remove_from_array($scope.shares, share_right, function (a,b) {
return a.id === b.id;
});
};
managerShare.delete_share_right(undefined, share_right.id).then(onSuccess, onError);
}
}]);
......
(function(angular) {
'use strict';
/**
* @ngdoc controller
* @name psonocli.controller:ModalPickUserCtrl
* @requires $scope
* @requires $uibModalInstance
* @requires psonocli.helper
*
* @description
* Controller for the "Pick User" modal
*/
angular.module('psonocli').controller('ModalPickUserCtrl', ['$scope', '$uibModalInstance', 'helper','data',
function ($scope, $uibModalInstance, helper, data) {
$scope.errors = [];
$scope.save = save;
$scope.cancel = cancel;
$scope.data = data;
/**
* @ngdoc
* @name psonocli.controller:ModalPickUserCtrl#save
* @methodOf psonocli.controller:ModalPickUserCtrl
*
* @description
* Triggered once someone clicks the save button in the modal
*/
function save(user) {
$uibModalInstance.close(user);
}
/**
* @ngdoc
* @name psonocli.controller:ModalPickUserCtrl#cancel
* @methodOf psonocli.controller:ModalPickUserCtrl
*
* @description
* Triggered once someone clicks the cancel button in the modal
*/
function cancel() {
$uibModalInstance.dismiss('cancel');
}
}]);
}(angular));
......@@ -77,6 +77,9 @@
managerGroups.read_groups(true)
.then(function (groups) {
helper.remove_from_array(groups, '', function(a, b){
return !a.share_admin;
});
$scope.groups = groups;
});
}
......
......@@ -542,9 +542,7 @@
* @returns {promise} promise
*/
var read_datastore = function (token, session_secret_key, datastore_id) {
if (datastore_id === undefined) { datastore_id = null; }
var endpoint = '/datastore/' + (datastore_id === null ? '' : datastore_id + '/');
var endpoint = '/datastore/' + ( !datastore_id ? '' : datastore_id + '/');
var connection_type = "GET";
var data = null;
var headers = {
......@@ -1211,16 +1209,18 @@
* @param {string} token authentication token of the user, returned by authentication_login(email, authkey)
* @param {string} session_secret_key The session secret key
* @param {uuid|undefined} [user_id] (optional) the user ID
* @param {email|undefined} [user_username] (optional) the username
* @param {str|undefined} [user_username] (optional) the username
* @param {email|undefined} [user_email] (optional) the email
*
* @returns {promise} Returns a promise with the user information
*/
var search_user = function (token, session_secret_key, user_id, user_username) {
var search_user = function (token, session_secret_key, user_id, user_username, user_email) {
var endpoint = '/user/search/';
var connection_type = "POST";
var data = {
user_id: user_id,
user_username: user_username
user_username: user_username,
user_email: user_email,
};
var headers = {
"Authorization": "Token "+ token
......@@ -1680,9 +1680,7 @@
* @returns {promise} promise
*/
var read_group = function (token, session_secret_key, group_id) {
if (group_id === undefined) { group_id = null; }
var endpoint = '/group/' + (group_id === null ? '' : group_id + '/');
var endpoint = '/group/' + ( !group_id ? '' : group_id + '/');
var connection_type = "GET";
var data = null;
var headers = {
......@@ -1806,9 +1804,7 @@
* @returns {promise} promise
*/
var read_group_rights = function (token, session_secret_key, group_id) {
if (group_id === undefined) { group_id = null; }
var endpoint = '/group/rights/' + (group_id === null ? '' : group_id + '/');
var endpoint = '/group/rights/' + ( !group_id ? '' : group_id + '/');
var connection_type = "GET";
var data = null;
var headers = {
......@@ -1838,12 +1834,13 @@
* @param {string} private_key_nonce nonce for private key
* @param {string} private_key_type type of the private key
* @param {boolean} group_admin Weather the users should have group admin rights or not
* @param {boolean} share_admin Weather the users should have share admin rights or not
*
* @returns {promise} promise
*/
var create_membership = function (token, session_secret_key, group_id, user_id, secret_key, secret_key_nonce,
secret_key_type, private_key,
private_key_nonce, private_key_type, group_admin) {
private_key_nonce, private_key_type, group_admin, share_admin) {
var endpoint = '/membership/';
var connection_type = "PUT";
var data = {
......@@ -1855,7 +1852,8 @@
private_key: private_key,
private_key_nonce: private_key_nonce,
private_key_type: private_key_type,
group_admin: group_admin
group_admin: group_admin,
share_admin: share_admin
};
var headers = {
"Authorization": "Token "+ token
......@@ -1877,15 +1875,17 @@
* @param {string} session_secret_key The session secret key
* @param {uuid} membership_id The membership id to update
* @param {boolean} group_admin Weather the users should have group admin rights or not
* @param {boolean} share_admin Weather the users should have share admin rights or not
*
* @returns {promise} promise
*/
var update_membership = function (token, session_secret_key, membership_id, group_admin) {
var update_membership = function (token, session_secret_key, membership_id, group_admin, share_admin) {
var endpoint = '/membership/';
var connection_type = "POST";
var data = {
membership_id: membership_id,
group_admin: group_admin
group_admin: group_admin,
share_admin: share_admin
};
var headers = {
"Authorization": "Token "+ token
......
......@@ -466,7 +466,11 @@
storage.upsert('persistent', {key: 'trust_device', value: trust_device});
if (!server_info.hasOwnProperty('allowed_second_factors')) {
server_info['allowed_second_factors'] = ['yubikey_otp', 'google_authenticator', 'duo']
server_info['allowed_second_factors'] = ['yubikey_otp', 'google_authenticator', 'duo'];
}
if (!server_info.hasOwnProperty('allow_user_search_by_email')) {
server_info['allow_user_search_by_email'] = false;
}
storage.upsert('config', {key: 'server_info', value: server_info});
......@@ -843,12 +847,13 @@
* @description
* searches a user in the database according to his username
*
* @param {string} username The username to search
* @param {string} username (optional) The username to search
* @param {string} email (optional) The email to search
* @returns {promise} Returns a promise with the user information
*/
var search_user = function(username) {
var search_user = function(username, email) {
return apiClient.search_user(managerBase.get_token(), managerBase.get_session_secret_key(), undefined, username);
return apiClient.search_user(managerBase.get_token(), managerBase.get_session_secret_key(), undefined, username, email);
};
/**
......
......@@ -339,6 +339,7 @@
var get_outstanding_group_shares = function() {
var onSuccess = function(data){
console.log(data);
var inaccessible_share_list = managerDatastorePassword.get_inaccessible_shares(data.group_rights);
var inaccessible_share_by_group_dict = {};
......@@ -370,15 +371,17 @@
* @methodOf psonocli.managerGroups
*
* @description
* Creates a new group membership
* Creates a new group membership. Encrypts the group secrets (secret and private key) asymmetric with the the
* groups private key and the users public key and sends everything to the server.
*
* @param {object} user The user for the new membership
* @param {object} group The group for the new membership
* @param {boolean} group_admin If the new group member should get group admin rights or not
* @param {boolean} share_admin If the new group member should get share admin rights or not
*
* @returns {promise} Returns whether the creation was successful or not
*/
var create_membership = function(user, group, group_admin) {
var create_membership = function(user, group, group_admin, share_admin) {
var onSuccess = function(data){
return data.data;
......@@ -401,7 +404,7 @@
return apiClient.create_membership(managerBase.get_token(), managerBase.get_session_secret_key(), group.group_id,
user.id, group_secret_key_enc.text, group_secret_key_enc.nonce, 'asymmetric', group_private_key_enc.text,
group_private_key_enc.nonce, 'asymmetric', group_admin)
group_private_key_enc.nonce, 'asymmetric', group_admin, share_admin)
.then(onSuccess, onError);
};
......@@ -414,11 +417,12 @@
* Updates a group membership
*
* @param {uuid} membership_id The membership_id to delete
* @param {boolean} group_admin If the new group member should get group admin rights or not
* @param {boolean} group_admin If the group member should get group admin rights or not
* @param {boolean} share_admin If the group member should get share admin rights or not
*
* @returns {promise} Returns whether the deletion was successful or not
*/
var update_membership = function(membership_id, group_admin) {
var update_membership = function(membership_id, group_admin, share_admin) {
var onSuccess = function(data){
return data.data;
......@@ -428,7 +432,7 @@
//pass
};
return apiClient.update_membership(managerBase.get_token(), managerBase.get_session_secret_key(), membership_id, group_admin)
return apiClient.update_membership(managerBase.get_token(), managerBase.get_session_secret_key(), membership_id, group_admin, share_admin)
.then(onSuccess, onError);
};
......
......@@ -5,7 +5,9 @@
* @ngdoc service
* @name psonocli.shareBlueprint
* @requires $window
* @requires $uibModal
* @requires psonocli.helper
* @requires psonocli.storage
*
* @description
* Service that provides the possible sharing partner blueprints, currently only "users".
......@@ -14,9 +16,13 @@
*/
var shareBlueprint = function($window, helper) {
var shareBlueprint = function($window, $uibModal, helper, storage) {
var _default = "user";
var email_regexp = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i
var allow_user_search_by_email;
var allow_user_search_by_username_partial;
var registrations = {};
......@@ -27,7 +33,6 @@
title_field: "user_username",
search: ['user_name', 'user_username'],
fields: [
{ name: "user_search_username", field: "input", type: "text", title: "Username", placeholder: "Username", onChange: "onChangeSearchUsername", usernameInputGroupAddon: true },
{ name: "user_search_button", field: "button", type: "button", title: "Search", hidden: true, class: 'btn-primary', onClick:"onClickSearchButton" },
{ name: "user_name", field: "input", type: "text", title: "Name", placeholder: "Name (optional)", hidden: true},
{ name: "user_id", field: "input", type: "text", title: "ID", placeholder: "ID", required: true, hidden: true, readonly: true },
......@@ -59,8 +64,7 @@
* @param fields
* @param selected_server_domain
*/
onChangeSearchUsername: function(fields, selected_server_domain){
onChangeSearch: function(fields, selected_server_domain){
var has_search_username = false;
var possible_username = '';
......@@ -70,15 +74,25 @@
if (fields[i].name === "user_search_username") {
if (fields[i].value && fields[i].value.length > 0) {
possible_username = helper.form_full_username(fields[i].value, selected_server_domain);
if (allow_user_search_by_username_partial && fields[i].value.length > 2) {
has_search_username = true;
} else if (!allow_user_search_by_username_partial) {
possible_username = helper.form_full_username(fields[i].value, selected_server_domain);
// Regex obtained from Angular JS
if (email_regexp.test(possible_username)) {
has_search_username = true;
}
}
}
}
if (fields[i].name === "user_search_email") {
if (fields[i].value && fields[i].value.length > 0) {
// Regex obtained from Angular JS
var regexp = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
if (regexp.test(possible_username)) {
if (email_regexp.test(fields[i].value)) {
has_search_username = true;
}
}
break;
}
}
......@@ -101,39 +115,71 @@
onClickSearchButton: function(fields, errors, form_control, selected_server_domain){
var search_username = '';
var search_email = '';
errors.splice(0,errors.length);
var i;
for (i = 0; i < fields.length; i++) {
if (fields[i].name === "user_search_username") {
search_username = fields[i].value;
break;
}
if (fields[i].name === "user_search_email") {
search_email = fields[i].value;
}
}
if (!allow_user_search_by_username_partial) {
search_username = helper.form_full_username(search_username, selected_server_domain);
}
search_username = helper.form_full_username(search_username, selected_server_domain);
var onSuccess = function(data) {
data = data.data;
var show_user = function(id, username, public_key) {
for (i = 0; i < fields.length; i++) {
if (fields[i].name === "user_name") {
fields[i].hidden = false;
}
if (fields[i].name === "user_id") {
fields[i].value = data.id;
fields[i].value = id;
}
if (fields[i].name === "user_username") {
fields[i].value = data.username;
fields[i].value = username;
fields[i].hidden = false;
}
if (fields[i].name === "user_public_key") {
fields[i].value = data.public_key;
fields[i].value = public_key;
fields[i].hidden = false;