1
0
mirror of https://github.com/bitwarden/web synced 2025-12-06 00:03:28 +00:00

Compare commits

..

15 Commits

Author SHA1 Message Date
Hinton
ac8bd7292e Wrap connector calls in try to handle offline key connector 2022-01-11 20:23:45 +01:00
Hinton
2a4f6415d6 Changed methods to be protected 2022-01-11 20:16:06 +01:00
Hinton
91eb60ebef Fix linting 2022-01-11 20:14:37 +01:00
Hinton
29476f6744 Refactor 2022-01-11 20:05:10 +01:00
Hinton
ff7151fbbd Add support for setting keys 2022-01-11 19:45:34 +01:00
Hinton
4415720f3f Fix linting 2022-01-11 17:54:59 +01:00
Hinton
c49a9aa330 Only run it on cloud 2022-01-11 16:59:47 +01:00
Hinton
189b4437d4 Move logic to web, remove debug statements 2022-01-11 16:51:46 +01:00
Hinton
d22f17fc81 Update connector with changes from Kyle 2022-01-10 16:36:46 +01:00
Hinton
6efe992680 Undo some changes prettier made 2022-01-10 16:34:36 +01:00
Hinton
a1cde3c820 Merge branch 'master' of github.com:bitwarden/web into feature/cme-connector 2022-01-10 16:25:17 +01:00
Hinton
79b6f3595e Merge commit '56477eb39cfd8a73c9920577d24d75fed36e2cf5' into feature/cme-connector 2022-01-10 16:25:01 +01:00
Hinton
d0c0db70c5 Apply prettier 2022-01-10 16:24:38 +01:00
Hinton
f05b9439cc Merge commit '2b0a9d995e0147601ca8ae4778434a19354a60c2' into feature/cme-connector 2022-01-10 16:23:59 +01:00
Hinton
371c21553c WIP 2021-11-26 23:13:08 +01:00
16 changed files with 285 additions and 43 deletions

View File

@@ -27,13 +27,13 @@ jobs:
ref: version_bump_${{ github.event.inputs.version_number }}
- name: Bump Version - package.json
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
uses: bitwarden/gh-actions/version-bump@0c263b3963211ccaf5804313c3b3a0bcc52d4b19
with:
version: ${{ github.event.inputs.version_number }}
file_path: "./package.json"
- name: Bump Version - package-lock.json
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
uses: bitwarden/gh-actions/version-bump@0c263b3963211ccaf5804313c3b3a0bcc52d4b19
with:
version: ${{ github.event.inputs.version_number }}
file_path: "./package-lock.json"

View File

@@ -3,6 +3,10 @@ const { AngularWebpackPlugin } = require("@ngtools/webpack");
const webpackConfig = require("../webpack.config");
webpackConfig.entry["app/main"] = "./bitwarden_license/src/app/main.ts";
webpackConfig.plugins[webpackConfig.plugins.length - 1].entryModule = "bitwarden_license/src/app/app.module#AppModule";
webpackConfig.plugins[webpackConfig.plugins.length - 1] = new AngularWebpackPlugin({
tsConfigPath: "tsconfig.json",
entryModule: "bitwarden_license/src/app/app.module#AppModule",
sourceMap: true,
});
module.exports = webpackConfig;

View File

@@ -30,24 +30,7 @@ function loadConfig(configName) {
}
}
function generateSubstitutions(configObj) {
let result = {};
Object.keys(configObj.compileFlags ?? {}).forEach(key => {
result = { ...result, ...generateSubstitutionsForFlag(key) };
});
return result;
}
function generateSubstitutionsForFlag(flagName) {
return {
"featureFlag: (.*)[\\s\\S]*?\\/\\/ endFeatureFlag": ""
};
}
module.exports = {
load,
log,
generateSubstitutions,
};

View File

@@ -7,8 +7,5 @@
"proxyIdentity": "http://localhost:33656",
"proxyEvents": "http://localhost:46273",
"proxyNotifications": "http://localhost:61840"
},
"compileFlags": {
"Test": false
}
}

View File

2
jslib

