Finilization v1.1.0.0
This commit is contained in:
parent
2b3da349b0
commit
e10ba53c1a
|
@ -3,7 +3,7 @@
|
||||||
"solution": {
|
"solution": {
|
||||||
"name": "SPFx app dev Simple Password Vault",
|
"name": "SPFx app dev Simple Password Vault",
|
||||||
"id": "d091b369-f63d-459c-846e-5a4323e31745",
|
"id": "d091b369-f63d-459c-846e-5a4323e31745",
|
||||||
"version": "1.0.0.0",
|
"version": "1.1.0.0",
|
||||||
"includeClientSideAssets": true,
|
"includeClientSideAssets": true,
|
||||||
"skipFeatureDeployment": true,
|
"skipFeatureDeployment": true,
|
||||||
"isDomainIsolated": false,
|
"isDomainIsolated": false,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "sp-fx-app-dev-simple-password-vault",
|
"name": "sp-fx-app-dev-simple-password-vault",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
export interface IVaultData {
|
export interface IVaultData {
|
||||||
masterPW: string;
|
masterPW: string;
|
||||||
// username: string;
|
|
||||||
// password: string;
|
|
||||||
// note: string;
|
|
||||||
}
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
import * as CryptoJS from 'crypto-js';
|
import * as CryptoJS from 'crypto-js';
|
||||||
import { isNullOrEmpty, toBoolean } from '@spfxappdev/utility';
|
import { isNullOrEmpty, toBoolean } from '@spfxappdev/utility';
|
||||||
import { SessionStorage } from '@spfxappdev/storage';
|
import { SessionStorage } from '@spfxappdev/storage';
|
||||||
import { IVaultData } from '@src/models/IVaultData';
|
|
||||||
import { ModuleType } from '@src/models';
|
import { ModuleType } from '@src/models';
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,7 +70,7 @@ export class PasswordVaultService implements IPasswordVaultService {
|
||||||
|
|
||||||
this.cache = new SessionStorage(cacheSettings);
|
this.cache = new SessionStorage(cacheSettings);
|
||||||
this.isVaultOpen = toBoolean(this.cache.get(this.encryptedInstanceId));
|
this.isVaultOpen = toBoolean(this.cache.get(this.encryptedInstanceId));
|
||||||
let pwFromCache = this.cache.get(this.encryptedMasterPWInstanceId);
|
const pwFromCache = this.cache.get(this.encryptedMasterPWInstanceId);
|
||||||
this.encryptedMasterPw = isNullOrEmpty(pwFromCache) ? "" : pwFromCache;
|
this.encryptedMasterPw = isNullOrEmpty(pwFromCache) ? "" : pwFromCache;
|
||||||
|
|
||||||
if(!isNullOrEmpty(this.encryptedMasterPw)) {
|
if(!isNullOrEmpty(this.encryptedMasterPw)) {
|
||||||
|
@ -103,7 +102,7 @@ export class PasswordVaultService implements IPasswordVaultService {
|
||||||
public open(masterPW: string, encryptedMasterPW: string): boolean {
|
public open(masterPW: string, encryptedMasterPW: string): boolean {
|
||||||
const masterPWEncrypted = CryptoJS.HmacSHA256(masterPW, this.masterSecretKey);
|
const masterPWEncrypted = CryptoJS.HmacSHA256(masterPW, this.masterSecretKey);
|
||||||
|
|
||||||
if(masterPWEncrypted.toString() == encryptedMasterPW) {
|
if(masterPWEncrypted.toString() === encryptedMasterPW) {
|
||||||
this.isVaultOpen = true;
|
this.isVaultOpen = true;
|
||||||
this.cache.set(this.encryptedInstanceId, true);
|
this.cache.set(this.encryptedInstanceId, true);
|
||||||
this.encryptedMasterPw = this.encrypt(masterPW, this.masterSecretKey);
|
this.encryptedMasterPw = this.encrypt(masterPW, this.masterSecretKey);
|
||||||
|
@ -115,7 +114,6 @@ export class PasswordVaultService implements IPasswordVaultService {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public isOpen(): boolean {
|
public isOpen(): boolean {
|
||||||
return this.isVaultOpen;
|
return this.isVaultOpen;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,18 @@
|
||||||
"officeFabricIconFontName": "ProtectRestrict",
|
"officeFabricIconFontName": "ProtectRestrict",
|
||||||
"properties": {
|
"properties": {
|
||||||
"masterPW": "",
|
"masterPW": "",
|
||||||
"username": "",
|
"modules": [
|
||||||
"password": "",
|
{
|
||||||
"note": ""
|
"id": "718def44-3e0c-48bf-8843-ccabefece27e",
|
||||||
|
"data": "",
|
||||||
|
"type": "UserField"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "65025036-e345-43f7-9561-6d3026294b2e",
|
||||||
|
"data": "",
|
||||||
|
"type": "PasswordField"
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,15 +39,15 @@ export default class PasswordVaultWebPart extends SPFxAppDevClientSideWebPart<IP
|
||||||
Title: this.properties.Title,
|
Title: this.properties.Title,
|
||||||
passwordVaultService: this.passwordVaultService,
|
passwordVaultService: this.passwordVaultService,
|
||||||
masterPW: this.properties.masterPW,
|
masterPW: this.properties.masterPW,
|
||||||
// username: this.properties.username,
|
|
||||||
// password: this.properties.password,
|
|
||||||
// note: this.properties.note,
|
|
||||||
modules: this.properties.modules||[],
|
modules: this.properties.modules||[],
|
||||||
onTitleChanged: (title: string): void => {
|
onTitleChanged: (title: string): void => {
|
||||||
this.onTitleChanged(title);
|
this.onTitleChanged(title);
|
||||||
},
|
},
|
||||||
onVaultDataChanged: (encryptedMasterPw: string, modules: IModule[]): void => {
|
onVaultDataChanged: (encryptedMasterPw: string, modules: IModule[]): void => {
|
||||||
this.onVaultDataChanged(encryptedMasterPw, modules);
|
this.onVaultDataChanged(encryptedMasterPw, modules);
|
||||||
|
},
|
||||||
|
onVaultPasswordChanged: (encryptedMasterPw: string): void => {
|
||||||
|
this.onMasterPasswordChanged(encryptedMasterPw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -66,7 +66,7 @@ export default class PasswordVaultWebPart extends SPFxAppDevClientSideWebPart<IP
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.helper.functions.isNullOrEmpty(oldProps.username)) {
|
if (!this.helper.functions.isNullOrEmpty(oldProps.password)) {
|
||||||
this.properties.modules.push({
|
this.properties.modules.push({
|
||||||
id: Guid.newGuid().toString(),
|
id: Guid.newGuid().toString(),
|
||||||
type: ModuleType.PasswordField,
|
type: ModuleType.PasswordField,
|
||||||
|
@ -81,6 +81,23 @@ export default class PasswordVaultWebPart extends SPFxAppDevClientSideWebPart<IP
|
||||||
data: oldProps.note
|
data: oldProps.note
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.removePropertiesFromOldVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
private removePropertiesFromOldVersion(): void {
|
||||||
|
const oldProps: any = this.properties as any;
|
||||||
|
if (this.helper.functions.isset(oldProps.username)) {
|
||||||
|
delete oldProps.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.helper.functions.isset(oldProps.password)) {
|
||||||
|
delete oldProps.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.helper.functions.isset(oldProps.note)) {
|
||||||
|
delete oldProps.note;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getLogCategory(): string {
|
public getLogCategory(): string {
|
||||||
|
@ -94,9 +111,11 @@ export default class PasswordVaultWebPart extends SPFxAppDevClientSideWebPart<IP
|
||||||
public onVaultDataChanged(encryptedMasterPW: string, modules: IModule[]): void {
|
public onVaultDataChanged(encryptedMasterPW: string, modules: IModule[]): void {
|
||||||
this.properties.masterPW = encryptedMasterPW;
|
this.properties.masterPW = encryptedMasterPW;
|
||||||
this.properties.modules = modules;
|
this.properties.modules = modules;
|
||||||
// this.properties.note = vaultData.note;
|
this.removePropertiesFromOldVersion();
|
||||||
// this.properties.password = vaultData.password;
|
}
|
||||||
// this.properties.username = vaultData.username;
|
|
||||||
|
private onMasterPasswordChanged(encryptedMasterPW: string): void {
|
||||||
|
this.properties.masterPW = encryptedMasterPW;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onDispose(): void {
|
protected onDispose(): void {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styles from './PasswordVault.module.scss';
|
|
||||||
import * as strings from 'PasswordVaultWebPartStrings';
|
import * as strings from 'PasswordVaultWebPartStrings';
|
||||||
import { Label } from 'office-ui-fabric-react';
|
import { Label } from 'office-ui-fabric-react';
|
||||||
import { RichText } from "@pnp/spfx-controls-react/lib/RichText";
|
import { RichText } from "@pnp/spfx-controls-react/lib/RichText";
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styles from './PasswordVault.module.scss';
|
import { Callout, Icon, DirectionalHint, TextField, ITextField } from 'office-ui-fabric-react';
|
||||||
import { ActionButton, Callout, Icon, TooltipHost, DirectionalHint, TextField, ITextField } from 'office-ui-fabric-react';
|
|
||||||
import * as strings from 'PasswordVaultWebPartStrings';
|
import * as strings from 'PasswordVaultWebPartStrings';
|
||||||
import { getDeepOrDefault, issetDeep, isset } from '@spfxappdev/utility';
|
import { getDeepOrDefault, issetDeep, isset } from '@spfxappdev/utility';
|
||||||
|
|
||||||
|
@ -30,8 +29,6 @@ export default class PasswordField extends React.Component<IPasswordFieldProps,
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.renderEditMode();
|
return this.renderEditMode();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderDisplayMode(): JSX.Element {
|
public renderDisplayMode(): JSX.Element {
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
&::before {
|
&::before {
|
||||||
border-top-style: solid;
|
border-top-style: solid;
|
||||||
border-top-width: 2px;
|
border-top-width: 2px;
|
||||||
|
border-top-color: $ms-color-neutralTertiary;
|
||||||
content: "";
|
content: "";
|
||||||
height: 0;
|
height: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -49,6 +50,7 @@
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
|
border-color: $ms-color-neutralTertiary;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
line-height: 12px;
|
line-height: 12px;
|
||||||
|
@ -57,7 +59,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: opacity .3s ease-in-out;
|
transition: opacity .3s ease-in-out;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
background-color: #000;
|
background-color: $ms-color-neutralTertiary;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
|
|
||||||
|
@ -104,13 +106,19 @@
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.ms-CommandBar {
|
||||||
|
padding: 0 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:global {
|
:global {
|
||||||
|
|
||||||
.ql-editor
|
.ql-editor
|
||||||
{
|
{
|
||||||
padding: 0px !important;
|
padding: 8px 0 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ql-editor[contenteditable='true'] {
|
.ql-editor[contenteditable='true'] {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import styles from './PasswordVault.module.scss';
|
||||||
import { SPFxAppDevWebPartComponent, ISPFxAppDevWebPartComponentProps } from '@spfxappdev/framework';
|
import { SPFxAppDevWebPartComponent, ISPFxAppDevWebPartComponentProps } from '@spfxappdev/framework';
|
||||||
import PasswordVaultWebPart from '../PasswordVaultWebPart';
|
import PasswordVaultWebPart from '../PasswordVaultWebPart';
|
||||||
import { IPasswordVaultService } from '@src/services/PasswordVaultService';
|
import { IPasswordVaultService } from '@src/services/PasswordVaultService';
|
||||||
import { DefaultButton, IconButton, MessageBar, MessageBarType, PrimaryButton, TextField } from 'office-ui-fabric-react';
|
import { Dialog, DefaultButton, IconButton, MessageBar, MessageBarType, PrimaryButton, TextField, CommandBar, ICommandBarItemProps, DialogFooter, DialogType } from 'office-ui-fabric-react';
|
||||||
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
|
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
|
||||||
import * as strings from 'PasswordVaultWebPartStrings';
|
import * as strings from 'PasswordVaultWebPartStrings';
|
||||||
import AddNewModule from './AddNewModule';
|
import AddNewModule from './AddNewModule';
|
||||||
|
@ -13,12 +13,14 @@ import UserField from './UserField';
|
||||||
import PasswordField from './PasswordField';
|
import PasswordField from './PasswordField';
|
||||||
import NoteField from './NoteField';
|
import NoteField from './NoteField';
|
||||||
import { cloneDeep } from '@microsoft/sp-lodash-subset';
|
import { cloneDeep } from '@microsoft/sp-lodash-subset';
|
||||||
|
import '@spfxappdev/utility/lib/extensions/ArrayExtensions';
|
||||||
|
|
||||||
interface IPasswordVaultState {
|
interface IPasswordVaultState {
|
||||||
isVaultOpen: boolean;
|
isVaultOpen: boolean;
|
||||||
showWrongMasterInfo: boolean;
|
showWrongMasterInfo: boolean;
|
||||||
isSaveButtonDisabled: boolean;
|
isSaveButtonDisabled: boolean;
|
||||||
modules: IModule[];
|
modules: IModule[];
|
||||||
|
showChangePasswordDialog: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPasswordVaultProps extends ISPFxAppDevWebPartComponentProps<PasswordVaultWebPart> {
|
export interface IPasswordVaultProps extends ISPFxAppDevWebPartComponentProps<PasswordVaultWebPart> {
|
||||||
|
@ -26,10 +28,10 @@ export interface IPasswordVaultProps extends ISPFxAppDevWebPartComponentProps<Pa
|
||||||
masterPW?: string;
|
masterPW?: string;
|
||||||
onTitleChanged?(value: string): void;
|
onTitleChanged?(value: string): void;
|
||||||
onVaultDataChanged?(encryptedMaster: string, modules: IModule[]): void;
|
onVaultDataChanged?(encryptedMaster: string, modules: IModule[]): void;
|
||||||
|
onVaultPasswordChanged?(encryptedMaster: string): void;
|
||||||
modules?: IModule[];
|
modules?: IModule[];
|
||||||
}
|
}
|
||||||
//TODO:
|
|
||||||
// "Change Password" as command button, only if vault already have set a master password
|
|
||||||
|
|
||||||
export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVaultWebPart, IPasswordVaultProps, IPasswordVaultState> {
|
export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVaultWebPart, IPasswordVaultProps, IPasswordVaultState> {
|
||||||
|
|
||||||
|
@ -43,7 +45,8 @@ export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVa
|
||||||
isVaultOpen: this.isVaultOpen,
|
isVaultOpen: this.isVaultOpen,
|
||||||
showWrongMasterInfo: false,
|
showWrongMasterInfo: false,
|
||||||
isSaveButtonDisabled: this.helper.functions.isNullOrEmpty(this.props.masterPW),
|
isSaveButtonDisabled: this.helper.functions.isNullOrEmpty(this.props.masterPW),
|
||||||
modules: cloneDeep(this.props.modules)
|
modules: cloneDeep(this.props.modules),
|
||||||
|
showChangePasswordDialog: false
|
||||||
};
|
};
|
||||||
|
|
||||||
private get isVaultOpen(): boolean {
|
private get isVaultOpen(): boolean {
|
||||||
|
@ -61,6 +64,8 @@ export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVa
|
||||||
|
|
||||||
private enteredMasterPW: string = "";
|
private enteredMasterPW: string = "";
|
||||||
|
|
||||||
|
private repeatedEnteredMasterPW: string = "";
|
||||||
|
|
||||||
private encryptedModuleData: Record<string, string> = {};
|
private encryptedModuleData: Record<string, string> = {};
|
||||||
|
|
||||||
private decryptedModuleData: Record<string, string> = {};
|
private decryptedModuleData: Record<string, string> = {};
|
||||||
|
@ -152,26 +157,17 @@ export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVa
|
||||||
|
|
||||||
{showForm &&
|
{showForm &&
|
||||||
<>
|
<>
|
||||||
<MessageBar messageBarType={MessageBarType.warning}>
|
|
||||||
{strings.DontLoseMasterpasswordLabel}
|
{this.renderCommandBarButtons()}
|
||||||
</MessageBar>
|
{this.renderChangePasswordDialog()}
|
||||||
|
|
||||||
|
{this.isNewVault &&
|
||||||
|
<MessageBar messageBarType={MessageBarType.warning}>
|
||||||
|
{strings.DontLoseMasterpasswordLabel}
|
||||||
|
</MessageBar>
|
||||||
|
}
|
||||||
<div className='spfxappdev-grid'>
|
<div className='spfxappdev-grid'>
|
||||||
<div className="spfxappdev-grid-row">
|
{this.isNewVault && this.renderMasterPasswordControls()}
|
||||||
<div className="spfxappdev-grid-col spfxappdev-sm12">
|
|
||||||
<TextField
|
|
||||||
label={this.isNewVault ? strings.SetMasterPasswordLabel : strings.ChangeMasterPasswordLabel}
|
|
||||||
type="password"
|
|
||||||
required={this.isNewVault}
|
|
||||||
canRevealPassword={true}
|
|
||||||
onChange={(ev: any, newValue: string) => {
|
|
||||||
this.enteredMasterPW = newValue;
|
|
||||||
this.setState({
|
|
||||||
isSaveButtonDisabled: this.isSaveButtonDisabled()
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{this.state.modules.map((module: IModule, index: number): JSX.Element => {
|
{this.state.modules.map((module: IModule, index: number): JSX.Element => {
|
||||||
return this.renderModuleEditMode(module, index);
|
return this.renderModuleEditMode(module, index);
|
||||||
|
@ -187,31 +183,7 @@ export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVa
|
||||||
<div className="spfxappdev-grid-row grid-footer">
|
<div className="spfxappdev-grid-row grid-footer">
|
||||||
<div className="spfxappdev-grid-col spfxappdev-sm12">
|
<div className="spfxappdev-grid-col spfxappdev-sm12">
|
||||||
<PrimaryButton disabled={this.state.isSaveButtonDisabled} onClick={() => {
|
<PrimaryButton disabled={this.state.isSaveButtonDisabled} onClick={() => {
|
||||||
this.isNewVault = false;
|
this.onSaveButtonClick();
|
||||||
let encryptedMaster = this.currentMasterPW;
|
|
||||||
|
|
||||||
if(!this.enteredMasterPW.IsEmpty()) {
|
|
||||||
encryptedMaster = this.props.passwordVaultService.setMasterPassword(this.enteredMasterPW);
|
|
||||||
this.currentMasterPW = encryptedMaster;
|
|
||||||
}
|
|
||||||
|
|
||||||
const encryptedModules: IModule[] = [];
|
|
||||||
this.state.modules.forEach((module: IModule) => {
|
|
||||||
const encryptedValue: string = this.props.passwordVaultService.encryptModuleData(module.type, this.decryptedModuleData[module.id]);
|
|
||||||
|
|
||||||
encryptedModules.push({
|
|
||||||
id: module.id,
|
|
||||||
data: encryptedValue,
|
|
||||||
type: module.type
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.onVaultDataChanged(encryptedMaster, encryptedModules);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
modules: encryptedModules
|
|
||||||
});
|
|
||||||
|
|
||||||
}}>
|
}}>
|
||||||
{strings.SaveLabel}
|
{strings.SaveLabel}
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
|
@ -274,19 +246,19 @@ export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVa
|
||||||
|
|
||||||
<div className={styles["edit-container--content"]}>
|
<div className={styles["edit-container--content"]}>
|
||||||
|
|
||||||
{module.type == ModuleType.UserField &&
|
{module.type === ModuleType.UserField &&
|
||||||
<UserField defaultValue={this.decryptedModuleData[module.id]} tabIndex={index} onChange={(newVal: string) => {
|
<UserField defaultValue={this.decryptedModuleData[module.id]} tabIndex={index} onChange={(newVal: string) => {
|
||||||
this.decryptedModuleData[module.id] = newVal;
|
this.decryptedModuleData[module.id] = newVal;
|
||||||
}} isDisplayMode={false} />
|
}} isDisplayMode={false} />
|
||||||
}
|
}
|
||||||
|
|
||||||
{module.type == ModuleType.PasswordField &&
|
{module.type === ModuleType.PasswordField &&
|
||||||
<PasswordField defaultValue={this.decryptedModuleData[module.id]} tabIndex={index} onChange={(newVal: string) => {
|
<PasswordField defaultValue={this.decryptedModuleData[module.id]} tabIndex={index} onChange={(newVal: string) => {
|
||||||
this.decryptedModuleData[module.id] = newVal;
|
this.decryptedModuleData[module.id] = newVal;
|
||||||
}} isDisplayMode={false} />
|
}} isDisplayMode={false} />
|
||||||
}
|
}
|
||||||
|
|
||||||
{module.type == ModuleType.NoteField &&
|
{module.type === ModuleType.NoteField &&
|
||||||
<NoteField defaultValue={this.decryptedModuleData[module.id]} onChange={(newVal: string) => {
|
<NoteField defaultValue={this.decryptedModuleData[module.id]} onChange={(newVal: string) => {
|
||||||
|
|
||||||
this.decryptedModuleData[module.id] = newVal;
|
this.decryptedModuleData[module.id] = newVal;
|
||||||
|
@ -307,15 +279,15 @@ export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVa
|
||||||
<>
|
<>
|
||||||
<div className="spfxappdev-grid-row" key={module.id}>
|
<div className="spfxappdev-grid-row" key={module.id}>
|
||||||
<div className="spfxappdev-grid-col spfxappdev-sm12">
|
<div className="spfxappdev-grid-col spfxappdev-sm12">
|
||||||
{module.type == ModuleType.UserField &&
|
{module.type === ModuleType.UserField &&
|
||||||
<UserField defaultValue={this.decryptedModuleData[module.id]} isDisplayMode={true} tabIndex={index} />
|
<UserField defaultValue={this.decryptedModuleData[module.id]} isDisplayMode={true} tabIndex={index} />
|
||||||
}
|
}
|
||||||
|
|
||||||
{module.type == ModuleType.PasswordField &&
|
{module.type === ModuleType.PasswordField &&
|
||||||
<PasswordField defaultValue={this.decryptedModuleData[module.id]} isDisplayMode={true} tabIndex={index} />
|
<PasswordField defaultValue={this.decryptedModuleData[module.id]} isDisplayMode={true} tabIndex={index} />
|
||||||
}
|
}
|
||||||
|
|
||||||
{module.type == ModuleType.NoteField &&
|
{module.type === ModuleType.NoteField &&
|
||||||
<NoteField defaultValue={this.decryptedModuleData[module.id]} isDisplayMode={true} />
|
<NoteField defaultValue={this.decryptedModuleData[module.id]} isDisplayMode={true} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -350,7 +322,7 @@ export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVa
|
||||||
this.enteredMasterPW = newValue;
|
this.enteredMasterPW = newValue;
|
||||||
}}
|
}}
|
||||||
onKeyUp={(ev: React.KeyboardEvent<HTMLInputElement>) => {
|
onKeyUp={(ev: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
if(ev.keyCode == 13) {
|
if(ev.keyCode === 13) {
|
||||||
this.onOpenVault();
|
this.onOpenVault();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -375,9 +347,123 @@ export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVa
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private renderMasterPasswordControls(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className="spfxappdev-grid-row">
|
||||||
|
<div className="spfxappdev-grid-col spfxappdev-sm12">
|
||||||
|
<TextField
|
||||||
|
label={this.isNewVault ? strings.SetMasterPasswordLabel : strings.ChangeMasterPasswordLabel}
|
||||||
|
type="password"
|
||||||
|
required={this.isNewVault}
|
||||||
|
canRevealPassword={true}
|
||||||
|
onChange={(ev: any, newValue: string) => {
|
||||||
|
this.enteredMasterPW = newValue;
|
||||||
|
this.setState({
|
||||||
|
isSaveButtonDisabled: this.isSaveButtonDisabled()
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="spfxappdev-grid-col spfxappdev-sm12">
|
||||||
|
<TextField
|
||||||
|
label={strings.RepeatMasterPasswordLabel}
|
||||||
|
type="password"
|
||||||
|
required={this.isNewVault}
|
||||||
|
canRevealPassword={true}
|
||||||
|
onChange={(ev: any, newValue: string) => {
|
||||||
|
this.repeatedEnteredMasterPW = newValue;
|
||||||
|
this.setState({
|
||||||
|
isSaveButtonDisabled: this.isSaveButtonDisabled()
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderChangePasswordDialog(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
hidden={!this.state.showChangePasswordDialog}
|
||||||
|
dialogContentProps={{
|
||||||
|
title: strings.ChangeMasterPasswordDialogTitle,
|
||||||
|
type: DialogType.normal,
|
||||||
|
}}
|
||||||
|
onDismiss={() => { this.toggleChangePasswordDialogVisibility(); }}
|
||||||
|
>
|
||||||
|
|
||||||
|
<MessageBar messageBarType={MessageBarType.warning}>
|
||||||
|
{strings.DontLoseMasterpasswordLabel}
|
||||||
|
</MessageBar>
|
||||||
|
|
||||||
|
{this.renderMasterPasswordControls()}
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<PrimaryButton
|
||||||
|
disabled={this.state.isSaveButtonDisabled}
|
||||||
|
onClick={() => { this.onChangePasswordClick(); }}
|
||||||
|
text={strings.SaveLabel} />
|
||||||
|
<DefaultButton onClick={() => { this.toggleChangePasswordDialogVisibility(); }} text={strings.CancelLabel} />
|
||||||
|
</DialogFooter>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderCommandBarButtons(): JSX.Element {
|
||||||
|
const buttons: ICommandBarItemProps[] = [];
|
||||||
|
|
||||||
|
const saveButton: ICommandBarItemProps = {
|
||||||
|
key: 'saveSettings',
|
||||||
|
text: strings.SaveLabel,
|
||||||
|
disabled: this.isSaveButtonDisabled(),
|
||||||
|
iconProps: { iconName: 'Save' },
|
||||||
|
onClick: () => {
|
||||||
|
this.onSaveButtonClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.push(saveButton);
|
||||||
|
|
||||||
|
if(!this.isNewVault) {
|
||||||
|
const changeMasterPwButton: ICommandBarItemProps = {
|
||||||
|
key: 'changeMasterPassword',
|
||||||
|
text: strings.ChangeMasterPasswordButtonText,
|
||||||
|
iconProps: { iconName: 'PasswordField' },
|
||||||
|
onClick: () => {
|
||||||
|
this.toggleChangePasswordDialogVisibility();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.push(changeMasterPwButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.helper.functions.isNullOrEmpty(this.currentMasterPW)) {
|
||||||
|
const closeButton: ICommandBarItemProps = {
|
||||||
|
key: 'closeVault',
|
||||||
|
text: strings.CloseVaultLabel,
|
||||||
|
iconProps: { iconName: 'Lock' },
|
||||||
|
onClick: () => {
|
||||||
|
this.closeVault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.push(closeButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CommandBar
|
||||||
|
items={buttons}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private isSaveButtonDisabled(): boolean {
|
private isSaveButtonDisabled(): boolean {
|
||||||
if(this.isNewVault && this.helper.functions.isNullOrEmpty(this.enteredMasterPW)) {
|
if((this.isNewVault || this.state.showChangePasswordDialog) && this.helper.functions.isNullOrEmpty(this.enteredMasterPW)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.enteredMasterPW.Equals(this.repeatedEnteredMasterPW, false)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,8 +490,6 @@ export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVa
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("SSC onOpen", this.decryptedModuleData);
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isVaultOpen: isCorrectPW,
|
isVaultOpen: isCorrectPW,
|
||||||
showWrongMasterInfo: !isCorrectPW
|
showWrongMasterInfo: !isCorrectPW
|
||||||
|
@ -461,4 +545,55 @@ export default class PasswordVault extends SPFxAppDevWebPartComponent<PasswordVa
|
||||||
modules: this.state.modules
|
modules: this.state.modules
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onSaveButtonClick(): void {
|
||||||
|
this.isNewVault = false;
|
||||||
|
let encryptedMaster = this.currentMasterPW;
|
||||||
|
|
||||||
|
if(!this.enteredMasterPW.IsEmpty()) {
|
||||||
|
encryptedMaster = this.props.passwordVaultService.setMasterPassword(this.enteredMasterPW);
|
||||||
|
this.currentMasterPW = encryptedMaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
const encryptedModules: IModule[] = [];
|
||||||
|
this.state.modules.forEach((module: IModule) => {
|
||||||
|
const encryptedValue: string = this.props.passwordVaultService.encryptModuleData(module.type, this.decryptedModuleData[module.id]);
|
||||||
|
|
||||||
|
encryptedModules.push({
|
||||||
|
id: module.id,
|
||||||
|
data: encryptedValue,
|
||||||
|
type: module.type
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.props.onVaultDataChanged(encryptedMaster, encryptedModules);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
modules: encryptedModules
|
||||||
|
});
|
||||||
|
|
||||||
|
this.enteredMasterPW = '';
|
||||||
|
this.repeatedEnteredMasterPW = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private onChangePasswordClick(): void {
|
||||||
|
let encryptedMaster = this.currentMasterPW;
|
||||||
|
|
||||||
|
if(!this.enteredMasterPW.IsEmpty()) {
|
||||||
|
encryptedMaster = this.props.passwordVaultService.setMasterPassword(this.enteredMasterPW);
|
||||||
|
this.currentMasterPW = encryptedMaster;
|
||||||
|
this.props.onVaultPasswordChanged(encryptedMaster);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.enteredMasterPW = '';
|
||||||
|
this.repeatedEnteredMasterPW = '';
|
||||||
|
this.toggleChangePasswordDialogVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private toggleChangePasswordDialogVisibility(): void {
|
||||||
|
this.setState({
|
||||||
|
showChangePasswordDialog: !this.state.showChangePasswordDialog,
|
||||||
|
isSaveButtonDisabled: !this.state.showChangePasswordDialog
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styles from './PasswordVault.module.scss';
|
import { Callout, Icon, DirectionalHint, TextField, ITextField } from 'office-ui-fabric-react';
|
||||||
import { ActionButton, Callout, Icon, TooltipHost, DirectionalHint, TextField, ITextField } from 'office-ui-fabric-react';
|
|
||||||
import * as strings from 'PasswordVaultWebPartStrings';
|
import * as strings from 'PasswordVaultWebPartStrings';
|
||||||
import { getDeepOrDefault, issetDeep, isset } from '@spfxappdev/utility';
|
import { getDeepOrDefault, issetDeep, isset } from '@spfxappdev/utility';
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ define([], function() {
|
||||||
"PasswordLabel": "Passwort",
|
"PasswordLabel": "Passwort",
|
||||||
"MasterPasswordLabel": "Master Passwort",
|
"MasterPasswordLabel": "Master Passwort",
|
||||||
"SetMasterPasswordLabel": "Master Passwort",
|
"SetMasterPasswordLabel": "Master Passwort",
|
||||||
|
"RepeatMasterPasswordLabel": "Master Passwort wiederholen",
|
||||||
"ChangeMasterPasswordLabel": "Master Passwort ändern",
|
"ChangeMasterPasswordLabel": "Master Passwort ändern",
|
||||||
"UsernameLabel": "Benutezrname",
|
"UsernameLabel": "Benutezrname",
|
||||||
"NoteLabel": "Notiz",
|
"NoteLabel": "Notiz",
|
||||||
|
@ -22,5 +23,8 @@ define([], function() {
|
||||||
"PasswordModuleLabel": "Passwort",
|
"PasswordModuleLabel": "Passwort",
|
||||||
"UsernameModuleLabel": "Benutzername",
|
"UsernameModuleLabel": "Benutzername",
|
||||||
"NoteModuleLabel": "Notiz",
|
"NoteModuleLabel": "Notiz",
|
||||||
|
"ChangeMasterPasswordButtonText": "Master Passwort ändern",
|
||||||
|
"ChangeMasterPasswordDialogTitle": "Master Passwort ändern",
|
||||||
|
"CancelLabel": "Abbrechen"
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -5,6 +5,7 @@ define([], function() {
|
||||||
"PasswordLabel": "Password",
|
"PasswordLabel": "Password",
|
||||||
"MasterPasswordLabel": "Master Password",
|
"MasterPasswordLabel": "Master Password",
|
||||||
"SetMasterPasswordLabel": "Master Password",
|
"SetMasterPasswordLabel": "Master Password",
|
||||||
|
"RepeatMasterPasswordLabel": "Repeat Master Passwort",
|
||||||
"ChangeMasterPasswordLabel": "Change Master Password",
|
"ChangeMasterPasswordLabel": "Change Master Password",
|
||||||
"UsernameLabel": "Username",
|
"UsernameLabel": "Username",
|
||||||
"NoteLabel": "Note",
|
"NoteLabel": "Note",
|
||||||
|
@ -22,5 +23,8 @@ define([], function() {
|
||||||
"PasswordModuleLabel": "Password",
|
"PasswordModuleLabel": "Password",
|
||||||
"UsernameModuleLabel": "Username",
|
"UsernameModuleLabel": "Username",
|
||||||
"NoteModuleLabel": "Note",
|
"NoteModuleLabel": "Note",
|
||||||
|
"ChangeMasterPasswordButtonText": "Change Master Passwort",
|
||||||
|
"ChangeMasterPasswordDialogTitle": "Change Master Passwort",
|
||||||
|
"CancelLabel": "Cancel"
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -4,6 +4,7 @@ declare interface IPasswordVaultWebPartStrings {
|
||||||
PasswordLabel: string;
|
PasswordLabel: string;
|
||||||
MasterPasswordLabel: string;
|
MasterPasswordLabel: string;
|
||||||
SetMasterPasswordLabel: string;
|
SetMasterPasswordLabel: string;
|
||||||
|
RepeatMasterPasswordLabel: string;
|
||||||
ChangeMasterPasswordLabel: string;
|
ChangeMasterPasswordLabel: string;
|
||||||
UsernameLabel: string;
|
UsernameLabel: string;
|
||||||
NoteLabel: string;
|
NoteLabel: string;
|
||||||
|
@ -21,6 +22,9 @@ declare interface IPasswordVaultWebPartStrings {
|
||||||
PasswordModuleLabel: string;
|
PasswordModuleLabel: string;
|
||||||
UsernameModuleLabel: string;
|
UsernameModuleLabel: string;
|
||||||
NoteModuleLabel: string;
|
NoteModuleLabel: string;
|
||||||
|
ChangeMasterPasswordButtonText: string;
|
||||||
|
ChangeMasterPasswordDialogTitle: string;
|
||||||
|
CancelLabel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'PasswordVaultWebPartStrings' {
|
declare module 'PasswordVaultWebPartStrings' {
|
||||||
|
|
Loading…
Reference in New Issue