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

[Send] Enterprise policy enforcement (#1637)

This commit is contained in:
Vincent Salucci 2021-02-24 16:03:44 -06:00 committed by GitHub
parent 4853fb3e29
commit e17649d869
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 87 additions and 34 deletions

View File

@ -1624,6 +1624,10 @@
"newPassword": { "newPassword": {
"message": "New Password" "message": "New Password"
}, },
"sendDisabled": {
"message": "Send Disabled",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
"sendDisabledWarning": { "sendDisabledWarning": {
"message": "Due to an enterprise policy, you are only able to delete an existing Send.", "message": "Due to an enterprise policy, you are only able to delete an existing Send.",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."

View File

@ -39,8 +39,8 @@
(click)="copySendLink(s)"> (click)="copySendLink(s)">
<i class="fa fa-lg fa-copy" aria-hidden="true"></i> <i class="fa fa-lg fa-copy" aria-hidden="true"></i>
</span> </span>
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'removePassword' | i18n}}" <span class="row-btn" [ngClass]="{'disabled' : disabledByPolicy}" appStopClick appStopProp
(click)="removePassword(s)" *ngIf="s.password"> appA11yTitle="{{'removePassword' | i18n}}" (click)="removePassword(s)" *ngIf="s.password">
<i class="fa fa-lg fa-undo" aria-hidden="true"></i> <i class="fa fa-lg fa-undo" aria-hidden="true"></i>
</span> </span>
<span class="row-btn" appStopClick appStopProp appA11yTitle="{{'delete' | i18n}}" (click)="delete(s)"> <span class="row-btn" appStopClick appStopProp appA11yTitle="{{'delete' | i18n}}" (click)="delete(s)">

View File

@ -16,6 +16,7 @@ import { SendType } from 'jslib/enums/sendType';
export class SendListComponent { export class SendListComponent {
@Input() sends: SendView[]; @Input() sends: SendView[];
@Input() title: string; @Input() title: string;
@Input() disabledByPolicy = false;
@Output() onSelected = new EventEmitter<SendView>(); @Output() onSelected = new EventEmitter<SendView>();
@Output() onCopySendLink = new EventEmitter<SendView>(); @Output() onCopySendLink = new EventEmitter<SendView>();
@Output() onRemovePassword = new EventEmitter<SendView>(); @Output() onRemovePassword = new EventEmitter<SendView>();

View File

@ -356,6 +356,16 @@ content {
&.no-header { &.no-header {
top: 0; top: 0;
} }
&.flex {
display: flex;
flex-flow: column;
height: calc(100% - 44px);
&.tab-page {
height: calc(100% - 99px);
}
}
} }
.tab-page { .tab-page {

View File

@ -7,17 +7,21 @@
<span class="title">{{title}}</span> <span class="title">{{title}}</span>
</div> </div>
<div class="right"> <div class="right">
<button type="submit" appBlurClick [disabled]="form.loading"> <button type="submit" appBlurClick [disabled]="form.loading || disableSend">
<span [hidden]="form.loading">{{'save' | i18n}}</span> <span [hidden]="form.loading">{{'save' | i18n}}</span>
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i> <i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button> </button>
</div> </div>
</header> </header>
<content *ngIf="send"> <content *ngIf="send">
<!-- Policy Banner -->
<app-callout type="warning" title="{{'sendDisabled' | i18n}}" *ngIf="disableSend">
{{'sendDisabledWarning' | i18n}}
</app-callout>
<!-- File Warning --> <!-- File Warning -->
<app-callout type="warning" icon="fa fa-external-link fa-rotate-270 fa-fw" [clickable]="true" <app-callout type="warning" icon="fa fa-external-link fa-rotate-270 fa-fw" [clickable]="true"
title="{{'sendFileCalloutHeader' | i18n}}" *ngIf="showFilePopoutMessage && send.type === sendType.File" title="{{'sendFileCalloutHeader' | i18n}}"
(click)="popOutWindow()"> *ngIf="showFilePopoutMessage && send.type === sendType.File && !disableSend" (click)="popOutWindow()">
<div *ngIf="showFirefoxFileWarning">{{'sendFirefoxFileWarning' | i18n}}</div> <div *ngIf="showFirefoxFileWarning">{{'sendFirefoxFileWarning' | i18n}}</div>
<div *ngIf="showSafariFileWarning">{{'sendSafariFileWarning' | i18n}}</div> <div *ngIf="showSafariFileWarning">{{'sendSafariFileWarning' | i18n}}</div>
</app-callout> </app-callout>
@ -26,7 +30,7 @@
<div class="box-content"> <div class="box-content">
<div class="box-content-row" appBoxRow> <div class="box-content-row" appBoxRow>
<label for="name">{{'name' | i18n}}</label> <label for="name">{{'name' | i18n}}</label>
<input id="name" type="text" name="Name" [(ngModel)]="send.name"> <input id="name" type="text" name="Name" [(ngModel)]="send.name" [readonly]="disableSend">
</div> </div>
</div> </div>
<div class="box-footer"> <div class="box-footer">
@ -41,7 +45,8 @@
<div class="radio-group text-default" appBoxRow name="SendTypeOptions" <div class="radio-group text-default" appBoxRow name="SendTypeOptions"
*ngFor="let o of typeOptions"> *ngFor="let o of typeOptions">
<input type="radio" [(ngModel)]="send.type" name="Type_{{o.value}}" id="type_{{o.value}}" <input type="radio" [(ngModel)]="send.type" name="Type_{{o.value}}" id="type_{{o.value}}"
[value]="o.value" (change)="typeChanged()" [checked]="send.type === o.value"> [value]="o.value" (change)="typeChanged()" [checked]="send.type === o.value"
[readonly]="disableSend">
<label for="type_{{o.value}}"> <label for="type_{{o.value}}">
{{o.name}} {{o.name}}
</label> </label>
@ -58,7 +63,7 @@
</div> </div>
<div class="box-content-row" *ngIf="showFileSelector"> <div class="box-content-row" *ngIf="showFileSelector">
<label for="file">{{'file' | i18n}}</label> <label for="file">{{'file' | i18n}}</label>
<input type="file" id="file" name="file" required> <input type="file" id="file" name="file" required [readonly]="disableSend">
</div> </div>
</div> </div>
<div class="box-footer" *ngIf="showFileSelector"> <div class="box-footer" *ngIf="showFileSelector">
@ -70,7 +75,8 @@
<div class="box-content"> <div class="box-content">
<div class="box-content-row" appBoxRow> <div class="box-content-row" appBoxRow>
<label for="text">{{'sendTypeText' | i18n}}</label> <label for="text">{{'sendTypeText' | i18n}}</label>
<textarea id="text" name="Text" rows="6" [(ngModel)]="send.text.text"></textarea> <textarea id="text" name="Text" rows="6" [(ngModel)]="send.text.text"
[readonly]="disableSend"></textarea>
</div> </div>
</div> </div>
<div class="box-footer"> <div class="box-footer">
@ -79,7 +85,8 @@
<div class="box-content"> <div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow> <div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="hideText">{{'sendHideText' | i18n}}</label> <label for="hideText">{{'sendHideText' | i18n}}</label>
<input id="hideText" type="checkbox" name="HideText" [(ngModel)]="send.text.hidden"> <input id="hideText" type="checkbox" name="HideText" [(ngModel)]="send.text.hidden"
[disabled]="disableSend">
</div> </div>
</div> </div>
</div> </div>
@ -92,7 +99,8 @@
<!-- Copy Link on Save --> <!-- Copy Link on Save -->
<div class="box-content-row box-content-row-checkbox" appBoxRow> <div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="copyOnSave">{{'sendShareDesc' | i18n}}</label> <label for="copyOnSave">{{'sendShareDesc' | i18n}}</label>
<input id="copyOnSave" type="checkbox" name="CopyOnSave" [(ngModel)]="copyLink"> <input id="copyOnSave" type="checkbox" name="CopyOnSave" [(ngModel)]="copyLink"
[disabled]="disableSend">
</div> </div>
</div> </div>
</div> </div>
@ -122,7 +130,8 @@
<div class="box-content-row" *ngIf="editMode" appBoxRow> <div class="box-content-row" *ngIf="editMode" appBoxRow>
<label for="editDeletionDate">{{'deletionDate' | i18n}}</label> <label for="editDeletionDate">{{'deletionDate' | i18n}}</label>
<input id="editDeletionDate" type="datetime-local" name="EditDeletionDate" <input id="editDeletionDate" type="datetime-local" name="EditDeletionDate"
[(ngModel)]="deletionDate" required placeholder="MM/DD/YYYY HH:MM AM/PM"> [(ngModel)]="deletionDate" required placeholder="MM/DD/YYYY HH:MM AM/PM"
[readonly]="disableSend">
</div> </div>
</div> </div>
<div class="box-footer"> <div class="box-footer">
@ -147,12 +156,12 @@
<div class="box-content-row" *ngIf="editMode" appBoxRow> <div class="box-content-row" *ngIf="editMode" appBoxRow>
<div class="flex-label"> <div class="flex-label">
<label for="editExpirationDate">{{'expirationDate' | i18n}}</label> <label for="editExpirationDate">{{'expirationDate' | i18n}}</label>
<a href="#" appStopClick (click)="clearExpiration()"> <a *ngIf="!disableSend" href="#" appStopClick (click)="clearExpiration()">
{{'clear' | i18n}} {{'clear' | i18n}}
</a> </a>
</div> </div>
<input id="editExpirationDate" type="datetime-local" name="EditExpirationDate" <input id="editExpirationDate" type="datetime-local" name="EditExpirationDate"
[(ngModel)]="expirationDate" placeholder="MM/DD/YYYY HH:MM AM/PM"> [(ngModel)]="expirationDate" placeholder="MM/DD/YYYY HH:MM AM/PM" [readonly]="disableSend">
</div> </div>
</div> </div>
<div class="box-footer"> <div class="box-footer">
@ -165,7 +174,7 @@
<div class="box-content-row" appBoxRow> <div class="box-content-row" appBoxRow>
<label for="maximumAccessCount">{{'maximumAccessCount' | i18n}}</label> <label for="maximumAccessCount">{{'maximumAccessCount' | i18n}}</label>
<input id="maximumAccessCount" min="1" type="number" name="MaximumAccessCount" <input id="maximumAccessCount" min="1" type="number" name="MaximumAccessCount"
[(ngModel)]="send.maxAccessCount"> [(ngModel)]="send.maxAccessCount" [readonly]="disableSend">
</div> </div>
</div> </div>
<div class="box-footer"> <div class="box-footer">
@ -190,9 +199,9 @@
<label for="password" *ngIf="hasPassword">{{'newPassword' | i18n}}</label> <label for="password" *ngIf="hasPassword">{{'newPassword' | i18n}}</label>
<label for="password" *ngIf="!hasPassword">{{'password' | i18n}}</label> <label for="password" *ngIf="!hasPassword">{{'password' | i18n}}</label>
<input id="password" type="{{showPassword ? 'text' : 'password'}}" name="Password" <input id="password" type="{{showPassword ? 'text' : 'password'}}" name="Password"
class="monospaced" [(ngModel)]="password" appInputVerbatim> class="monospaced" [(ngModel)]="password" appInputVerbatim [readonly]="disableSend">
</div> </div>
<div class="action-buttons"> <div class="action-buttons" *ngIf="!disableSend">
<a class="row-btn" href="#" appStopClick appBlurClick <a class="row-btn" href="#" appStopClick appBlurClick
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePasswordVisible()"> appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePasswordVisible()">
<i class="fa fa-lg" [ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}" <i class="fa fa-lg" [ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"
@ -210,7 +219,8 @@
<div class="box-content"> <div class="box-content">
<div class="box-content-row" appBoxRow> <div class="box-content-row" appBoxRow>
<label for="notes">{{'notes' | i18n}}</label> <label for="notes">{{'notes' | i18n}}</label>
<textarea id="notes" name="Notes" rows="6" [(ngModel)]="send.notes"></textarea> <textarea id="notes" name="Notes" rows="6" [(ngModel)]="send.notes"
[readonly]="disableSend"></textarea>
</div> </div>
</div> </div>
<div class="box-footer"> <div class="box-footer">
@ -222,7 +232,8 @@
<div class="box-content"> <div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow> <div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="disableSend">{{'sendDisableDesc' | i18n}}</label> <label for="disableSend">{{'sendDisableDesc' | i18n}}</label>
<input id="disableSend" type="checkbox" name="DisableSend" [(ngModel)]="send.disabled"> <input id="disableSend" type="checkbox" name="DisableSend" [(ngModel)]="send.disabled"
[disabled]="disableSend">
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,18 +8,22 @@
<i class="fa fa-search"></i> <i class="fa fa-search"></i>
</div> </div>
<div class="right"> <div class="right">
<button appBlurClick (click)="addSend()" appA11yTitle="{{'addSend' | i18n}}"> <button appBlurClick (click)="addSend()" appA11yTitle="{{'addSend' | i18n}}" [disabled]="disableSend">
<i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i> <i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i>
</button> </button>
</div> </div>
</header> </header>
<content> <content [ngClass]="{'flex' : disableSend, 'tab-page' : disableSend}">
<app-callout type="warning" title="{{'sendDisabled' | i18n}}" *ngIf="disableSend">
{{'sendDisabledWarning' | i18n}}
</app-callout>
<div class="no-items" *ngIf="(!sends || !sends.length) && !showSearching()"> <div class="no-items" *ngIf="(!sends || !sends.length) && !showSearching()">
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded"></i> <i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded"></i>
<ng-container *ngIf="loaded"> <ng-container *ngIf="loaded">
<i class="fa fa-frown-o fa-4x"></i> <i class="fa fa-frown-o fa-4x"></i>
<p>{{'noItemsInList' | i18n}}</p> <p>{{'noItemsInList' | i18n}}</p>
<button (click)="addSend()" class="btn block primary link">{{'addSend' | i18n}}</button> <button (click)="addSend()" class="btn block primary link"
[disabled]="disableSend">{{'addSend' | i18n}}</button>
</ng-container> </ng-container>
</div> </div>
<ng-container *ngIf="sends && sends.length && !showSearching()"> <ng-container *ngIf="sends && sends.length && !showSearching()">
@ -52,9 +56,9 @@
<div class="flex-right">{{sends.length}}</div> <div class="flex-right">{{sends.length}}</div>
</div> </div>
<div class="box-content"> <div class="box-content">
<app-send-list [sends]="sends" title="{{'editItem' | i18n}}" (onSelected)="selectSend($event)" <app-send-list [sends]="sends" title="{{'editItem' | i18n}}" [disabledByPolicy]="disableSend"
(onCopySendLink)="copy($event)" (onRemovePassword)="removePassword($event)" (onSelected)="selectSend($event)" (onCopySendLink)="copy($event)"
(onDeleteSend)="delete($event)"></app-send-list> (onRemovePassword)="removePassword($event)" (onDeleteSend)="delete($event)"></app-send-list>
</div> </div>
</div> </div>
</ng-container> </ng-container>
@ -64,9 +68,9 @@
</div> </div>
<div class="box list full-list" *ngIf="filteredSends && filteredSends.length > 0"> <div class="box list full-list" *ngIf="filteredSends && filteredSends.length > 0">
<div class="box-content"> <div class="box-content">
<app-send-list [sends]="filteredSends" title="{{'editItem' | i18n}}" (onSelected)="selectSend($event)" <app-send-list [sends]="filteredSends" title="{{'editItem' | i18n}}" [disabledByPolicy]="disableSend"
(onCopySendLink)="copy($event)" (onRemovePassword)="removePassword($event)" (onSelected)="selectSend($event)" (onCopySendLink)="copy($event)"
(onDeleteSend)="delete($event)"> (onRemovePassword)="removePassword($event)" (onDeleteSend)="delete($event)">
</app-send-list> </app-send-list>
</div> </div>
</div> </div>

View File

@ -125,9 +125,19 @@ export class SendGroupingsComponent extends BaseSendComponent {
} }
async addSend() { async addSend() {
if (this.disableSend) {
return;
}
this.router.navigate(['/add-send']); this.router.navigate(['/add-send']);
} }
async removePassword(s: SendView): Promise<boolean> {
if (this.disableSend) {
return;
}
super.removePassword(s);
}
showSearching() { showSearching() {
return this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText)); return this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText));
} }

