Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/passman.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbrantje <brantje@gmail.com>2016-09-11 20:45:20 +0300
committerbrantje <brantje@gmail.com>2016-09-11 20:45:20 +0300
commit67734860595060e40633d555dbc879da66ec2879 (patch)
treeae44657a34bf7d33155f931116f07606a1393360
parent9c49cc3e1c572e46b5b346fdd4b42bbdd761d249 (diff)
Starting to work on login to vault
-rw-r--r--appinfo/database.xml157
-rw-r--r--appinfo/info.xml2
-rw-r--r--appinfo/routes.php26
-rw-r--r--controller/credentialcontroller.php51
-rw-r--r--controller/vaultcontroller.php3
-rw-r--r--css/app.css4
-rw-r--r--css/app.css.map2
-rw-r--r--js/app/controllers/vault.js53
-rw-r--r--js/app/services/credentialservice.js65
-rw-r--r--js/app/services/encryptservice.js44
-rw-r--r--js/app/services/vaultservice.js17
-rw-r--r--js/script.js0
-rw-r--r--js/templates.js2
-rw-r--r--js/vendor/sjcl/sjcl.js41
-rw-r--r--lib/Db/Credential.php121
-rw-r--r--lib/Db/CredentialMapper.php68
-rw-r--r--lib/Db/Vault.php13
-rw-r--r--lib/Service/CredentialService.php31
-rw-r--r--sass/vaults.scss6
-rw-r--r--templates/main.php2
-rw-r--r--templates/views/vaults.html16
21 files changed, 652 insertions, 72 deletions
diff --git a/appinfo/database.xml b/appinfo/database.xml
index 29b75449..fce99ff8 100644
--- a/appinfo/database.xml
+++ b/appinfo/database.xml
@@ -55,7 +55,7 @@
</field>
<index>
- <name>passman_last_access_index</name>
+ <name>passman_vault_last_access_index</name>
<field>
<name>last_access</name>
</field>
@@ -83,4 +83,159 @@
</index>
</declaration>
</table>
+ <table>
+ <name>*dbprefix*passman_credentials</name>
+ <declaration>
+ <field>
+ <name>id</name>
+ <type>integer</type>
+ <notnull>true</notnull>
+ <autoincrement>true</autoincrement>
+ <unsigned>true</unsigned>
+ <primary>true</primary>
+ <length>8</length>
+ </field>
+ <field>
+ <name>guid</name>
+ <type>text</type>
+ <notnull>false</notnull>
+ <length>64</length>
+ </field>
+
+ <field>
+ <name>user_id</name>
+ <type>text</type>
+ <notnull>false</notnull>
+ <length>64</length>
+ </field>
+ <field>
+ <name>vault_id</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+
+ <field>
+ <name>label</name>
+ <type>clob</type>
+ <notnull>true</notnull>
+ </field>
+
+ <field>
+ <name>description</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+
+ <field>
+ <name>created</name>
+ <type>integer</type>
+ <length>8</length>
+ <default>0</default>
+ <notnull>false</notnull>
+ <unsigned>true</unsigned>
+ </field>
+ <field>
+ <name>changed</name>
+ <type>integer</type>
+ <default>0</default>
+ <length>8</length>
+ <notnull>false</notnull>
+ <unsigned>true</unsigned>
+ </field>
+ <field>
+ <name>tags</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+ <field>
+ <name>email</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+ <field>
+ <name>username</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+ <field>
+ <name>password</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+ <field>
+ <name>url</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+ <field>
+ <name>favicon</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+ <field>
+ <name>renew_interval</name>
+ <type>integer</type>
+ <default>0</default>
+ <length>8</length>
+ <notnull>false</notnull>
+ <unsigned>true</unsigned>
+ </field>
+ <field>
+ <name>expire_time</name>
+ <type>integer</type>
+ <default>0</default>
+ <length>8</length>
+ <notnull>false</notnull>
+ <unsigned>true</unsigned>
+ </field>
+ <field>
+ <name>delete_time</name>
+ <type>integer</type>
+ <default>0</default>
+ <length>8</length>
+ <notnull>false</notnull>
+ <unsigned>true</unsigned>
+ </field>
+ <field>
+ <name>files</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+
+ <field>
+ <name>custom_fields</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+ <field>
+ <name>otp</name>
+ <type>clob</type>
+ <notnull>false</notnull>
+ </field>
+ <field>
+ <name>hidden</name>
+ <type>boolean</type>
+ <default>false</default>
+ </field>
+ <index>
+ <name>passman_credential_id_index</name>
+ <field>
+ <name>id</name>
+ </field>
+ </index>
+ <index>
+ <name>passman_credential_vault_id_index</name>
+ <field>
+ <name>vault_id</name>
+ </field>
+ </index>
+ <index>
+ <name>passman_credential_user_id_index</name>
+ <field>
+ <name>user_id</name>
+ </field>
+ </index>
+ </declaration>
+ </table>
</database> \ No newline at end of file
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 39d70b08..8e5ebd2c 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -5,7 +5,7 @@
<description>A password manager for Nextcloud</description>
<licence>AGPL</licence>
<author>Sander Brand</author>
- <version>1.0.2.1</version>
+ <version>1.0.2.2</version>
<namespace>Passman</namespace>
<category>other</category>
<website>https://github.com/nextcloud/passman/</website>
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 3e7df09a..e5c2abd4 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -22,25 +22,25 @@ return [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
//Vault
- ['name' => 'vault#listVaults', 'url' => '/api/v1/vaults', 'verb' => 'GET'],
- ['name' => 'vault#create', 'url' => '/api/v1/vaults', 'verb' => 'POST'],
- ['name' => 'vault#get', 'url' => '/api/v1/vaults/{vault_id}', 'verb' => 'GET'],
- ['name' => 'vault#update', 'url' => '/api/v1/vaults/{vault_id}', 'verb' => 'PATCH'],
- ['name' => 'vault#delete', 'url' => '/api/v1/vaults/{vault_id}', 'verb' => 'DELETE'],
+ ['name' => 'vault#listVaults', 'url' => '/api/v2/vaults', 'verb' => 'GET'],
+ ['name' => 'vault#create', 'url' => '/api/v2/vaults', 'verb' => 'POST'],
+ ['name' => 'vault#get', 'url' => '/api/v2/vaults/{vault_id}', 'verb' => 'GET'],
+ ['name' => 'vault#update', 'url' => '/api/v2/vaults/{vault_id}', 'verb' => 'PATCH'],
+ ['name' => 'vault#delete', 'url' => '/api/v2/vaults/{vault_id}', 'verb' => 'DELETE'],
//Credential
- ['name' => 'credential#createCredential', 'url' => '/api/v1/credentials', 'verb' => 'POST'],
- ['name' => 'credential#getCredential', 'url' => '/api/v1/credentials/{credential_id}', 'verb' => 'GET'],
- ['name' => 'credential#updateCredential', 'url' => '/api/v1/credentials/{credential_id}', 'verb' => 'PATCH'],
- ['name' => 'credential#deleteCredential', 'url' => '/api/v1/credentials/{credential_id}', 'verb' => 'DELETE'],
+ ['name' => 'credential#createCredential', 'url' => '/api/v2/credentials', 'verb' => 'POST'],
+ ['name' => 'credential#getCredential', 'url' => '/api/v2/credentials/{credential_id}', 'verb' => 'GET'],
+ ['name' => 'credential#updateCredential', 'url' => '/api/v2/credentials/{credential_id}', 'verb' => 'PATCH'],
+ ['name' => 'credential#deleteCredential', 'url' => '/api/v2/credentials/{credential_id}', 'verb' => 'DELETE'],
//Revisions
- ['name' => 'credential#getRevision', 'url' => '/api/v1/credentials/{credential_id}/revision', 'verb' => 'GET'],
- ['name' => 'credential#deleteRevision', 'url' => '/api/v1/credentials/{credential_id}/revision/{revision_id}', 'verb' => 'DELETE'],
+ ['name' => 'credential#getRevision', 'url' => '/api/v2/credentials/{credential_id}/revision', 'verb' => 'GET'],
+ ['name' => 'credential#deleteRevision', 'url' => '/api/v2/credentials/{credential_id}/revision/{revision_id}', 'verb' => 'DELETE'],
//File stuff
- ['name' => 'credential#uploadFile', 'url' => '/api/v1/credentials/{credential_id}/file', 'verb' => 'POST'],
- ['name' => 'credential#deleteFile', 'url' => '/api/v1/credentials/{credential_id}/file/{file_id}', 'verb' => 'DELETE'],
+ ['name' => 'credential#uploadFile', 'url' => '/api/v2/credentials/{credential_id}/file', 'verb' => 'POST'],
+ ['name' => 'credential#deleteFile', 'url' => '/api/v2/credentials/{credential_id}/file/{file_id}', 'verb' => 'DELETE'],
]
]; \ No newline at end of file
diff --git a/controller/credentialcontroller.php b/controller/credentialcontroller.php
index bb9ea218..37f5f74c 100644
--- a/controller/credentialcontroller.php
+++ b/controller/credentialcontroller.php
@@ -12,28 +12,57 @@
namespace OCA\Passman\Controller;
use OCP\IRequest;
-use OCA\Passman\Credential;
-
-use OCP\AppFramework\Http\TemplateResponse;
-use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\ApiController;
-use OCP\AppFramework\Controller;
-
-class CredentialController extends Controller {
+use OCA\Passman\Service\CredentialService;
+class CredentialController extends ApiController {
private $userId;
-
- public function __construct($AppName, IRequest $request, $UserId){
+ private $credentialService;
+ public function __construct($AppName,
+ IRequest $request,
+ $UserId,
+ CredentialService $credentialService){
parent::__construct($AppName, $request);
$this->userId = $UserId;
+ $this->credentialService = $credentialService;
}
/**
* @NoAdminRequired
*/
- public function createCredential() {
- return;
+ public function createCredential($changed, $created,
+ $credential_id, $custom_fields, $delete_time,
+ $description, $email, $expire_time, $favicon, $files, $guid,
+ $hidden, $label, $otp, $password, $renew_interval,
+ $tags, $url, $username, $vault_id) {
+ $credential = array(
+ 'credential_id' => $credential_id,
+ 'guid' => $guid,
+ 'user_id' => $this->userId,
+ 'vault_id' => $vault_id,
+ 'label' => $label,
+ 'description' => $description,
+ 'created' => $created,
+ 'changed' => $changed,
+ 'tags' => $tags,
+ 'email' => $email,
+ 'username' => $username,
+ 'password' => $password,
+ 'url' => $url,
+ 'favicon' => $favicon,
+ 'renew_interval' => $renew_interval,
+ 'expire_time' => $expire_time,
+ 'delete_time' => $delete_time,
+ 'files' => $files,
+ 'custom_fields' => $custom_fields,
+ 'otp' => $otp,
+ 'hidden' => $hidden,
+
+ );
+ $credential = $this->credentialService->createCredential($credential);
+ return new JSONResponse($credential);
}
/**
diff --git a/controller/vaultcontroller.php b/controller/vaultcontroller.php
index 61023887..3f2cfc66 100644
--- a/controller/vaultcontroller.php
+++ b/controller/vaultcontroller.php
@@ -12,10 +12,11 @@
namespace OCA\Passman\Controller;
use OCP\IRequest;
-use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\ApiController;
use OCA\Passman\Service\VaultService;
+
+
class VaultController extends ApiController {
private $userId;
private $vaultService;
diff --git a/css/app.css b/css/app.css
index a2755964..8ae5829a 100644
--- a/css/app.css
+++ b/css/app.css
@@ -54,4 +54,8 @@
.vault_wrapper .login_form .button {
margin-top: 10px; }
+@media screen and (max-width: 768px) {
+ .vault_wrapper {
+ width: 90%; } }
+
/*# sourceMappingURL=app.css.map */
diff --git a/css/app.css.map b/css/app.css.map
index 9e7366d8..e8b55edc 100644
--- a/css/app.css.map
+++ b/css/app.css.map
@@ -1,6 +1,6 @@
{
"version": 3,
-"mappings": "AAAA,YAAY;EACV,UAAU,EAAE,OAAO;EACnB,KAAK,EAAE,IAAI;;AAEb,kBAAkB;EAChB,UAAU,EAAE,OAAoB;EAChC,KAAK,EAAE,IAAI;;AAGb,WAAW;EACT,UAAU,EAAE,OAAO;EACnB,KAAK,EAAE,IAAI;;AAEb,iBAAiB;EACf,UAAU,EAAE,OAAoB;EAChC,KAAK,EAAE,IAAI;;ACdb,cAAc;EACZ,MAAM,EAAE,MAAM;EACd,UAAU,EAAE,IAAI;EAChB,KAAK,EAAE,IAAI;EACX,SAAS,EAAE,KAAK;ECMhB,qBAAqB,EDLE,GAAG;ECM1B,aAAa,EDNU,GAAG;ECO1B,eAAe,EAAE,WAAW;EAAG,qDAAqD;EDNpF,UAAU,EAAE,cAAc;EAC1B,UAAU,EAAE,IAAI;EAChB,OAAO,EAAE,KAAK;EACd,yBAAU;IAER,OAAO,EAAE,GAAG;IACZ,4HAAiB;MACf,MAAM,EAAE,OAAO;IAEjB,+BAAK;MACH,KAAK,EAAE,OAAO;IAEhB,qCAAW;MACT,gBAAgB,EAAE,kBAAkB;MACpC,KAAK,EAAE,IAAI;IAEb,4BAAE;MACA,aAAa,EAAE,iBAAiB;MAChC,OAAO,EAAE,IAAI;IAEf,kCAAQ;MACN,gBAAgB,EAAE,OAAO;EAG7B,0BAAW;IACT,OAAO,EAAE,IAAI;IACb,gGAA0C;MACxC,KAAK,EAAE,IAAI;MCvBf,qBAAqB,EDwBM,GAAG;MCvB9B,aAAa,EDuBc,GAAG;MCtB9B,eAAe,EAAE,WAAW;MAAG,qDAAqD;IDyBhF,kDAAO;MACL,KAAK,EAAE,GAAG;MACV,OAAO,EAAE,YAAY;IAGzB,kCAAO;MACL,UAAU,EAAE,IAAI",
+"mappings": "AAAA,YAAY;EACV,UAAU,EAAE,OAAO;EACnB,KAAK,EAAE,IAAI;;AAEb,kBAAkB;EAChB,UAAU,EAAE,OAAoB;EAChC,KAAK,EAAE,IAAI;;AAGb,WAAW;EACT,UAAU,EAAE,OAAO;EACnB,KAAK,EAAE,IAAI;;AAEb,iBAAiB;EACf,UAAU,EAAE,OAAoB;EAChC,KAAK,EAAE,IAAI;;ACdb,cAAc;EACZ,MAAM,EAAE,MAAM;EACd,UAAU,EAAE,IAAI;EAChB,KAAK,EAAE,IAAI;EACX,SAAS,EAAE,KAAK;ECMhB,qBAAqB,EDLE,GAAG;ECM1B,aAAa,EDNU,GAAG;ECO1B,eAAe,EAAE,WAAW;EAAG,qDAAqD;EDNpF,UAAU,EAAE,cAAc;EAC1B,UAAU,EAAE,IAAI;EAChB,OAAO,EAAE,KAAK;EACd,yBAAU;IAER,OAAO,EAAE,GAAG;IACZ,4HAAiB;MACf,MAAM,EAAE,OAAO;IAEjB,+BAAK;MACH,KAAK,EAAE,OAAO;IAEhB,qCAAW;MACT,gBAAgB,EAAE,kBAAkB;MACpC,KAAK,EAAE,IAAI;IAEb,4BAAE;MACA,aAAa,EAAE,iBAAiB;MAChC,OAAO,EAAE,IAAI;IAEf,kCAAQ;MACN,gBAAgB,EAAE,OAAO;EAG7B,0BAAW;IACT,OAAO,EAAE,IAAI;IACb,gGAA0C;MACxC,KAAK,EAAE,IAAI;MCvBf,qBAAqB,EDwBM,GAAG;MCvB9B,aAAa,EDuBc,GAAG;MCtB9B,eAAe,EAAE,WAAW;MAAG,qDAAqD;IDyBhF,kDAAO;MACL,KAAK,EAAE,GAAG;MACV,OAAO,EAAE,YAAY;IAGzB,kCAAO;MACL,UAAU,EAAE,IAAI;;AAKtB,oCAAoC;EAClC,cAAc;IACZ,KAAK,EAAE,GAAG",
"sources": ["../sass/partials/button.scss","../sass/vaults.scss","../sass/mixins.scss"],
"names": [],
"file": "app.css"
diff --git a/js/app/controllers/vault.js b/js/app/controllers/vault.js
index b85cb1f9..a4d63bd1 100644
--- a/js/app/controllers/vault.js
+++ b/js/app/controllers/vault.js
@@ -8,18 +8,24 @@
* Controller of the passmanApp
*/
angular.module('passmanApp')
- .controller('VaultCtrl', ['$scope', 'VaultService', 'SettingsService', function ($scope, VaultService, SettingsService) {
+ .controller('VaultCtrl', ['$scope', 'VaultService', 'SettingsService', 'CredentialService', function ($scope, VaultService, SettingsService, CredentialService) {
VaultService.getVaults().then(function (vaults) {
$scope.vaults = vaults;
if(SettingsService.getSetting('defaultVault') != null){
var default_vault = SettingsService.getSetting('defaultVault');
- angular.forEach(vaults, function (vault) {
+
+ /**
+ * Using a native for loop for preformance reasons.
+ * More info see http://stackoverflow.com/questions/13843972/angular-js-break-foreach
+ */
+ for(var i = 0; i < vaults.length; i++){
+ var vault = vaults[i];
if(vault.guid == default_vault.guid){
$scope.default_vault = true;
$scope.list_selected_vault = SettingsService.getSetting('defaultVault');
- return;
+ break;
}
- })
+ }
}
});
@@ -50,9 +56,42 @@ angular.module('passmanApp')
$scope.creating_vault = true;
};
- $scope.createVault = function(vault_name){
- VaultService.createVault(vault_name).then(function (result) {
- console.log(result)
+
+ $scope.vaultDecryptionKey = '';
+ $scope.loginToVault = function (vault) {
+ VaultService.getVault(vault).then(function(credentials){
+ for(var i = 0; i < credentials.length; i++){
+ var credential = credentials[i];
+ if(credential.hidden = true){
+ console.log(credential);
+ break;
+ }
+ }
})
};
+
+ $scope.vaultKey = '';
+ $scope.vaultKey_2 = '';
+ $scope.createVault = function(vault_name){
+ if($scope.vaultKey != $scope.vaultKey_2){
+ //@todo Show an message
+ return;
+ }
+ VaultService.createVault(vault_name).then(function (vault) {
+ $scope.vaults.push(vault)
+ var _vault = angular.copy(vault)
+ _vault.vaultKey = angular.copy($scope.vaultKey);
+ VaultService.setActiveVault(_vault);
+ var test_credential = CredentialService.newCredential();
+ test_credential.label = 'Test key for vault '+ vault_name;
+ test_credential.hidden = true;
+ test_credential.vault_id = vault.vault_id;
+ test_credential.password = 'lorum ipsum';
+ CredentialService.createCredential(test_credential).then(function (result) {
+ console.log('succes =)')
+ console.log(result)
+ //@TODO Redirect to newly created vault
+ })
+ });
+ };
}]);
diff --git a/js/app/services/credentialservice.js b/js/app/services/credentialservice.js
index abbba1f8..ab0f19f4 100644
--- a/js/app/services/credentialservice.js
+++ b/js/app/services/credentialservice.js
@@ -8,6 +8,65 @@
* Service in the passmanApp.
*/
angular.module('passmanApp')
- .service('CredentialService', function () {
- // AngularJS will instantiate a singleton by calling "new" on this function
- });
+ .service('CredentialService', ['$http', 'EncryptService', function ($http, EncryptService) {
+ var credential = {
+ 'credential_id': null,
+ 'guid': null,
+ 'vault_id': null,
+ 'label': null,
+ 'description': null,
+ 'created': null,
+ 'changed': null,
+ 'tags': null,
+ 'email': null,
+ 'username': null,
+ 'password': null,
+ 'url': null,
+ 'favicon': null,
+ 'renew_interval': null,
+ 'expire_time': null,
+ 'delete_time': null,
+ 'files': null,
+ 'custom_fields': null,
+ 'otp': null,
+ 'hidden': false
+ };
+ var _encryptedFields = ['description','username','password','files','custom_fields','otp'];
+ return {
+ newCredential: function () {
+ return angular.extend({}, credential);
+ },
+ createCredential: function (credential) {
+ for(var i = 0; i < _encryptedFields.length; i++){
+ var field = _encryptedFields[i];
+ var fieldValue = angular.copy(credential[field]);
+ credential[field] = EncryptService.encryptString(JSON.stringify(fieldValue));
+ }
+
+ var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials');
+ return $http.post(queryUrl, credential).then(function (response) {
+ if(response.data){
+ return response.data;
+ } else {
+ return response;
+ }
+ });
+ },
+ updateCredential: function (credential) {
+ for(var i = 0; i < _encryptedFields.length; i++){
+ var field = _encryptedFields[i];
+ var fieldValue = angular.copy(credential[field]);
+ credential[field] = EncryptService.encryptString(JSON.stringify(fieldValue));
+ }
+
+ var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/'+ credential.credential_id);
+ return $http.post(queryUrl, credential).then(function (response) {
+ if(response.data){
+ return response.data;
+ } else {
+ return response;
+ }
+ });
+ }
+ }
+ }]);
diff --git a/js/app/services/encryptservice.js b/js/app/services/encryptservice.js
new file mode 100644
index 00000000..b1e53881
--- /dev/null
+++ b/js/app/services/encryptservice.js
@@ -0,0 +1,44 @@
+'use strict';
+
+/**
+ * @ngdoc service
+ * @name passmanApp.VaultService
+ * @description
+ * # VaultService
+ * Service in the passmanApp.
+ */
+angular.module('passmanApp')
+ .service('EncryptService', ['VaultService', function (VaultService) {
+ // AngularJS will instantiate a singleton by calling "new" on this function
+ var encryption_config = {
+ adata:"",
+ iter: 1000,
+ ks: 256,
+ mode: 'ccm',
+ ts:64
+ };
+
+ return {
+ encryptString: function(string){
+ var _key = VaultService.getActiveVault().vaultKey;
+ var rp = {
+
+ };
+ var ct = sjcl.encrypt(_key, string, encryption_config, rp);
+ return window.btoa(ct);
+ },
+ decryptString: function(ciphertext){
+ ciphertext = window.atob(ciphertext);
+ var _key = VaultService.getActiveVault().vaultKey;
+ var rp = {
+
+ };
+ try {
+ return sjcl.decrypt(_key, ciphertext, encryption_config, rp)
+ } catch(e) {
+ Error("Can't decrypt: "+e);
+ return;
+ }
+ }
+ }
+ }]);
diff --git a/js/app/services/vaultservice.js b/js/app/services/vaultservice.js
index c4d0aecf..dbb3b931 100644
--- a/js/app/services/vaultservice.js
+++ b/js/app/services/vaultservice.js
@@ -10,9 +10,10 @@
angular.module('passmanApp')
.service('VaultService', ['$http', 'CacheService', function ($http, CacheService) {
// AngularJS will instantiate a singleton by calling "new" on this function
+ var _activeVault;
return {
getVaults: function(){
- var queryUrl = OC.generateUrl('apps/passman/api/v1/vaults');
+ var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults');
return $http.get(queryUrl).then(function (response) {
if(response.data){
return response.data;
@@ -21,8 +22,14 @@ angular.module('passmanApp')
}
});
},
+ setActiveVault: function(vault){
+ _activeVault = vault;
+ },
+ getActiveVault: function(vault){
+ return _activeVault;
+ },
createVault: function (vaultName) {
- var queryUrl = OC.generateUrl('apps/passman/api/v1/vaults');
+ var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults');
return $http.post(queryUrl, { vault_name: vaultName }).then(function (response) {
if(response.data){
return response.data;
@@ -32,7 +39,7 @@ angular.module('passmanApp')
});
},
getVault: function (vault) {
- var queryUrl = OC.generateUrl('apps/passman/api/v1/vaults/' + vault.vault_id);
+ var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults/' + vault.vault_id);
return $http.get(queryUrl).then(function (response) {
if(response.data){
return response.data;
@@ -42,7 +49,7 @@ angular.module('passmanApp')
});
},
updateVault: function (vault) {
- var queryUrl = OC.generateUrl('apps/passman/api/v1/vaults/' + vault.vault_id);
+ var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults/' + vault.vault_id);
return $http.post(queryUrl).then(function (response) {
if(response.data){
return response.data;
@@ -52,7 +59,7 @@ angular.module('passmanApp')
});
},
deleteVault: function (vault) {
- var queryUrl = OC.generateUrl('apps/passman/api/v1/vaults/' + vault.vault_id);
+ var queryUrl = OC.generateUrl('apps/passman/api/v2/vaults/' + vault.vault_id);
return $http.delete(queryUrl).then(function (response) {
if(response.data){
return response.data;
diff --git a/js/script.js b/js/script.js
deleted file mode 100644
index e69de29b..00000000
--- a/js/script.js
+++ /dev/null
diff --git a/js/templates.js b/js/templates.js
index ec8d3f6a..2ce42972 100644
--- a/js/templates.js
+++ b/js/templates.js
@@ -3,5 +3,5 @@ angular.module('templates-main', ['views/vaults.html']);
angular.module('views/vaults.html', []).run(['$templateCache', function($templateCache) {
'use strict';
$templateCache.put('views/vaults.html',
- '<div class="vault_wrapper"><div class="vaults" ng-if="!list_selected_vault && !creating_vault"><div class="ui-select-container ui-select-bootstrap vaultlist"><ul><li ng-click="newVault()">+ Create a new vault</li><li ng-repeat="vault in vaults" ng-class="{\'selected\': vault == list_selected_vault }" ng-click="selectVault(vault)"><div><span class="ui-select-choices-row-inner"><div class="ng-binding ng-scope">{{vault.name}}</div><small class="ng-binding ng-scope">Created: {{vault.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}} | Last accessed: {{vault.last_access * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</small></span></div></li><li ng-if="vaults.length === 0">No vaults found, why not create one?</li></ul></div></div><div ng-if="creating_vault"><div class="login_form" ng-init="vault_name = \'\' ">Please give your new vault a name.<div><input type="text" ng-model="vault_name"></div><div class="button_wrapper"><div class="button button-geen" ng-click="createVault(vault_name)">Create vault</div><div class="button button-red" ng-click="clearState()">Cancel</div></div></div></div><div ng-if="list_selected_vault != false"><div class="vaultlist"><ul><li ng-click="clearState()">Go back to vaults</li></ul></div><div class="login_form">Please input the password for {{list_selected_vault.name}}<div><input type="password"></div><div><div><label><input type="checkbox" ng-checked="default_vault" ng-click="toggleDefaultVault()"> Set this vault as default.</label></div><div><label><input type="checkbox" ng-model="remember_vault_password"> Login automatically to this vault.</label></div></div><div class="button button-geen">Decrypt vault</div></div></div></div>');
+ '<div class="vault_wrapper"><div class="vaults" ng-if="!list_selected_vault && !creating_vault"><div class="ui-select-container ui-select-bootstrap vaultlist"><ul><li ng-click="newVault()">+ Create a new vault</li><li ng-repeat="vault in vaults" ng-class="{\'selected\': vault == list_selected_vault }" ng-click="selectVault(vault)"><div><span class="ui-select-choices-row-inner"><div class="ng-binding ng-scope">{{vault.name}}</div><small class="ng-binding ng-scope">Created: {{vault.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}} | Last accessed: <span ng-if="vault.last_access > 0">{{vault.last_access * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</span> <span ng-if="vault.last_access === 0">Never</span></small></span></div></li><li ng-if="vaults.length === 0">No vaults found, why not create one?</li></ul></div></div><div ng-if="creating_vault"><div class="login_form" ng-init="vault_name = \'\' ">Please give your new vault a name.<div><input type="text" ng-model="vault_name"></div><div>Vault password <input type="text" ng-model="vaultKey"></div><div>Repeat vault password <input type="text" ng-model="vaultKey_2"></div><div class="button_wrapper"><div class="button button-geen" ng-click="createVault(vault_name)">Create vault</div><div class="button button-red" ng-click="clearState()">Cancel</div></div></div></div><div ng-if="list_selected_vault != false"><div class="vaultlist"><ul><li ng-click="clearState()">Go back to vaults</li></ul></div><div class="login_form">Please input the password for {{list_selected_vault.name}}<div><input type="password" ng-model="vaultDecryptionKey"></div><div><div><label><input type="checkbox" ng-checked="default_vault" ng-click="toggleDefaultVault()"> Set this vault as default.</label></div><div><label><input type="checkbox" ng-model="remember_vault_password"> Login automatically to this vault.</label></div></div><div class="button button-geen" ng-click="loginToVault(list_selected_vault)">Decrypt vault</div></div></div></div>');
}]);
diff --git a/js/vendor/sjcl/sjcl.js b/js/vendor/sjcl/sjcl.js
new file mode 100644
index 00000000..56783fa1
--- /dev/null
+++ b/js/vendor/sjcl/sjcl.js
@@ -0,0 +1,41 @@
+"use strict";var sjcl={cipher:{},hash:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
+sjcl.cipher.aes=function(a){this.h[0][0][0]||this.z();var b,c,d,e,f=this.h[0][4],g=this.h[1];b=a.length;var h=1;if(b!==4&&b!==6&&b!==8)throw new sjcl.exception.invalid("invalid aes key size");this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(a%b===0||b===8&&a%b===4){c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255];if(a%b===0){c=c<<8^c>>>24^h<<24;h=h<<1^(h>>7)*283}}d[a]=d[a-b]^c}for(b=0;a;b++,a--){c=d[b&3?a:a-4];e[b]=a<=4||b<4?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^
+g[3][f[c&255]]}};
+sjcl.cipher.aes.prototype={encrypt:function(a){return this.I(a,0)},decrypt:function(a){return this.I(a,1)},h:[[[],[],[],[],[]],[[],[],[],[],[]]],z:function(){var a=this.h[0],b=this.h[1],c=a[4],d=b[4],e,f,g,h=[],i=[],k,j,l,m;for(e=0;e<0x100;e++)i[(h[e]=e<<1^(e>>7)*283)^e]=e;for(f=g=0;!c[f];f^=k||1,g=i[g]||1){l=g^g<<1^g<<2^g<<3^g<<4;l=l>>8^l&255^99;c[f]=l;d[l]=f;j=h[e=h[k=h[f]]];m=j*0x1010101^e*0x10001^k*0x101^f*0x1010100;j=h[l]*0x101^l*0x1010100;for(e=0;e<4;e++){a[e][f]=j=j<<24^j>>>8;b[e][l]=m=m<<24^m>>>8}}for(e=
+ 0;e<5;e++){a[e]=a[e].slice(0);b[e]=b[e].slice(0)}},I:function(a,b){if(a.length!==4)throw new sjcl.exception.invalid("invalid aes block size");var c=this.a[b],d=a[0]^c[0],e=a[b?3:1]^c[1],f=a[2]^c[2];a=a[b?1:3]^c[3];var g,h,i,k=c.length/4-2,j,l=4,m=[0,0,0,0];g=this.h[b];var n=g[0],o=g[1],p=g[2],q=g[3],r=g[4];for(j=0;j<k;j++){g=n[d>>>24]^o[e>>16&255]^p[f>>8&255]^q[a&255]^c[l];h=n[e>>>24]^o[f>>16&255]^p[a>>8&255]^q[d&255]^c[l+1];i=n[f>>>24]^o[a>>16&255]^p[d>>8&255]^q[e&255]^c[l+2];a=n[a>>>24]^o[d>>16&
+ 255]^p[e>>8&255]^q[f&255]^c[l+3];l+=4;d=g;e=h;f=i}for(j=0;j<4;j++){m[b?3&-j:j]=r[d>>>24]<<24^r[e>>16&255]<<16^r[f>>8&255]<<8^r[a&255]^c[l++];g=d;d=e;e=f;f=a;a=g}return m}};
+sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===undefined?a:sjcl.bitArray.clamp(a,c-b)},concat:function(a,b){if(a.length===0||b.length===0)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return d===32?a.concat(b):sjcl.bitArray.P(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;if(b===0)return 0;return(b-1)*32+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(a.length*32<b)return a;a=a.slice(0,Math.ceil(b/
+ 32));var c=a.length;b&=31;if(c>0&&b)a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1);return a},partial:function(a,b,c){if(a===32)return b;return(c?b|0:b<<32-a)+a*0x10000000000},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return false;var c=0,d;for(d=0;d<a.length;d++)c|=a[d]^b[d];return c===0},P:function(a,b,c,d){var e;e=0;if(d===undefined)d=[];for(;b>=32;b-=32){d.push(c);c=0}if(b===0)return d.concat(a);
+ for(e=0;e<a.length;e++){d.push(c|a[e]>>>b);c=a[e]<<32-b}e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,b+a>32?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
+sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++){if((d&3)===0)e=a[d/4];b+=String.fromCharCode(e>>>24);e<<=8}return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++){d=d<<8|a.charCodeAt(c);if((c&3)===3){b.push(d);d=0}}c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
+sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,d*4)}};
+sjcl.codec.base64={F:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b){var c="",d,e=0,f=sjcl.codec.base64.F,g=0,h=sjcl.bitArray.bitLength(a);for(d=0;c.length*6<h;){c+=f.charAt((g^a[d]>>>e)>>>26);if(e<6){g=a[d]<<6-e;e+=26;d++}else{g<<=6;e-=6}}for(;c.length&3&&!b;)c+="=";return c},toBits:function(a){a=a.replace(/\s|=/g,"");var b=[],c,d=0,e=sjcl.codec.base64.F,f=0,g;for(c=0;c<a.length;c++){g=e.indexOf(a.charAt(c));if(g<0)throw new sjcl.exception.invalid("this isn't base64!");
+ if(d>26){d-=26;b.push(f^g>>>d);f=g<<32-d}else{d+=6;f^=g<<32-d}}d&56&&b.push(sjcl.bitArray.partial(d&56,f,1));return b}};sjcl.hash.sha256=function(a){this.a[0]||this.z();if(a){this.n=a.n.slice(0);this.i=a.i.slice(0);this.e=a.e}else this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
+sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.n=this.N.slice(0);this.i=[];this.e=0;return this},update:function(a){if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);var b,c=this.i=sjcl.bitArray.concat(this.i,a);b=this.e;a=this.e=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)this.D(c.splice(0,16));return this},finalize:function(){var a,b=this.i,c=this.n;b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.e/
+ 4294967296));for(b.push(this.e|0);b.length;)this.D(b.splice(0,16));this.reset();return c},N:[],a:[],z:function(){function a(e){return(e-Math.floor(e))*0x100000000|0}var b=0,c=2,d;a:for(;b<64;c++){for(d=2;d*d<=c;d++)if(c%d===0)continue a;if(b<8)this.N[b]=a(Math.pow(c,0.5));this.a[b]=a(Math.pow(c,1/3));b++}},D:function(a){var b,c,d=a.slice(0),e=this.n,f=this.a,g=e[0],h=e[1],i=e[2],k=e[3],j=e[4],l=e[5],m=e[6],n=e[7];for(a=0;a<64;a++){if(a<16)b=d[a];else{b=d[a+1&15];c=d[a+14&15];b=d[a&15]=(b>>>7^b>>>18^
+ b>>>3^b<<25^b<<14)+(c>>>17^c>>>19^c>>>10^c<<15^c<<13)+d[a&15]+d[a+9&15]|0}b=b+n+(j>>>6^j>>>11^j>>>25^j<<26^j<<21^j<<7)+(m^j&(l^m))+f[a];n=m;m=l;l=j;j=k+b|0;k=i;i=h;h=g;g=b+(h&i^k&(h^i))+(h>>>2^h>>>13^h>>>22^h<<30^h<<19^h<<10)|0}e[0]=e[0]+g|0;e[1]=e[1]+h|0;e[2]=e[2]+i|0;e[3]=e[3]+k|0;e[4]=e[4]+j|0;e[5]=e[5]+l|0;e[6]=e[6]+m|0;e[7]=e[7]+n|0}};
+sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,i=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];if(i<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;f<4&&k>>>8*f;f++);if(f<15-i)f=15-i;c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.H(a,b,c,d,e,f);g=sjcl.mode.ccm.J(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),i=f.clamp(b,h-e),k=f.bitSlice(b,
+ h-e);h=(h-e)/8;if(g<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;b<4&&h>>>8*b;b++);if(b<15-g)b=15-g;c=f.clamp(c,8*(15-b));i=sjcl.mode.ccm.J(a,i,c,k,e,b);a=sjcl.mode.ccm.H(a,i.data,c,d,e,b);if(!f.equal(i.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match");return i.data},H:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,i=h.k;e/=8;if(e%2||e<4||e>16)throw new sjcl.exception.invalid("ccm: invalid tag length");if(d.length>0xffffffff||b.length>0xffffffff)throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
+ f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;if(c<=65279)g=[h.partial(16,c)];else if(c<=0xffffffff)g=h.concat([h.partial(16,65534)],[c]);g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(i(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(i(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,e*8)},J:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.k;var i=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
+ f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!i)return{tag:d,data:[]};for(g=0;g<i;g+=4){c[3]++;e=a.encrypt(c);b[g]^=e[0];b[g+1]^=e[1];b[g+2]^=e[2];b[g+3]^=e[3]}return{tag:d,data:h.clamp(b,k)}}};
+sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");var g,h=sjcl.mode.ocb2.B,i=sjcl.bitArray,k=i.k,j=[0,0,0,0];c=h(a.encrypt(c));var l,m=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4){l=b.slice(g,g+4);j=k(j,l);m=m.concat(k(c,a.encrypt(k(c,l))));c=h(c)}l=b.slice(g);b=i.bitLength(l);g=a.encrypt(k(c,[0,0,0,b]));l=i.clamp(k(l.concat([0,0,0]),g),b);j=k(j,k(l.concat([0,0,0]),g));j=a.encrypt(k(j,k(c,h(c))));
+ if(d.length)j=k(j,f?d:sjcl.mode.ocb2.pmac(a,d));return m.concat(i.concat(l,i.clamp(j,e)))},decrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");e=e||64;var g=sjcl.mode.ocb2.B,h=sjcl.bitArray,i=h.k,k=[0,0,0,0],j=g(a.encrypt(c)),l,m,n=sjcl.bitArray.bitLength(b)-e,o=[];d=d||[];for(c=0;c+4<n/32;c+=4){l=i(j,a.decrypt(i(j,b.slice(c,c+4))));k=i(k,l);o=o.concat(l);j=g(j)}m=n-c*32;l=a.encrypt(i(j,[0,0,0,m]));l=i(l,h.clamp(b.slice(c),
+ m).concat([0,0,0]));k=i(k,l);k=a.encrypt(i(k,i(j,g(j))));if(d.length)k=i(k,f?d:sjcl.mode.ocb2.pmac(a,d));if(!h.equal(h.clamp(k,e),h.bitSlice(b,n)))throw new sjcl.exception.corrupt("ocb: tag doesn't match");return o.concat(h.clamp(l,m))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.B,e=sjcl.bitArray,f=e.k,g=[0,0,0,0],h=a.encrypt([0,0,0,0]);h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4){h=d(h);g=f(g,a.encrypt(f(h,b.slice(c,c+4))))}b=b.slice(c);if(e.bitLength(b)<128){h=f(h,d(h));b=e.concat(b,[2147483648|0,0,
+ 0,0])}g=f(g,b);return a.encrypt(f(d(f(h,d(h))),g))},B:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^(a[0]>>>31)*135]}};sjcl.misc.hmac=function(a,b){this.M=b=b||sjcl.hash.sha256;var c=[[],[]],d=b.prototype.blockSize/32;this.l=[new b,new b];if(a.length>d)a=b.hash(a);for(b=0;b<d;b++){c[0][b]=a[b]^909522486;c[1][b]=a[b]^1549556828}this.l[0].update(c[0]);this.l[1].update(c[1])};
+sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){a=(new this.M(this.l[0])).update(a).finalize();return(new this.M(this.l[1])).update(a).finalize()};
+sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;if(d<0||c<0)throw sjcl.exception.invalid("invalid params to pbkdf2");if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,i,k=[],j=sjcl.bitArray;for(i=1;32*k.length<(d||1);i++){e=f=a.encrypt(j.concat(b,[i]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}if(d)k=j.clamp(k,d);return k};
+sjcl.random={randomWords:function(a,b){var c=[];b=this.isReady(b);var d;if(b===0)throw new sjcl.exception.notReady("generator isn't seeded");else b&2&&this.U(!(b&1));for(b=0;b<a;b+=4){(b+1)%0x10000===0&&this.L();d=this.w();c.push(d[0],d[1],d[2],d[3])}this.L();return c.slice(0,a)},setDefaultParanoia:function(a){this.t=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.q[c],h=this.isReady(),i=0;d=this.G[c];if(d===undefined)d=this.G[c]=this.R++;if(g===undefined)g=this.q[c]=
+ 0;this.q[c]=(this.q[c]+1)%this.b.length;switch(typeof a){case "number":if(b===undefined)b=1;this.b[g].update([d,this.u++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if(c==="[object Uint32Array]"){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{if(c!=="[object Array]")i=1;for(c=0;c<a.length&&!i;c++)if(typeof a[c]!="number")i=1}if(!i){if(b===undefined)for(c=b=0;c<a.length;c++)for(e=a[c];e>0;){b++;e>>>=1}this.b[g].update([d,this.u++,2,b,f,a.length].concat(a))}break;case "string":if(b===
+ undefined)b=a.length;this.b[g].update([d,this.u++,3,b,f,a.length]);this.b[g].update(a);break;default:i=1}if(i)throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");this.j[g]+=b;this.f+=b;if(h===0){this.isReady()!==0&&this.K("seeded",Math.max(this.g,this.f));this.K("progress",this.getProgress())}},isReady:function(a){a=this.C[a!==undefined?a:this.t];return this.g&&this.g>=a?this.j[0]>80&&(new Date).valueOf()>this.O?3:1:this.f>=a?2:0},getProgress:function(a){a=
+ this.C[a?a:this.t];return this.g>=a?1:this.f>a?1:this.f/a},startCollectors:function(){if(!this.m){if(window.addEventListener){window.addEventListener("load",this.o,false);window.addEventListener("mousemove",this.p,false)}else if(document.attachEvent){document.attachEvent("onload",this.o);document.attachEvent("onmousemove",this.p)}else throw new sjcl.exception.bug("can't attach event");this.m=true}},stopCollectors:function(){if(this.m){if(window.removeEventListener){window.removeEventListener("load",
+ this.o,false);window.removeEventListener("mousemove",this.p,false)}else if(window.detachEvent){window.detachEvent("onload",this.o);window.detachEvent("onmousemove",this.p)}this.m=false}},addEventListener:function(a,b){this.r[a][this.Q++]=b},removeEventListener:function(a,b){var c;a=this.r[a];var d=[];for(c in a)a.hasOwnProperty(c)&&a[c]===b&&d.push(c);for(b=0;b<d.length;b++){c=d[b];delete a[c]}},b:[new sjcl.hash.sha256],j:[0],A:0,q:{},u:0,G:{},R:0,g:0,f:0,O:0,a:[0,0,0,0,0,0,0,0],d:[0,0,0,0],s:undefined,
+ t:6,m:false,r:{progress:{},seeded:{}},Q:0,C:[0,48,64,96,128,192,0x100,384,512,768,1024],w:function(){for(var a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}return this.s.encrypt(this.d)},L:function(){this.a=this.w().concat(this.w());this.s=new sjcl.cipher.aes(this.a)},T:function(a){this.a=sjcl.hash.sha256.hash(this.a.concat(a));this.s=new sjcl.cipher.aes(this.a);for(a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}},U:function(a){var b=[],c=0,d;this.O=b[0]=(new Date).valueOf()+3E4;for(d=
+ 0;d<16;d++)b.push(Math.random()*0x100000000|0);for(d=0;d<this.b.length;d++){b=b.concat(this.b[d].finalize());c+=this.j[d];this.j[d]=0;if(!a&&this.A&1<<d)break}if(this.A>=1<<this.b.length){this.b.push(new sjcl.hash.sha256);this.j.push(0)}this.f-=c;if(c>this.g)this.g=c;this.A++;this.T(b)},p:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX||0,a.y||a.clientY||a.offsetY||0],2,"mouse")},o:function(){sjcl.random.addEntropy((new Date).valueOf(),2,"loadtime")},K:function(a,b){var c;a=sjcl.random.r[a];
+ var d=[];for(c in a)a.hasOwnProperty(c)&&d.push(a[c]);for(c=0;c<d.length;c++)d[c](b)}};try{var s=new Uint32Array(32);crypto.getRandomValues(s);sjcl.random.addEntropy(s,1024,"crypto['getRandomValues']")}catch(t){}
+sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.c({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.c(f,c);c=f.adata;if(typeof f.salt==="string")f.salt=sjcl.codec.base64.toBits(f.salt);if(typeof f.iv==="string")f.iv=sjcl.codec.base64.toBits(f.iv);if(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||typeof a==="string"&&f.iter<=100||f.ts!==64&&f.ts!==96&&f.ts!==128||f.ks!==128&&f.ks!==192&&f.ks!==0x100||f.iv.length<
+ 2||f.iv.length>4)throw new sjcl.exception.invalid("json encrypt: invalid parameters");if(typeof a==="string"){g=sjcl.misc.cachedPbkdf2(a,f);a=g.key.slice(0,f.ks/32);f.salt=g.salt}if(typeof b==="string")b=sjcl.codec.utf8String.toBits(b);if(typeof c==="string")c=sjcl.codec.utf8String.toBits(c);g=new sjcl.cipher[f.cipher](a);e.c(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return e.encode(f)},decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.c(e.c(e.c({},e.defaults),e.decode(b)),
+ c,true);var f;c=b.adata;if(typeof b.salt==="string")b.salt=sjcl.codec.base64.toBits(b.salt);if(typeof b.iv==="string")b.iv=sjcl.codec.base64.toBits(b.iv);if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||typeof a==="string"&&b.iter<=100||b.ts!==64&&b.ts!==96&&b.ts!==128||b.ks!==128&&b.ks!==192&&b.ks!==0x100||!b.iv||b.iv.length<2||b.iv.length>4)throw new sjcl.exception.invalid("json decrypt: invalid parameters");if(typeof a==="string"){f=sjcl.misc.cachedPbkdf2(a,b);a=f.key.slice(0,b.ks/32);b.salt=f.salt}if(typeof c===
+ "string")c=sjcl.codec.utf8String.toBits(c);f=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,c,b.ts);e.c(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+'"'+b+'":';d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+
+ sjcl.codec.base64.fromBits(a[b],1)+'"';break;default:throw new sjcl.exception.bug("json encode: unsupported type");}}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");if(!a.match(/^\{.*\}$/))throw new sjcl.exception.invalid("json decode: this isn't json!");a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++){if(!(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i)))throw new sjcl.exception.invalid("json decode: this isn't json!");b[d[2]]=
+ d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4])}return b},c:function(a,b,c){if(a===undefined)a={};if(b===undefined)return a;var d;for(d in b)if(b.hasOwnProperty(d)){if(c&&a[d]!==undefined&&a[d]!==b[d])throw new sjcl.exception.invalid("required parameter overridden");a[d]=b[d]}return a},V:function(a,b){var c={},d;for(d=0;d<b.length;d++)if(a[b[d]]!==undefined)c[b[d]]=a[b[d]];return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;
+sjcl.misc.S={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.S,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===undefined?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}}; \ No newline at end of file
diff --git a/lib/Db/Credential.php b/lib/Db/Credential.php
index 569182b4..c590dc59 100644
--- a/lib/Db/Credential.php
+++ b/lib/Db/Credential.php
@@ -10,23 +10,118 @@
*/
namespace OCA\Passman\Db;
-use OCP\Activity\IManager;
-use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\IDBConnection;
-use OCP\IL10N;
+use \OCP\AppFramework\Db\Entity;
+
/**
- * @brief Class for managing the data in the activities
+ * @method integer getId()
+ * @method void setId(integer $value)
+ * @method void setGuid(string $value)
+ * @method string getGuid()
+ * @method void setVaultId(integer $value)
+ * @method integer getVaultId()
+ * @method void setUserId(string $value)
+ * @method string getUserId()
+ * @method void setLabel(string $value)
+ * @method string getLabel()
+ * @method void setDescription(string $value)
+ * @method string getDescription()
+ * @method void setCreated(string $value)
+ * @method string getCreated()
+ * @method void setChanged(string $value)
+ * @method string getChanged()
+ * @method void setTags(string $value)
+ * @method string getTags()
+ * @method void setEmail(string $value)
+ * @method string getEmail()
+ * @method void setUsername(string $value)
+ * @method string getUsername()
+ * @method void setPassword(string $value)
+ * @method string getPassword()
+ * @method void setUrl(string $value)
+ * @method string getUrl()
+ * @method void setFavicon(string $value)
+ * @method string getFavicon()
+ * @method void setRenewInterval(integer $value)
+ * @method integer getRenewInterval()
+ * @method void setExpireTime(integer $value)
+ * @method integer getExpireTime()
+ * @method void setDeleteTime(integer $value)
+ * @method integer getDeleteTime()
+ * @method void setFiles(string $value)
+ * @method string getFiles()
+ * @method void setCustomFields(string $value)
+ * @method string getCustomFields()
+ * @method void setOtp(string $value)
+ * @method string getOtp()
+ * @method void setHidden(bool $value)
+ * @method string getHidden()
+
+
+
*/
-class Credential {
- /** @var IDBConnection */
- protected $connection;
+
+class Credential extends Entity implements \JsonSerializable{
+
+ use EntityJSONSerializer;
+
+ protected $guid;
+ protected $vaultId;
+ protected $userId;
+ protected $label;
+ protected $description;
+ protected $created;
+ protected $changed;
+ protected $tags;
+ protected $email;
+ protected $username;
+ protected $password;
+ protected $url;
+ protected $favicon;
+ protected $renewInterval;
+ protected $expireTime;
+ protected $deleteTime;
+ protected $files;
+ protected $customFields;
+ protected $otp;
+ protected $hidden;
+
+ public function __construct() {
+ // add types in constructor
+ $this->addType('created', 'integer');
+ $this->addType('changed', 'integer');
+ $this->addType('renewInterval', 'integer');
+ $this->addType('expireTime', 'integer');
+ $this->addType('deleteTime', 'integer');
+ }
+
+
/**
- * @param IManager $activityManager
- * @param IDBConnection $connection
+ * Turns entitie attributes into an array
*/
- public function __construct(IDBConnection $connection) {
- $this->connection = $connection;
+ public function jsonSerialize() {
+ return [
+ 'credential_id' => $this->getId(),
+ 'guid' => $this->getGuid(),
+ 'user_id' => $this->getUserId(),
+ 'vault_id' => $this->getVaultId(),
+ 'label' => $this->getLabel(),
+ 'description' => $this->getDescription(),
+ 'created' => $this->getCreated(),
+ 'changed' => $this->getChanged(),
+ 'tags' => $this->getTags(),
+ 'email' => $this->getEmail(),
+ 'username' => $this->getUsername(),
+ 'password' => $this->getPassword(),
+ 'url' => $this->getUrl(),
+ 'favicon' => $this->getFavicon(),
+ 'renew_interval' => $this->getRenewInterval(),
+ 'expire_time' => $this->getExpireTime(),
+ 'delete_time' => $this->getDeleteTime(),
+ 'files' => $this->getFiles(),
+ 'custom_fields' => $this->getCustomFields(),
+ 'otp' => $this->getOtp(),
+ 'hidden' => $this->getHidden(),
+ ];
}
-
} \ No newline at end of file
diff --git a/lib/Db/CredentialMapper.php b/lib/Db/CredentialMapper.php
new file mode 100644
index 00000000..74b889b2
--- /dev/null
+++ b/lib/Db/CredentialMapper.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Nextcloud - passman
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Sander Brand <brantje@gmail.com>
+ * @copyright Sander Brand 2016
+ */
+namespace OCA\Passman\Db;
+
+use OCA\Passman\Utility\Utils;
+use OCP\IDBConnection;
+use OCP\AppFramework\Db\Mapper;
+
+class CredentialMapper extends Mapper {
+ private $utils;
+ public function __construct(IDBConnection $db, Utils $utils) {
+ parent::__construct($db, 'passman_credentials');
+ $this->utils = $utils;
+ }
+
+
+ /**
+ * @throws \OCP\AppFramework\Db\DoesNotExistException if not found
+ * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result
+ */
+ public function find($vault_id) {
+ $sql = 'SELECT * FROM `*PREFIX*passman_vaults` ' .
+ 'WHERE `user_id` = ? LIMIT 1';
+ return $this->findEntity($sql, [$vault_id]);
+ }
+
+ public function findVaultsFromUser($userId){
+ $sql = 'SELECT id, name, created, guid, last_access FROM `*PREFIX*passman_vaults` ' .
+ 'WHERE `user_id` = ? ';
+ $params = [$userId];
+ return $this->findEntities($sql, $params);
+ }
+
+ public function create($raw_credential){
+ $credential = new Credential();
+
+ $credential->setGuid($this->utils->GUID());
+ $credential->setVaultId($raw_credential['vault_id']);
+ $credential->setUserId($raw_credential['user_id']);
+ $credential->setLabel($raw_credential['label']);
+ $credential->setDescription($raw_credential['description']);
+ $credential->setCreated($this->utils->getTime());
+ $credential->setChanged($this->utils->getTime());
+ $credential->setTags($raw_credential['tags']);
+ $credential->setEmail($raw_credential['email']);
+ $credential->setUsername($raw_credential['username']);
+ $credential->setPassword($raw_credential['password']);
+ $credential->setUrl($raw_credential['url']);
+ $credential->setFavicon($raw_credential['favicon']);
+ $credential->setRenewInterval($raw_credential['renew_interval']);
+ $credential->setExpireTime($raw_credential['expire_time']);
+ $credential->setDeleteTime($raw_credential['delete_time']);
+ $credential->setFiles($raw_credential['files']);
+ $credential->setCustomFields($raw_credential['custom_fields']);
+ $credential->setOtp($raw_credential['otp']);
+ $credential->setHidden($raw_credential['hidden']);
+ return parent::insert($credential);
+ }
+
+} \ No newline at end of file
diff --git a/lib/Db/Vault.php b/lib/Db/Vault.php
index d7e02636..5da6e6f4 100644
--- a/lib/Db/Vault.php
+++ b/lib/Db/Vault.php
@@ -43,23 +43,12 @@ class Vault extends Entity implements \JsonSerializable{
$this->addType('created', 'integer');
$this->addType('lastAccess', 'integer');
}
-
- public static function fromRow($row){
- $vault = new Vault();
- $vault->setId($row['id']);
- $vault->setGuid($row['guid']);
- $vault->setName($row['name']);
- $vault->setCreated($row['created']);
- $vault->setlastAccess($row['last_access']);
- return $vault;
- }
-
/**
* Turns entitie attributes into an array
*/
public function jsonSerialize() {
return [
- 'id' => $this->getId(),
+ 'vault_id' => $this->getId(),
'guid' => $this->getGuid(),
'name' => $this->getName(),
'created' => $this->getCreated(),
diff --git a/lib/Service/CredentialService.php b/lib/Service/CredentialService.php
new file mode 100644
index 00000000..143d43e0
--- /dev/null
+++ b/lib/Service/CredentialService.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Nextcloud - passman
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Sander Brand <brantje@gmail.com>
+ * @copyright Sander Brand 2016
+ */
+
+namespace OCA\Passman\Service;
+
+use OCP\IConfig;
+use OCP\AppFramework\Db\DoesNotExistException;
+
+use OCA\Passman\Db\CredentialMapper;
+
+
+class CredentialService {
+
+ private $credentialMapper;
+
+ public function __construct(CredentialMapper $credentialMapper) {
+ $this->credentialMapper = $credentialMapper;
+ }
+
+ public function createCredential($credential) {
+ return $this->credentialMapper->create($credential);
+ }
+} \ No newline at end of file
diff --git a/sass/vaults.scss b/sass/vaults.scss
index c083d259..a53b8cf6 100644
--- a/sass/vaults.scss
+++ b/sass/vaults.scss
@@ -45,4 +45,10 @@
margin-top: 10px;
}
}
+}
+
+@media screen and (max-width: 768px){
+ .vault_wrapper{
+ width: 90%;
+ }
} \ No newline at end of file
diff --git a/templates/main.php b/templates/main.php
index 3c6ae883..a342fa78 100644
--- a/templates/main.php
+++ b/templates/main.php
@@ -10,6 +10,7 @@ script('passman', 'vendor/angular-route/angular-route.min');
script('passman', 'vendor/angular-sanitize/angular-sanitize.min');
script('passman', 'vendor/angular-touch/angular-touch.min');
script('passman', 'vendor/angular-local-storage/angular-local-storage.min');
+script('passman', 'vendor/sjcl/sjcl');
script('passman', 'app/app');
@@ -21,6 +22,7 @@ script('passman', 'app/services/cacheservice');
script('passman', 'app/services/vaultservice');
script('passman', 'app/services/credentialservice');
script('passman', 'app/services/settingsservice');
+script('passman', 'app/services/encryptservice');
/*
diff --git a/templates/views/vaults.html b/templates/views/vaults.html
index fd5c796e..04b46491 100644
--- a/templates/views/vaults.html
+++ b/templates/views/vaults.html
@@ -13,7 +13,9 @@
<small class="ng-binding ng-scope">
Created: {{vault.created * 1000 | date:'dd-MM-yyyy @ HH:mm:ss'}}
|
- Last accessed: {{vault.last_access * 1000 | date:'dd-MM-yyyy @ HH:mm:ss'}}
+ Last accessed:
+ <span ng-if="vault.last_access > 0">{{vault.last_access * 1000 | date:'dd-MM-yyyy @ HH:mm:ss'}}</span>
+ <span ng-if="vault.last_access === 0">Never</span>
</small>
</span>
</div>
@@ -30,6 +32,14 @@
<div>
<input type="text" ng-model="vault_name">
</div>
+ <div>
+ Vault password
+ <input type="text" ng-model="vaultKey">
+ </div>
+ <div>
+ Repeat vault password
+ <input type="text" ng-model="vaultKey_2">
+ </div>
<div class="button_wrapper">
<div class="button button-geen" ng-click="createVault(vault_name)">
Create vault
@@ -51,7 +61,7 @@
Please input the password for {{list_selected_vault.name}}
<div>
- <input type="password">
+ <input type="password" ng-model="vaultDecryptionKey">
</div>
<div>
<div>
@@ -67,7 +77,7 @@
</label>
</div>
</div>
- <div class="button button-geen">
+ <div class="button button-geen" ng-click="loginToVault(list_selected_vault)">
Decrypt vault
</div>
</div>