Merge pull request #538 from Hinton/feature/hide-passwords
Add support for collections with hide passwords
This commit is contained in:
commit
3d160ee1df
2
jslib
2
jslib
|
@ -1 +1 @@
|
||||||
Subproject commit eef9588646091d268ef4c94f3d6db1412d9a99ab
|
Subproject commit 2b6657a293e7be107271ad080c64ccd3f005afb7
|
|
@ -41,6 +41,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th>{{'name' | i18n}}</th>
|
<th>{{'name' | i18n}}</th>
|
||||||
|
<th width="100" class="text-center">{{'hidePasswords' | i18n}}</th>
|
||||||
<th width="100" class="text-center">{{'readOnly' | i18n}}</th>
|
<th width="100" class="text-center">{{'readOnly' | i18n}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -58,6 +59,10 @@
|
||||||
<span class="sr-only">{{'groupAccessAllItems' | i18n}}</span>
|
<span class="sr-only">{{'groupAccessAllItems' | i18n}}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox" [(ngModel)]="g.hidePasswords"
|
||||||
|
name="Groups[{{i}}].HidePasswords" [disabled]="!g.checked || g.accessAll">
|
||||||
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<input type="checkbox" [(ngModel)]="g.readOnly" name="Groups[{{i}}].ReadOnly"
|
<input type="checkbox" [(ngModel)]="g.readOnly" name="Groups[{{i}}].ReadOnly"
|
||||||
[disabled]="!g.checked || g.accessAll">
|
[disabled]="!g.checked || g.accessAll">
|
||||||
|
|
|
@ -73,6 +73,7 @@ export class CollectionAddEditComponent implements OnInit {
|
||||||
if (group != null && group.length > 0) {
|
if (group != null && group.length > 0) {
|
||||||
(group[0] as any).checked = true;
|
(group[0] as any).checked = true;
|
||||||
(group[0] as any).readOnly = s.readOnly;
|
(group[0] as any).readOnly = s.readOnly;
|
||||||
|
(group[0] as any).hidePasswords = s.hidePasswords;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -97,6 +98,7 @@ export class CollectionAddEditComponent implements OnInit {
|
||||||
(g as any).checked = select == null ? !(g as any).checked : select;
|
(g as any).checked = select == null ? !(g as any).checked : select;
|
||||||
if (!(g as any).checked) {
|
if (!(g as any).checked) {
|
||||||
(g as any).readOnly = false;
|
(g as any).readOnly = false;
|
||||||
|
(g as any).hidePasswords = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +115,7 @@ export class CollectionAddEditComponent implements OnInit {
|
||||||
request.name = (await this.cryptoService.encrypt(this.name, this.orgKey)).encryptedString;
|
request.name = (await this.cryptoService.encrypt(this.name, this.orgKey)).encryptedString;
|
||||||
request.externalId = this.externalId;
|
request.externalId = this.externalId;
|
||||||
request.groups = this.groups.filter((g) => (g as any).checked && !g.accessAll)
|
request.groups = this.groups.filter((g) => (g as any).checked && !g.accessAll)
|
||||||
.map((g) => new SelectionReadOnlyRequest(g.id, !!(g as any).readOnly));
|
.map((g) => new SelectionReadOnlyRequest(g.id, !!(g as any).readOnly, !!(g as any).hidePasswords));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.editMode) {
|
if (this.editMode) {
|
||||||
|
|
|
@ -47,6 +47,8 @@
|
||||||
<th>{{'name' | i18n}}</th>
|
<th>{{'name' | i18n}}</th>
|
||||||
<th *ngIf="entity === 'collection'"> </th>
|
<th *ngIf="entity === 'collection'"> </th>
|
||||||
<th>{{'userType' | i18n}}</th>
|
<th>{{'userType' | i18n}}</th>
|
||||||
|
<th width="100" class="text-center" *ngIf="entity === 'collection'">{{'hidePasswords' |
|
||||||
|
i18n}}</th>
|
||||||
<th width="100" class="text-center" *ngIf="entity === 'collection'">{{'readOnly' |
|
<th width="100" class="text-center" *ngIf="entity === 'collection'">{{'readOnly' |
|
||||||
i18n}}</th>
|
i18n}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -85,6 +87,11 @@
|
||||||
<span *ngIf="u.type === organizationUserType.Manager">{{'manager' | i18n}}</span>
|
<span *ngIf="u.type === organizationUserType.Manager">{{'manager' | i18n}}</span>
|
||||||
<span *ngIf="u.type === organizationUserType.User">{{'user' | i18n}}</span>
|
<span *ngIf="u.type === organizationUserType.User">{{'user' | i18n}}</span>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="text-center" *ngIf="entity === 'collection'">
|
||||||
|
<input type="checkbox" [(ngModel)]="u.hidePasswords"
|
||||||
|
name="{{u.id.substr(0,8)}}_HidePasswords"
|
||||||
|
[disabled]="u.accessAll || !u.checked">
|
||||||
|
</td>
|
||||||
<td class="text-center" *ngIf="entity === 'collection'">
|
<td class="text-center" *ngIf="entity === 'collection'">
|
||||||
<input type="checkbox" [(ngModel)]="u.readOnly" name="{{u.id.substr(0,8)}}_ReadOnly"
|
<input type="checkbox" [(ngModel)]="u.readOnly" name="{{u.id.substr(0,8)}}_ReadOnly"
|
||||||
[disabled]="u.accessAll || !u.checked">
|
[disabled]="u.accessAll || !u.checked">
|
||||||
|
|
|
@ -78,6 +78,7 @@ export class EntityUsersComponent implements OnInit {
|
||||||
if (user != null && user.length > 0) {
|
if (user != null && user.length > 0) {
|
||||||
(user[0] as any).checked = true;
|
(user[0] as any).checked = true;
|
||||||
(user[0] as any).readOnly = s.readOnly;
|
(user[0] as any).readOnly = s.readOnly;
|
||||||
|
(user[0] as any).hidePasswords = s.hidePasswords;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -107,6 +108,7 @@ export class EntityUsersComponent implements OnInit {
|
||||||
} else {
|
} else {
|
||||||
if (this.entity === 'collection') {
|
if (this.entity === 'collection') {
|
||||||
(u as any).readOnly = false;
|
(u as any).readOnly = false;
|
||||||
|
(u as any).hidePasswords = false;
|
||||||
}
|
}
|
||||||
this.selectedCount--;
|
this.selectedCount--;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +125,7 @@ export class EntityUsersComponent implements OnInit {
|
||||||
this.formPromise = this.apiService.putGroupUsers(this.organizationId, this.entityId, selections);
|
this.formPromise = this.apiService.putGroupUsers(this.organizationId, this.entityId, selections);
|
||||||
} else {
|
} else {
|
||||||
const selections = this.users.filter((u) => (u as any).checked && !u.accessAll)
|
const selections = this.users.filter((u) => (u as any).checked && !u.accessAll)
|
||||||
.map((u) => new SelectionReadOnlyRequest(u.id, !!(u as any).readOnly));
|
.map((u) => new SelectionReadOnlyRequest(u.id, !!(u as any).readOnly, !!(u as any).hidePasswords));
|
||||||
this.formPromise = this.apiService.putCollectionUsers(this.organizationId, this.entityId, selections);
|
this.formPromise = this.apiService.putCollectionUsers(this.organizationId, this.entityId, selections);
|
||||||
}
|
}
|
||||||
await this.formPromise;
|
await this.formPromise;
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th>{{'name' | i18n}}</th>
|
<th>{{'name' | i18n}}</th>
|
||||||
|
<th width="100" class="text-center">{{'hidePasswords' | i18n}}</th>
|
||||||
<th width="100" class="text-center">{{'readOnly' | i18n}}</th>
|
<th width="100" class="text-center">{{'readOnly' | i18n}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -71,6 +72,10 @@
|
||||||
<td (click)="check(c)">
|
<td (click)="check(c)">
|
||||||
{{c.name}}
|
{{c.name}}
|
||||||
</td>
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox" [(ngModel)]="c.hidePasswords"
|
||||||
|
name="Collection[{{i}}].HidePasswords" [disabled]="!c.checked">
|
||||||
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<input type="checkbox" [(ngModel)]="c.readOnly" name="Collection[{{i}}].ReadOnly"
|
<input type="checkbox" [(ngModel)]="c.readOnly" name="Collection[{{i}}].ReadOnly"
|
||||||
[disabled]="!c.checked">
|
[disabled]="!c.checked">
|
||||||
|
|
|
@ -63,6 +63,7 @@ export class GroupAddEditComponent implements OnInit {
|
||||||
if (collection != null && collection.length > 0) {
|
if (collection != null && collection.length > 0) {
|
||||||
(collection[0] as any).checked = true;
|
(collection[0] as any).checked = true;
|
||||||
collection[0].readOnly = s.readOnly;
|
collection[0].readOnly = s.readOnly;
|
||||||
|
collection[0].hidePasswords = s.hidePasswords;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -99,7 +100,7 @@ export class GroupAddEditComponent implements OnInit {
|
||||||
request.accessAll = this.access === 'all';
|
request.accessAll = this.access === 'all';
|
||||||
if (!request.accessAll) {
|
if (!request.accessAll) {
|
||||||
request.collections = this.collections.filter((c) => (c as any).checked)
|
request.collections = this.collections.filter((c) => (c as any).checked)
|
||||||
.map((c) => new SelectionReadOnlyRequest(c.id, !!c.readOnly));
|
.map((c) => new SelectionReadOnlyRequest(c.id, !!c.readOnly, !!c.hidePasswords));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -94,6 +94,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th>{{'name' | i18n}}</th>
|
<th>{{'name' | i18n}}</th>
|
||||||
|
<th width="100" class="text-center">{{'hidePasswords' | i18n}}</th>
|
||||||
<th width="100" class="text-center">{{'readOnly' | i18n}}</th>
|
<th width="100" class="text-center">{{'readOnly' | i18n}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -106,6 +107,10 @@
|
||||||
<td (click)="check(c)">
|
<td (click)="check(c)">
|
||||||
{{c.name}}
|
{{c.name}}
|
||||||
</td>
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox" [(ngModel)]="c.hidePasswords"
|
||||||
|
name="Collection[{{i}}].HidePasswords" [disabled]="!c.checked">
|
||||||
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<input type="checkbox" [(ngModel)]="c.readOnly" name="Collection[{{i}}].ReadOnly"
|
<input type="checkbox" [(ngModel)]="c.readOnly" name="Collection[{{i}}].ReadOnly"
|
||||||
[disabled]="!c.checked">
|
[disabled]="!c.checked">
|
||||||
|
|
|
@ -67,6 +67,7 @@ export class UserAddEditComponent implements OnInit {
|
||||||
if (collection != null && collection.length > 0) {
|
if (collection != null && collection.length > 0) {
|
||||||
(collection[0] as any).checked = true;
|
(collection[0] as any).checked = true;
|
||||||
collection[0].readOnly = s.readOnly;
|
collection[0].readOnly = s.readOnly;
|
||||||
|
collection[0].hidePasswords = s.hidePasswords;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -100,7 +101,7 @@ export class UserAddEditComponent implements OnInit {
|
||||||
let collections: SelectionReadOnlyRequest[] = null;
|
let collections: SelectionReadOnlyRequest[] = null;
|
||||||
if (this.access !== 'all') {
|
if (this.access !== 'all') {
|
||||||
collections = this.collections.filter((c) => (c as any).checked)
|
collections = this.collections.filter((c) => (c as any).checked)
|
||||||
.map((c) => new SelectionReadOnlyRequest(c.id, !!c.readOnly));
|
.map((c) => new SelectionReadOnlyRequest(c.id, !!c.readOnly, !!c.hidePasswords));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -54,7 +54,8 @@
|
||||||
<label for="loginPassword">{{'password' | i18n}}</label>
|
<label for="loginPassword">{{'password' | i18n}}</label>
|
||||||
<div class="ml-auto d-flex" *ngIf="!cipher.isDeleted">
|
<div class="ml-auto d-flex" *ngIf="!cipher.isDeleted">
|
||||||
<a href="#" class="d-block mr-2" appStopClick
|
<a href="#" class="d-block mr-2" appStopClick
|
||||||
appA11yTitle="{{'generatePassword' | i18n}}" (click)="generatePassword()">
|
appA11yTitle="{{'generatePassword' | i18n}}" (click)="generatePassword()"
|
||||||
|
*ngIf="cipher.viewPassword">
|
||||||
<i class="fa fa-lg fa-fw fa-refresh" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-fw fa-refresh" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" class="d-block" #checkPasswordBtn appStopClick
|
<a href="#" class="d-block" #checkPasswordBtn appStopClick
|
||||||
|
@ -71,17 +72,18 @@
|
||||||
<input id="loginPassword" class="form-control text-monospace"
|
<input id="loginPassword" class="form-control text-monospace"
|
||||||
type="{{showPassword ? 'text' : 'password'}}" name="Login.Password"
|
type="{{showPassword ? 'text' : 'password'}}" name="Login.Password"
|
||||||
[(ngModel)]="cipher.login.password" appInputVerbatim autocomplete="new-password"
|
[(ngModel)]="cipher.login.password" appInputVerbatim autocomplete="new-password"
|
||||||
[disabled]="cipher.isDeleted">
|
[disabled]="cipher.isDeleted || !cipher.viewPassword">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button type="button" class="btn btn-outline-secondary"
|
<button type="button" class="btn btn-outline-secondary"
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()"
|
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()"
|
||||||
tabindex="-1">
|
tabindex="-1" [disabled]="!cipher.viewPassword">
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
<i class="fa fa-lg" aria-hidden="true"
|
||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-outline-secondary"
|
<button type="button" class="btn btn-outline-secondary"
|
||||||
appA11yTitle="{{'copyPassword' | i18n}}"
|
appA11yTitle="{{'copyPassword' | i18n}}"
|
||||||
(click)="copy(cipher.login.password, 'password', 'Password')" tabindex="-1">
|
(click)="copy(cipher.login.password, 'password', 'Password')" tabindex="-1"
|
||||||
|
[disabled]="!cipher.viewPassword">
|
||||||
<i class="fa fa-lg fa-clipboard" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clipboard" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -91,8 +93,8 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6 form-group">
|
<div class="col-6 form-group">
|
||||||
<label for="loginTotp">{{'authenticatorKeyTotp' | i18n}}</label>
|
<label for="loginTotp">{{'authenticatorKeyTotp' | i18n}}</label>
|
||||||
<input id="loginTotp" type="text" name="Login.Totp" class="form-control text-monospace"
|
<input id="loginTotp" type="{{cipher.viewPassword ? 'text' : 'password'}}" name="Login.Totp" class="form-control text-monospace"
|
||||||
[(ngModel)]="cipher.login.totp" appInputVerbatim [disabled]="cipher.isDeleted">
|
[(ngModel)]="cipher.login.totp" appInputVerbatim [disabled]="cipher.isDeleted || !cipher.viewPassword">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 form-group totp d-flex align-items-end" [ngClass]="{'low': totpLow}">
|
<div class="col-6 form-group totp d-flex align-items-end" [ngClass]="{'low': totpLow}">
|
||||||
<div *ngIf="!cipher.login.totp || !totpCode">
|
<div *ngIf="!cipher.login.totp || !totpCode">
|
||||||
|
@ -401,11 +403,11 @@
|
||||||
<input id="fieldValue{{i}}" type="{{f.showValue ? 'text' : 'password'}}"
|
<input id="fieldValue{{i}}" type="{{f.showValue ? 'text' : 'password'}}"
|
||||||
name="Field.Value{{i}}" [(ngModel)]="f.value"
|
name="Field.Value{{i}}" [(ngModel)]="f.value"
|
||||||
class="form-control text-monospace" appInputVerbatim
|
class="form-control text-monospace" appInputVerbatim
|
||||||
autocomplete="new-password" [disabled]="cipher.isDeleted">
|
autocomplete="new-password" [disabled]="cipher.isDeleted || (!cipher.viewPassword && !f.newField)">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button type="button" class="btn btn-outline-secondary"
|
<button type="button" class="btn btn-outline-secondary"
|
||||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleFieldValue(f)"
|
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleFieldValue(f)"
|
||||||
tabindex="-1">
|
tabindex="-1" [disabled]="!cipher.viewPassword && !f.newField">
|
||||||
<i class="fa fa-lg" aria-hidden="true"
|
<i class="fa fa-lg" aria-hidden="true"
|
||||||
[ngClass]="{'fa-eye': !f.showValue, 'fa-eye-slash': f.showValue}">
|
[ngClass]="{'fa-eye': !f.showValue, 'fa-eye-slash': f.showValue}">
|
||||||
</i>
|
</i>
|
||||||
|
@ -413,7 +415,7 @@
|
||||||
<button type="button" class="btn btn-outline-secondary"
|
<button type="button" class="btn btn-outline-secondary"
|
||||||
appA11yTitle="{{'copyValue' | i18n}}"
|
appA11yTitle="{{'copyValue' | i18n}}"
|
||||||
(click)="copy(f.value, 'value', f.type === fieldType.Hidden ? 'H_Field' : 'Field')"
|
(click)="copy(f.value, 'value', f.type === fieldType.Hidden ? 'H_Field' : 'Field')"
|
||||||
tabindex="-1">
|
tabindex="-1" [disabled]="!cipher.viewPassword && !f.newField">
|
||||||
<i class="fa fa-lg fa-clipboard" aria-hidden="true"></i>
|
<i class="fa fa-lg fa-clipboard" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
||||||
<ng-container *ngIf="c.type === cipherType.Login && !c.isDeleted">
|
<ng-container *ngIf="c.type === cipherType.Login && !c.isDeleted">
|
||||||
<a class="dropdown-item" href="#" appStopClick
|
<a class="dropdown-item" href="#" appStopClick
|
||||||
(click)="copy(c, c.login.password, 'password', 'password')">
|
(click)="copy(c, c.login.password, 'password', 'password')" *ngIf="c.viewPassword">
|
||||||
<i class="fa fa-fw fa-clipboard" aria-hidden="true"></i>
|
<i class="fa fa-fw fa-clipboard" aria-hidden="true"></i>
|
||||||
{{'copyPassword' | i18n}}
|
{{'copyPassword' | i18n}}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3148,5 +3148,8 @@
|
||||||
},
|
},
|
||||||
"vaultTimeoutLogOutConfirmationTitle": {
|
"vaultTimeoutLogOutConfirmationTitle": {
|
||||||
"message": "Timeout Action Confirmation"
|
"message": "Timeout Action Confirmation"
|
||||||
|
},
|
||||||
|
"hidePasswords": {
|
||||||
|
"message": "Hide Passwords"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue