1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-13 23:03:32 +00:00

SG-680 - Domain Add Edit Dialog - (1) Random generation of DNS TXT Record now working (2) DNS TXT Record Copyable (3) Additional translations added (4) Info callout added

This commit is contained in:
Jared Snider
2022-12-06 18:43:21 -05:00
parent 6bdc78385b
commit d58c93e517
3 changed files with 83 additions and 11 deletions

View File

@@ -5537,5 +5537,24 @@
}, },
"verifyDomain": { "verifyDomain": {
"message": "Verify domain" "message": "Verify domain"
},
"copyDnsTxtRecord": {
"message": "Copy DNS TXT record"
},
"dnsTxtRecord": {
"message": "DNS TXT record"
},
"dnsTxtRecordInputHint": {
"message": "Copy and paste the TXT record into your DNS Provider."
},
"domainNameInputHint": {
"message": "Example: mydomain.com. Subdomains require separate entries to be verified."
},
"automaticDomainVerification": {
"message": "Automatic Domain Verification"
},
"automaticDomainVerificationProcess": {
"message": "Bitwarden will attempt to verify the domain 3 times during the first 72 hours. If the domain cant be verified, check the DNS record in your host and manually verify."
} }
} }

View File

@@ -3,19 +3,30 @@
<div bitDialogContent> <div bitDialogContent>
<form [formGroup]="domainForm"> <form [formGroup]="domainForm">
<bit-form-field> <bit-form-field>
<bit-label>Domain name</bit-label> <bit-label>{{ "domainName" | i18n }}</bit-label>
<input bitInput formControlName="domainName" /> <input bitInput formControlName="domainName" />
<bit-hint <bit-hint>{{ "domainNameInputHint" | i18n }}</bit-hint>
>Example: mydomain.com. Subdomains require separate entries to be verified.</bit-hint
>
</bit-form-field> </bit-form-field>
<bit-form-field> <bit-form-field>
<bit-label>DNS TXT record</bit-label> <bit-label>{{ "dnsTxtRecord" | i18n }}</bit-label>
<input bitInput formControlName="txt" /> <input bitInput formControlName="txt" />
<bit-hint>Copy and paste the TXT record into your DNS Provider.</bit-hint> <bit-hint>{{ "dnsTxtRecordInputHint" | i18n }}</bit-hint>
<button
type="button"
bitSuffix
bitButton
appA11yTitle="{{ 'copyDnsTxtRecord' | i18n }}"
(click)="copyDnsTxt()"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</bit-form-field> </bit-form-field>
</form> </form>
<bit-callout type="info" title="{{ 'automaticDomainVerification' | i18n }}">
{{ "automaticDomainVerificationProcess" | i18n }}
</bit-callout>
</div> </div>
<div bitDialogFooter class="tw-flex tw-flex-row tw-items-center tw-gap-2"> <div bitDialogFooter class="tw-flex tw-flex-row tw-items-center tw-gap-2">
<button bitButton buttonType="primary">{{ "verifyDomain" | i18n }}</button> <button bitButton buttonType="primary">{{ "verifyDomain" | i18n }}</button>

View File

@@ -1,9 +1,12 @@
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject, OnInit } from "@angular/core"; import { Component, Inject, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms"; import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { OrganizationDomainResponse } from "@bitwarden/common/abstractions/organization-domain/responses/organization-domain.response"; import { OrganizationDomainResponse } from "@bitwarden/common/abstractions/organization-domain/responses/organization-domain.response";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { Utils } from "@bitwarden/common/misc/utils";
export interface DomainAddEditDialogData { export interface DomainAddEditDialogData {
organizationId: string; organizationId: string;
orgDomain: OrganizationDomainResponse; orgDomain: OrganizationDomainResponse;
@@ -17,18 +20,57 @@ export class DomainAddEditDialogComponent implements OnInit {
dialogSize: "small" | "default" | "large" = "default"; dialogSize: "small" | "default" | "large" = "default";
disablePadding = false; disablePadding = false;
// TODO: custom validator for preventing https:// or www. on domainName
domainForm: FormGroup = this.formBuilder.group({ domainForm: FormGroup = this.formBuilder.group({
domainName: ["", [Validators.required]], domainName: ["", [Validators.required]],
txt: [""], txt: [{ value: null, disabled: true }],
}); });
get domainNameCtrl(): FormControl {
return this.domainForm.controls.domainName as FormControl;
}
get txtCtrl(): FormControl {
return this.domainForm.controls.txt as FormControl;
}
constructor( constructor(
public dialogRef: DialogRef, public dialogRef: DialogRef,
@Inject(DIALOG_DATA) public data: DomainAddEditDialogData, @Inject(DIALOG_DATA) public data: DomainAddEditDialogData,
private formBuilder: FormBuilder private formBuilder: FormBuilder,
private cryptoFunctionService: CryptoFunctionServiceAbstraction,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService
) {} ) {}
ngOnInit(): void { async ngOnInit(): Promise<void> {
// If we have data.orgDomain, then editing, otherwise creating new domain // If we have data.orgDomain, then editing, otherwise creating new domain
await this.populateForm();
}
async populateForm(): Promise<void> {
if (this.data.orgDomain) {
// Edit
this.domainForm.patchValue(this.data.orgDomain);
} else {
// Add
// Figuring out the proper length of our DNS TXT Record value was fun.
// DNS-Based Service Discovery RFC: https://www.ietf.org/rfc/rfc6763.txt; see section 6.1
// Google uses 43 chars for their TXT record value: https://support.google.com/a/answer/2716802
// So, chose a magic # of 33 bytes to achieve at least that once converted to base 64 (47 char length).
const generatedTxt = `bw=${Utils.fromBufferToB64(
await this.cryptoFunctionService.randomBytes(33)
)}`;
this.txtCtrl.setValue(generatedTxt);
}
}
copyDnsTxt() {
this.platformUtilsService.copyToClipboard(this.txtCtrl.value);
this.platformUtilsService.showToast(
"info",
null,
this.i18nService.t("valueCopied", this.i18nService.t("dnsTxtRecord"))
);
} }
} }