Submodule jslib updated: e4cd0af2f9...462a4d7c56

View File

@@ -4,6 +4,7 @@ import { ToastrModule } from "ngx-toastr";
import { BroadcasterMessagingService } from "../../services/broadcasterMessaging.service";
import { HtmlStorageService } from "../../services/htmlStorage.service";
import { I18nService } from "../../services/i18n.service";
import { KeyConnectorService } from "../../services/keyConnector.service";
import { MemoryStorageService } from "../../services/memoryStorage.service";
import { PasswordRepromptService } from "../../services/passwordReprompt.service";
import { StateService } from "../../services/state.service";
@@ -40,6 +41,7 @@ import { EventService as EventLoggingServiceAbstraction } from "jslib-common/abs
import { FolderService as FolderServiceAbstraction } from "jslib-common/abstractions/folder.service";
import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service";
import { ImportService as ImportServiceAbstraction } from "jslib-common/abstractions/import.service";
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "jslib-common/abstractions/keyConnector.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service";
import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service";
@@ -190,6 +192,10 @@ export function initFactory(
provide: PasswordRepromptServiceAbstraction,
useClass: PasswordRepromptService,
},
{
provide: KeyConnectorServiceAbstraction,
useClass: KeyConnectorService,
},
],
})
export class ServicesModule {}

View File

@@ -587,7 +587,6 @@
id="idEmail"
class="form-control"
type="text"
inputmode="email"
name="Identity.Email"
[(ngModel)]="cipher.identity.email"
appInputVerbatim
@@ -600,7 +599,6 @@
id="idPhone"
class="form-control"
type="text"
inputmode="tel"
name="Identity.Phone"
[(ngModel)]="cipher.identity.phone"
[disabled]="cipher.isDeleted || viewOnly"

View File

