[PM-1060] Added new forwarder (Forward Email <https://forwardemail.net>) (#4809)
* Added new forwarder (Forward Email <https://forwardemail.net>) * fix: fixed Basic authorization header * fix: fixed returned email value * feat: added verbose message for end-users (e.g. "Not Found" vs. "Domain does not exist on your account." (automatically localized with i18n for user) * fix: fixed Buffer.from to Utils.fromBufferToB64 * fix: fixed fromBufferToB64 to fromUtf8ToB64 * Remove try-catch to properly display api errors --------- Co-authored-by: Daniel James Smith <djsmith@web.de>
This commit is contained in:
parent
ba5e890e86
commit
d18b45a87e
|
@ -405,6 +405,28 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container *ngIf="usernameOptions.forwardedService === 'forwardemail'">
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="forwardemail-accessToken">{{ "apiAccessToken" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="forwardemail-accessToken"
|
||||||
|
type="password"
|
||||||
|
name="ForwardEmailAccessToken"
|
||||||
|
[(ngModel)]="usernameOptions.forwardedForwardEmailApiToken"
|
||||||
|
(blur)="saveUsernameOptions()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="forwardemail-domain">{{ "domainName" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="forwardemail-domain"
|
||||||
|
type="text"
|
||||||
|
name="ForwardEmailDomain"
|
||||||
|
[(ngModel)]="usernameOptions.forwardedForwardEmailDomain"
|
||||||
|
(blur)="saveUsernameOptions()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" *ngIf="usernameOptions.type === 'subaddress'">
|
<div class="box" *ngIf="usernameOptions.type === 'subaddress'">
|
||||||
|
|
|
@ -432,6 +432,28 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container *ngIf="usernameOptions.forwardedService === 'forwardemail'">
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="forwardemail-accessToken">{{ "apiAccessToken" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="forwardemail-accessToken"
|
||||||
|
type="password"
|
||||||
|
name="ForwardEmailAccessToken"
|
||||||
|
[(ngModel)]="usernameOptions.forwardedForwardEmailApiToken"
|
||||||
|
(blur)="saveUsernameOptions()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="forwardemail-domain">{{ "domainName" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="forwardemail-domain"
|
||||||
|
type="text"
|
||||||
|
name="ForwardEmailDomain"
|
||||||
|
[(ngModel)]="usernameOptions.forwardedForwardEmailDomain"
|
||||||
|
(blur)="saveUsernameOptions()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" *ngIf="usernameOptions.type === 'subaddress'" [hidden]="!showOptions">
|
<div class="box" *ngIf="usernameOptions.type === 'subaddress'" [hidden]="!showOptions">
|
||||||
|
|
|
@ -343,6 +343,28 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row" *ngIf="usernameOptions.forwardedService === 'forwardemail'">
|
||||||
|
<div class="form-group col-4">
|
||||||
|
<label for="forwardemail-apikey">{{ "apiAccessToken" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="forwardemail-apikey"
|
||||||
|
class="form-control"
|
||||||
|
type="password"
|
||||||
|
[(ngModel)]="usernameOptions.forwardedForwardEmailApiToken"
|
||||||
|
(blur)="saveUsernameOptions()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-4">
|
||||||
|
<label for="forwardemail-domain">{{ "domainName" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="forwardemail-domain"
|
||||||
|
class="form-control"
|
||||||
|
type="text"
|
||||||
|
[(ngModel)]="usernameOptions.forwardedForwardEmailDomain"
|
||||||
|
(blur)="saveUsernameOptions()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div class="row" *ngIf="usernameOptions.type === 'subaddress'">
|
<div class="row" *ngIf="usernameOptions.type === 'subaddress'">
|
||||||
<div class="form-group col-4">
|
<div class="form-group col-4">
|
||||||
|
|
|
@ -280,6 +280,7 @@ const devServer =
|
||||||
https://quack.duckduckgo.com/api/email/addresses
|
https://quack.duckduckgo.com/api/email/addresses
|
||||||
https://app.anonaddy.com/api/v1/aliases
|
https://app.anonaddy.com/api/v1/aliases
|
||||||
https://api.fastmail.com
|
https://api.fastmail.com
|
||||||
|
https://api.forwardemail.net
|
||||||
http://localhost:5000
|
http://localhost:5000
|
||||||
;object-src
|
;object-src
|
||||||
'self'
|
'self'
|
||||||
|
|
|
@ -242,6 +242,7 @@ export class GeneratorComponent implements OnInit {
|
||||||
{ name: "Fastmail", value: "fastmail", validForSelfHosted: true },
|
{ name: "Fastmail", value: "fastmail", validForSelfHosted: true },
|
||||||
{ name: "Firefox Relay", value: "firefoxrelay", validForSelfHosted: false },
|
{ name: "Firefox Relay", value: "firefoxrelay", validForSelfHosted: false },
|
||||||
{ name: "SimpleLogin", value: "simplelogin", validForSelfHosted: true },
|
{ name: "SimpleLogin", value: "simplelogin", validForSelfHosted: true },
|
||||||
|
{ name: "Forward Email", value: "forwardemail", validForSelfHosted: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
this.usernameOptions = await this.usernameGenerationService.getOptions();
|
this.usernameOptions = await this.usernameGenerationService.getOptions();
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { ApiService } from "../../../../abstractions/api.service";
|
||||||
|
import { Utils } from "../../../../misc/utils";
|
||||||
|
|
||||||
|
import { Forwarder } from "./forwarder";
|
||||||
|
import { ForwarderOptions } from "./forwarder-options";
|
||||||
|
|
||||||
|
export class ForwardEmailForwarder implements Forwarder {
|
||||||
|
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
|
||||||
|
if (options.apiKey == null || options.apiKey === "") {
|
||||||
|
throw "Invalid Forward Email API key.";
|
||||||
|
}
|
||||||
|
if (options.forwardemail?.domain == null || options.forwardemail.domain === "") {
|
||||||
|
throw "Invalid Forward Email domain.";
|
||||||
|
}
|
||||||
|
const requestInit: RequestInit = {
|
||||||
|
redirect: "manual",
|
||||||
|
cache: "no-store",
|
||||||
|
method: "POST",
|
||||||
|
headers: new Headers({
|
||||||
|
Authorization: "Basic " + Utils.fromUtf8ToB64(options.apiKey + ":"),
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const url = `https://api.forwardemail.net/v1/domains/${options.forwardemail.domain}/aliases`;
|
||||||
|
requestInit.body = JSON.stringify({
|
||||||
|
labels: options.website,
|
||||||
|
description:
|
||||||
|
(options.website != null ? "Website: " + options.website + ". " : "") +
|
||||||
|
"Generated by Bitwarden.",
|
||||||
|
});
|
||||||
|
const request = new Request(url, requestInit);
|
||||||
|
const response = await apiService.nativeFetch(request);
|
||||||
|
if (response.status === 200 || response.status === 201) {
|
||||||
|
const json = await response.json();
|
||||||
|
return json?.name + "@" + (json?.domain?.name || options.forwardemail.domain);
|
||||||
|
}
|
||||||
|
if (response.status === 401) {
|
||||||
|
throw "Invalid Forward Email API key.";
|
||||||
|
}
|
||||||
|
const json = await response.json();
|
||||||
|
if (json?.message != null) {
|
||||||
|
throw "Forward Email error:\n" + json.message;
|
||||||
|
}
|
||||||
|
if (json?.error != null) {
|
||||||
|
throw "Forward Email error:\n" + json.error;
|
||||||
|
}
|
||||||
|
throw "Unknown Forward Email error occurred.";
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ export class ForwarderOptions {
|
||||||
website: string;
|
website: string;
|
||||||
fastmail = new FastmailForwarderOptions();
|
fastmail = new FastmailForwarderOptions();
|
||||||
anonaddy = new AnonAddyForwarderOptions();
|
anonaddy = new AnonAddyForwarderOptions();
|
||||||
|
forwardemail = new ForwardEmailForwarderOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FastmailForwarderOptions {
|
export class FastmailForwarderOptions {
|
||||||
|
@ -12,3 +13,7 @@ export class FastmailForwarderOptions {
|
||||||
export class AnonAddyForwarderOptions {
|
export class AnonAddyForwarderOptions {
|
||||||
domain: string;
|
domain: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ForwardEmailForwarderOptions {
|
||||||
|
domain: string;
|
||||||
|
}
|
||||||
|
|
|
@ -5,3 +5,4 @@ export { FirefoxRelayForwarder } from "./firefox-relay-forwarder";
|
||||||
export { Forwarder } from "./forwarder";
|
export { Forwarder } from "./forwarder";
|
||||||
export { ForwarderOptions } from "./forwarder-options";
|
export { ForwarderOptions } from "./forwarder-options";
|
||||||
export { SimpleLoginForwarder } from "./simple-login-forwarder";
|
export { SimpleLoginForwarder } from "./simple-login-forwarder";
|
||||||
|
export { ForwardEmailForwarder } from "./forward-email-forwarder";
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
DuckDuckGoForwarder,
|
DuckDuckGoForwarder,
|
||||||
FastmailForwarder,
|
FastmailForwarder,
|
||||||
FirefoxRelayForwarder,
|
FirefoxRelayForwarder,
|
||||||
|
ForwardEmailForwarder,
|
||||||
Forwarder,
|
Forwarder,
|
||||||
ForwarderOptions,
|
ForwarderOptions,
|
||||||
SimpleLoginForwarder,
|
SimpleLoginForwarder,
|
||||||
|
@ -22,6 +23,7 @@ const DefaultOptions = {
|
||||||
catchallType: "random",
|
catchallType: "random",
|
||||||
forwardedService: "",
|
forwardedService: "",
|
||||||
forwardedAnonAddyDomain: "anonaddy.me",
|
forwardedAnonAddyDomain: "anonaddy.me",
|
||||||
|
forwardedForwardEmailDomain: "hideaddress.net",
|
||||||
};
|
};
|
||||||
|
|
||||||
export class UsernameGenerationService implements UsernameGenerationServiceAbstraction {
|
export class UsernameGenerationService implements UsernameGenerationServiceAbstraction {
|
||||||
|
@ -137,6 +139,10 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr
|
||||||
} else if (o.forwardedService === "duckduckgo") {
|
} else if (o.forwardedService === "duckduckgo") {
|
||||||
forwarder = new DuckDuckGoForwarder();
|
forwarder = new DuckDuckGoForwarder();
|
||||||
forwarderOptions.apiKey = o.forwardedDuckDuckGoToken;
|
forwarderOptions.apiKey = o.forwardedDuckDuckGoToken;
|
||||||
|
} else if (o.forwardedService === "forwardemail") {
|
||||||
|
forwarder = new ForwardEmailForwarder();
|
||||||
|
forwarderOptions.apiKey = o.forwardedForwardEmailApiToken;
|
||||||
|
forwarderOptions.forwardemail.domain = o.forwardedForwardEmailDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forwarder == null) {
|
if (forwarder == null) {
|
||||||
|
|
Loading…
Reference in New Issue