View File

@ -11,17 +11,20 @@
<i class="fa fa-search"></i> <i class="fa fa-search"></i>
</div> </div>
<div class="right"> <div class="right">
<button appBlurClick (click)="addSend()" appA11yTitle="{{'addSend' | i18n}}"> <button appBlurClick (click)="addSend()" appA11yTitle="{{'addSend' | i18n}}" [disabled]="disableSend">
<i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i> <i class="fa fa-plus fa-lg fa-fw" aria-hidden="true"></i>
</button> </button>
</div> </div>
</header> </header>
<content> <content [ngClass]="{'flex' : disableSend}">
<app-callout type="warning" title="{{'sendDisabled' | i18n}}" *ngIf="disableSend">
{{'sendDisabledWarning' | i18n}}
</app-callout>
<div class="no-items" *ngIf="!filteredSends.length"> <div class="no-items" *ngIf="!filteredSends.length">
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i> <i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
<ng-container *ngIf="loaded"> <ng-container *ngIf="loaded">
<p>{{'noItemsInList' | i18n}}</p> <p>{{'noItemsInList' | i18n}}</p>
<button (click)="addSend()" class="btn block primary link"> <button (click)="addSend()" class="btn block primary link" [disabled]="disableSend">
{{'addSend' | i18n}} {{'addSend' | i18n}}
</button> </button>
</ng-container> </ng-container>
@ -32,9 +35,9 @@
<span class="flex-right">{{filteredSends.length}}</span> <span class="flex-right">{{filteredSends.length}}</span>
</div> </div>
<div class="box-content"> <div class="box-content">
<app-send-list [sends]="filteredSends" title="{{'editItem' | i18n}}" (onSelected)="selectSend($event)" <app-send-list [sends]="filteredSends" title="{{'editItem' | i18n}}" [disabledByPolicy]="disableSend"
(onCopySendLink)="copy($event)" (onRemovePassword)="removePassword($event)" (onSelected)="selectSend($event)" (onCopySendLink)="copy($event)"
(onDeleteSend)="delete($event)"> (onRemovePassword)="removePassword($event)" (onDeleteSend)="delete($event)">
</app-send-list> </app-send-list>
</div> </div>
</div> </div>

View File

@ -132,9 +132,19 @@ export class SendTypeComponent extends BaseSendComponent {
} }
async addSend() { async addSend() {
if (this.disableSend) {
return;
}
this.router.navigate(['/add-send'], { queryParams: { type: this.type } }); this.router.navigate(['/add-send'], { queryParams: { type: this.type } });
} }
async removePassword(s: SendView): Promise<boolean> {
if (this.disableSend) {
return;
}
super.removePassword(s);
}
back() { back() {
(window as any).routeDirection = 'b'; (window as any).routeDirection = 'b';
this.location.back(); this.location.back();