@@ -37,9 +37,6 @@ import { SyncService } from "jslib-common/abstractions/sync.service";
import { TokenService } from "jslib-common/abstractions/token.service";
import { ModalService } from "jslib-angular/services/modal.service";
// featureFlag: Test
import { TestService } from 'jslib-common/services/test.service';
// endFeatureFlag
const BroadcasterSubscriptionId = "VaultComponent";
@@ -92,12 +89,10 @@ export class VaultComponent implements OnInit, OnDestroy {
private ngZone: NgZone,
private stateService: StateService,
private organizationService: OrganizationService,
private providerService: ProviderService,
/* featureFlag: Test */private testService: TestService, // endFeatureFlag
private providerService: ProviderService
) {}
async ngOnInit() {
/** featureFlag: Test */ this.testService.Print("test service was loaded"); // endFeatureFlag
this.showVerifyEmail = !(await this.tokenService.getEmailVerified());
this.showBrowserOutdated = window.navigator.userAgent.indexOf("MSIE") !== -1;
this.trashCleanupWarning = this.i18nService.t(

12
src/connectors/cme.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<title>Bitwarden CME Connector</title>
</head>
<body></body>
</html>

135
src/connectors/cme.ts Normal file
View File

@@ -0,0 +1,135 @@
import { KeyConnectorUserKeyResponse } from "jslib-common/models/response/keyConnectorUserKeyResponse";
import { b64Decode, getQsParam } from "./common";
document.addEventListener("DOMContentLoaded", () => {
init();
});
let parentUrl: string = null;
let parentOrigin: string = null;
let sentSuccess = false;
async function init() {
await start();
onMessage();
}
async function start() {
sentSuccess = false;
const data = getQsParam("data");
if (!data) {
error("No data.");
return;
}
parentUrl = getQsParam("parent");
if (!parentUrl) {
error("No parent.");
return;
} else {
parentUrl = decodeURIComponent(parentUrl);
parentOrigin = new URL(parentUrl).origin;
}
let decodedData: any;
try {
decodedData = JSON.parse(b64Decode(data));
} catch (e) {
error("Cannot parse data.");
return;
}
const keyConnectorUrl = new URL(decodedData.url);
const bearerAccessToken = decodedData.token;
const operation = decodedData.operation;
const key = decodedData.key;
if (keyConnectorUrl.hostname === "vault.bitwarden.com") {
error("Invalid hostname.");
}
if (operation === "get") {
const getRequest = new Request(keyConnectorUrl.href + "user-keys", {
cache: "no-store",
method: "GET",
headers: new Headers({
Accept: "application/json",
Authorization: "Bearer " + bearerAccessToken,
}),
});
getRequest.headers.set("Cache-Control", "no-store");
getRequest.headers.set("Pragma", "no-cache");
try {
const response = await fetch(getRequest);
if (response.status !== 200) {
throw new Error();
}
success(new KeyConnectorUserKeyResponse(await response.json()));
} catch {
error("Error getting key");
return;
}
} else if (operation === "post") {
const postRequest = new Request(keyConnectorUrl.href + "user-keys", {
cache: "no-store",
method: "POST",
headers: new Headers({
Accept: "application/json",
Authorization: "Bearer " + bearerAccessToken,
"Content-Type": "application/json; charset=utf-8",
}),
body: JSON.stringify({ key: key }),
});
try {
const response = await fetch(postRequest);
if (response.status !== 200) {
throw new Error();
}
} catch {
error("Error posting key");
return;
}
success(null);
} else {
// TODO: put operation
error("Unsupported operation.");
}
}
function onMessage() {
window.addEventListener(
"message",
(event) => {
if (!event.origin || event.origin === "" || event.origin !== parentOrigin) {
return;
}
if (event.data === "start") {
start();
}
},
false
);
}
function error(message: string) {
parent.postMessage("error|" + message, parentUrl);
}
function success(response: KeyConnectorUserKeyResponse) {
if (sentSuccess) {
return;
}
parent.postMessage(
"success|" + (response != null && response.key != null ? response.key : ""),
parentUrl
);
sentSuccess = true;
}
function info(message: string | object) {
parent.postMessage("info|" + JSON.stringify(message), parentUrl);
}

View File

@@ -3445,7 +3445,7 @@
"message": "Αποσύνδεση SSO"
},
"unlinkSsoConfirmation": {
"message": "Είστε βέβαιοι ότι θέλετε να αποσυνδέσετε το SSO για αυτόν τον οργανισμό;"
"message": "Are you sure you want to unlink SSO for this organization?"
},
"linkSso": {
"message": "Σύνδεσμος SSO"

View File

@@ -2078,7 +2078,7 @@
"message": "Samodzielnie hostowane środowisko (opcjonalnie)"
},
"usersGetPremium": {
"message": "Użytkownicy uzyskują dostęp do kont Premium"
"message": "Użytkownicy uzyskują dostęp do funkcji Premium"
},
"controlAccessWithGroups": {
"message": "Kontroluj dostęp z użyciem grup użytkowników"
@@ -4504,7 +4504,7 @@
"message": "Plan Bitwarden Families zawiera"
},
"sponsoredFamiliesPremiumAccess": {
"message": "Konto Premium dla maksymalnie 6 użytkowników"
"message": "Dostęp premium dla maksymalnie 6 użytkowników"
},
"sponsoredFamiliesSharedCollections": {
"message": "Udostępnione kolekcje dla sekretów rodziny"
@@ -4597,7 +4597,7 @@
"message": "Utworzono sponsorowanie"
},
"revoke": {
"message": "Unieważnij"
"message": "Odwołaj"
},
"emailSent": {
"message": "Wiadomość została wysłana"

View File

@@ -30,6 +30,12 @@
width: 100%;
}
#cme_iframe {
border: none;
height: 0;
width: 0;
}
.list-group-2fa {
.logo-2fa {
min-width: 100px;

View File

@@ -0,0 +1,105 @@
import { Injectable } from "@angular/core";
import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { TokenService } from "jslib-common/abstractions/token.service";
import { CMEIFrame } from "jslib-common/misc/cme_iframe";
import { KeyConnectorUserKeyRequest } from "jslib-common/models/request/keyConnectorUserKeyRequest";
import { KeyConnectorUserKeyResponse } from "jslib-common/models/response/keyConnectorUserKeyResponse";
import { KeyConnectorService as BaseKeyConnectorService } from "jslib-common/services/keyConnector.service";
@Injectable()
export class KeyConnectorService extends BaseKeyConnectorService {
constructor(
stateService: StateService,
cryptoFunctionService: CryptoFunctionService,
cryptoService: CryptoService,
apiService: ApiService,
tokenService: TokenService,
logService: LogService,
organizationService: OrganizationService,
private environmentService: EnvironmentService,
private platformUtilsService: PlatformUtilsService
) {
super(
stateService,
cryptoFunctionService,
cryptoService,
apiService,
tokenService,
logService,
organizationService
);
}
protected async getUserKeyFromKeyConnector(url: string): Promise<KeyConnectorUserKeyResponse> {
if (this.platformUtilsService.isSelfHost()) {
return super.getUserKeyFromKeyConnector(url);
}
const frame = this.createIframe();
frame.frame.initGet(await this.apiService.getActiveBearerToken(), url);
return frame.promise.then((key: string) => new KeyConnectorUserKeyResponse({ Key: key }));
}
protected async postUserKeyToKeyConnector(
url: string,
request: KeyConnectorUserKeyRequest
): Promise<void> {
if (this.platformUtilsService.isSelfHost()) {
return super.postUserKeyToKeyConnector(url, request);
}
const frame = this.createIframe();
frame.frame.initPost(await this.apiService.getActiveBearerToken(), url, request.key);
// tslint:disable-next-line
return frame.promise.then(() => {});
}
private createIframe(): { frame: CMEIFrame; promise: Promise<string> } {
const el = this.createIframeElement();
const webVaultUrl = this.environmentService.getWebVaultUrl();
let iframe: CMEIFrame;
const promise: Promise<string> = new Promise(async (resolve) => {
iframe = new CMEIFrame(
window,
webVaultUrl,
resolve,
(error: string) => {
this.platformUtilsService.showToast("error", null, error);
},
(info: string) => {
this.logService.info(info);
}
);
});
promise.finally(() => el.remove());
return {
frame: iframe,
promise: promise,
};
}
private createIframeElement() {
const el = document.createElement("iframe");
el.id = "cme_iframe";
document.body.appendChild(el);
return el;
}
}

View File

@@ -17,9 +17,6 @@ const NODE_ENV = process.env.NODE_ENV == null ? "development" : process.env.NODE
const envConfig = config.load(ENV);
config.log(envConfig);
const fileReplacements = {};
const substitutions = config.generateSubstitutions(envConfig);
const moduleRules = [
{
test: /\.ts$/,
@@ -34,7 +31,7 @@ const moduleRules = [
test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
exclude: /loading(|-white).svg/,
generator: {
filename: "fonts/[name][ext]",
filename: "fonts/[name].[ext]",
},
type: "asset/resource",
},
@@ -42,7 +39,7 @@ const moduleRules = [
test: /\.(jpe?g|png|gif|svg|webp|avif)$/i,
exclude: /.*(fontawesome-webfont)\.svg/,
generator: {
filename: "images/[name][ext]",
filename: "images/[name].[ext]",
},
type: "asset/resource",
},
@@ -115,6 +112,11 @@ const plugins = [
filename: "captcha-mobile-connector.html",
chunks: ["connectors/captcha"],
}),
new HtmlWebpackPlugin({
template: "./src/connectors/cme.html",
filename: "cme-connector.html",
chunks: ["connectors/cme"],
}),
new CopyWebpackPlugin({
patterns: [
{ from: "./src/.nojekyll" },
@@ -158,8 +160,6 @@ const plugins = [
tsConfigPath: "tsconfig.json",
entryModule: "src/app/app.module#AppModule",
sourceMap: true,
fileReplacements: fileReplacements,
substitutions: substitutions,
}),
];
@@ -226,6 +226,7 @@ const webpackConfig = {
"connectors/duo": "./src/connectors/duo.ts",
"connectors/sso": "./src/connectors/sso.ts",
"connectors/captcha": "./src/connectors/captcha.ts",
"connectors/cme": "./src/connectors/cme.ts",
theme_head: "./src/theme.js",
},
externals: {