1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-28 12:45:45 +01:00

attachments for org logins

This commit is contained in:
Kyle Spearrin 2017-07-07 15:43:24 -04:00
parent 76da9b1f18
commit abed4df973
13 changed files with 218 additions and 28 deletions

View File

@ -1,10 +1,19 @@
angular angular
.module('bit.global') .module('bit.global')
.controller('paidOrgRequiredController', function ($scope, $state, $uibModalInstance, $analytics, $uibModalStack, orgId) { .controller('paidOrgRequiredController', function ($scope, $state, $uibModalInstance, $analytics, $uibModalStack, orgId,
constants, authService) {
$analytics.eventTrack('paidOrgRequiredController', { category: 'Modal' }); $analytics.eventTrack('paidOrgRequiredController', { category: 'Modal' });
authService.getUserProfile().then(function (profile) {
$scope.admin = profile.organizations[orgId].type !== constants.orgUserType.user
});
$scope.go = function () { $scope.go = function () {
if (!$scope.admin) {
return;
}
$analytics.eventTrack('Get Paid Org'); $analytics.eventTrack('Get Paid Org');
$state.go('backend.org.billing', { orgId: orgId }).then(function () { $state.go('backend.org.billing', { orgId: orgId }).then(function () {
$uibModalStack.dismissAll(); $uibModalStack.dismissAll();

View File

@ -1,7 +1,7 @@
angular angular
.module('bit.global') .module('bit.global')
.controller('sideNavController', function ($scope, $state, authService, toastr, $analytics) { .controller('sideNavController', function ($scope, $state, authService, toastr, $analytics, constants) {
$scope.$state = $state; $scope.$state = $state;
$scope.params = $state.params; $scope.params = $state.params;
$scope.orgs = []; $scope.orgs = [];
@ -31,7 +31,7 @@ angular
}); });
$scope.viewOrganization = function (org) { $scope.viewOrganization = function (org) {
if (org.type === 2) { // 2 = User if (org.type === constants.orgUserType.user) {
toastr.error('You cannot manage this organization.'); toastr.error('You cannot manage this organization.');
return; return;
} }
@ -49,6 +49,6 @@ angular
}; };
$scope.isOrgOwner = function (org) { $scope.isOrgOwner = function (org) {
return org && org.type === 0; return org && org.type === constants.orgUserType.owner;
}; };
}); });

View File

@ -1,5 +1,5 @@
angular angular
.module('bit.vault') .module('bit.organization')
.controller('organizationVaultAddLoginController', function ($scope, apiService, $uibModalInstance, cryptoService, .controller('organizationVaultAddLoginController', function ($scope, apiService, $uibModalInstance, cryptoService,
cipherService, passwordService, $analytics, authService, orgId, $uibModal) { cipherService, passwordService, $analytics, authService, orgId, $uibModal) {

View File

@ -0,0 +1,127 @@
angular
.module('bit.organization')
.controller('organizationVaultAttachmentsController', function ($scope, apiService, $uibModalInstance, cryptoService,
cipherService, loginId, $analytics, validationService, toastr) {
$analytics.eventTrack('organizationVaultAttachmentsController', { category: 'Modal' });
$scope.login = {};
$scope.loading = true;
var closing = false;
apiService.logins.getAdmin({ id: loginId }, function (login) {
$scope.login = cipherService.decryptLogin(login);
$scope.loading = false;
}, function () {
$scope.loading = false;
});
$scope.save = function (form) {
var files = document.getElementById('file').files;
if (!files || !files.length) {
validationService.addError(form, 'file', 'Select a file.', true);
return;
}
var file = files[0];
if (file.size > 104857600) { // 100 MB
validationService.addError(form, 'file', 'Maximum file size is 100 MB.', true);
return;
}
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function (evt) {
form.$loading = true;
$scope.$apply();
var key = cryptoService.getOrgKey($scope.login.organizationId);
var encFilename = cryptoService.encrypt(file.name, key);
$scope.savePromise = cryptoService.encryptToBytes(evt.target.result, key).then(function (encData) {
var fd = new FormData();
var blob = new Blob([encData], { type: 'application/octet-stream' });
fd.append('data', blob, encFilename);
return apiService.ciphers.postAttachment({ id: loginId }, fd).$promise;
}).then(function (response) {
$analytics.eventTrack('Added Organization Attachment');
toastr.success('The attachment has been added.');
closing = true;
$uibModalInstance.close(true);
});
};
reader.onerror = function (evt) {
validationService.addError(form, 'file', 'Error reading file.', true);
};
}
$scope.download = function (attachment) {
attachment.loading = true;
var req = new XMLHttpRequest();
req.open('GET', attachment.url, true);
req.responseType = 'arraybuffer';
req.onload = function (evt) {
if (!req.response) {
attachment.loading = false;
$scope.$apply();
// error
return;
}
var key = cryptoService.getOrgKey($scope.login.organizationId);
cryptoService.decryptFromBytes(req.response, key).then(function (decBuf) {
var blob = new Blob([decBuf]);
// IE hack. ref http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, attachment.fileName);
}
else {
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = attachment.fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
attachment.loading = false;
$scope.$apply();
});
};
req.send(null);
};
$scope.remove = function (attachment) {
if (!confirm('Are you sure you want to delete this attachment (' + attachment.fileName + ')?')) {
return;
}
attachment.loading = true;
apiService.ciphers.delAttachment({ id: loginId, attachmentId: attachment.id }).$promise.then(function () {
attachment.loading = false;
$analytics.eventTrack('Deleted Organization Attachment');
var index = $scope.login.attachments.indexOf(attachment);
if (index > -1) {
$scope.login.attachments.splice(index, 1);
}
}, function () {
toastr.error('Cannot delete attachment.');
attachment.loading = false;
});
};
$scope.close = function () {
$uibModalInstance.dismiss('cancel');
};
$scope.$on('modal.closing', function (e, reason, closed) {
if (closing) {
return;
}
e.preventDefault();
closing = true;
$uibModalInstance.close(!!$scope.login.attachments && $scope.login.attachments.length > 0);
});
});

View File

@ -2,7 +2,7 @@
.module('bit.organization') .module('bit.organization')
.controller('organizationVaultController', function ($scope, apiService, cipherService, $analytics, $q, $state, .controller('organizationVaultController', function ($scope, apiService, cipherService, $analytics, $q, $state,
$localStorage, $uibModal, $filter) { $localStorage, $uibModal, $filter, authService) {
$scope.logins = []; $scope.logins = [];
$scope.collections = []; $scope.collections = [];
$scope.loading = true; $scope.loading = true;
@ -139,6 +139,37 @@
}); });
}; };
$scope.attachments = function (login) {
authService.getUserProfile().then(function (profile) {
return !!profile.organizations[login.organizationId].maxStorageGb;
}).then(function (useStorage) {
if (!useStorage) {
$uibModal.open({
animation: true,
templateUrl: 'app/views/paidOrgRequired.html',
controller: 'paidOrgRequiredController',
resolve: {
orgId: function () { return login.organizationId; }
}
});
return;
}
var attachmentModel = $uibModal.open({
animation: true,
templateUrl: 'app/vault/views/vaultAttachments.html',
controller: 'organizationVaultAttachmentsController',
resolve: {
loginId: function () { return login.id; }
}
});
attachmentModel.result.then(function (hasAttachments) {
login.hasAttachments = hasAttachments;
});
});
};
$scope.removeLogin = function (login, collection) { $scope.removeLogin = function (login, collection) {
if (!confirm('Are you sure you want to remove this login (' + login.name + ') from the ' + if (!confirm('Are you sure you want to remove this login (' + login.name + ') from the ' +
'collection (' + collection.name + ') ?')) { 'collection (' + collection.name + ') ?')) {

View File

@ -1,5 +1,5 @@
angular angular
.module('bit.vault') .module('bit.organization')
.controller('organizationVaultEditLoginController', function ($scope, apiService, $uibModalInstance, cryptoService, .controller('organizationVaultEditLoginController', function ($scope, apiService, $uibModalInstance, cryptoService,
cipherService, passwordService, loginId, $analytics, orgId, $uibModal) { cipherService, passwordService, loginId, $analytics, orgId, $uibModal) {

View File

@ -46,6 +46,11 @@
<i class="fa fa-fw fa-pencil"></i> Edit <i class="fa fa-fw fa-pencil"></i> Edit
</a> </a>
</li> </li>
<li>
<a href="#" stop-click ng-click="attachments(login)">
<i class="fa fa-fw fa-paperclip"></i> Attachments
</a>
</li>
<li> <li>
<a href="#" stop-click ng-click="editCollections(login)"> <a href="#" stop-click ng-click="editCollections(login)">
<i class="fa fa-fw fa-cubes"></i> Collections <i class="fa fa-fw fa-cubes"></i> Collections

View File

@ -5,7 +5,7 @@
loginId, $analytics, validationService, toastr) { loginId, $analytics, validationService, toastr) {
$analytics.eventTrack('vaultAttachmentsController', { category: 'Modal' }); $analytics.eventTrack('vaultAttachmentsController', { category: 'Modal' });
$scope.login = {}; $scope.login = {};
$scope.readOnly = false; $scope.readOnly = true;
$scope.loading = true; $scope.loading = true;
var closing = false; var closing = false;

View File

@ -201,9 +201,24 @@
$scope.attachments = function (login) { $scope.attachments = function (login) {
authService.getUserProfile().then(function (profile) { authService.getUserProfile().then(function (profile) {
return profile.premium; return {
}).then(function (isPremium) { isPremium: profile.premium,
if (!isPremium) { orgUseStorage: login.organizationId && !!profile.organizations[login.organizationId].maxStorageGb
};
}).then(function (perms) {
if (login.organizationId && !perms.orgUseStorage) {
$uibModal.open({
animation: true,
templateUrl: 'app/views/paidOrgRequired.html',
controller: 'paidOrgRequiredController',
resolve: {
orgId: function () { return login.organizationId; }
}
});
return;
}
if (!login.organizationId && !perms.isPremium) {
$uibModal.open({ $uibModal.open({
animation: true, animation: true,
templateUrl: 'app/views/premiumRequired.html', templateUrl: 'app/views/premiumRequired.html',
@ -212,7 +227,7 @@
return; return;
} }
if (!cryptoService.getEncKey()) { if (!login.organizationId && !cryptoService.getEncKey()) {
toastr.error('You cannot use this feature until you update your encryption key.', 'Feature Unavailable'); toastr.error('You cannot use this feature until you update your encryption key.', 'Feature Unavailable');
return; return;
} }

View File

@ -86,7 +86,7 @@
<i class="fa fa-fw fa-pencil"></i> Edit <i class="fa fa-fw fa-pencil"></i> Edit
</a> </a>
</li> </li>
<li ng-show="login.edit"> <li>
<a href="#" stop-click ng-click="attachments(login)"> <a href="#" stop-click ng-click="attachments(login)">
<i class="fa fa-fw fa-paperclip"></i> Attachments <i class="fa fa-fw fa-paperclip"></i> Attachments
</a> </a>
@ -196,7 +196,7 @@
<i class="fa fa-fw fa-pencil"></i> Edit <i class="fa fa-fw fa-pencil"></i> Edit
</a> </a>
</li> </li>
<li ng-show="login.edit"> <li>
<a href="#" stop-click ng-click="attachments(login)"> <a href="#" stop-click ng-click="attachments(login)">
<i class="fa fa-fw fa-paperclip"></i> Attachments <i class="fa fa-fw fa-paperclip"></i> Attachments
</a> </a>

View File

@ -45,22 +45,24 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<hr /> <div ng-if="!readOnly">
<h4>Add New Attachment</h4> <hr />
<div class="callout callout-danger validation-errors" ng-show="form.$errors"> <h4>Add New Attachment</h4>
<h4>Errors have occurred</h4> <div class="callout callout-danger validation-errors" ng-show="form.$errors">
<ul> <h4>Errors have occurred</h4>
<li ng-repeat="e in form.$errors">{{e}}</li> <ul>
</ul> <li ng-repeat="e in form.$errors">{{e}}</li>
</div> </ul>
<div class="form-group" show-error> </div>
<label for="file" class="sr-only">File</label> <div class="form-group" show-error>
<input type="file" id="file" name="file" /> <label for="file" class="sr-only">File</label>
<p class="help-block">Maximum size per file is 100 MB.</p> <input type="file" id="file" name="file" />
<p class="help-block">Maximum size per file is 100 MB.</p>
</div>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading"> <button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading" ng-if="!readOnly">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Save <i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Save
</button> </button>
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button> <button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>

View File

@ -6,7 +6,7 @@
This feature is not available for free organizations. Switch to a paid plan to unlock more features. This feature is not available for free organizations. Switch to a paid plan to unlock more features.
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-click="go()"> <button type="submit" class="btn btn-primary btn-flat" ng-click="go()" ng-if="admin">
Upgrade Organization Upgrade Organization
</button> </button>
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button> <button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>

View File

@ -189,6 +189,7 @@
<script src="app/organization/organizationVaultAddLoginController.js"></script> <script src="app/organization/organizationVaultAddLoginController.js"></script>
<script src="app/organization/organizationVaultEditLoginController.js"></script> <script src="app/organization/organizationVaultEditLoginController.js"></script>
<script src="app/organization/organizationVaultLoginCollectionsController.js"></script> <script src="app/organization/organizationVaultLoginCollectionsController.js"></script>
<script src="app/organization/organizationVaultAttachmentsController.js"></script>
<script src="app/organization/organizationGroupsController.js"></script> <script src="app/organization/organizationGroupsController.js"></script>
<script src="app/organization/organizationGroupsAddController.js"></script> <script src="app/organization/organizationGroupsAddController.js"></script>
<script src="app/organization/organizationGroupsEditController.js"></script> <script src="app/organization/organizationGroupsEditController.js"></script>