diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.html
index ab2a14fe..31d82e21 100644
--- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.html
+++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.html
@@ -1 +1,21 @@
-
+
+
+ 패스워드 변경
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.scss
index e69de29b..eeffa9a1 100644
--- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.scss
+++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.scss
@@ -0,0 +1,35 @@
+::ng-deep .setting-frame {
+ padding: 16px;
+ height: 100%;
+ min-width: 400px;
+ position: relative;
+
+ .mat-dialog-container {
+ position: relative;
+ }
+
+ .mat-card-header {
+ position: relative;
+ width: 100%;
+ border-bottom: 1px solid #dddddd;
+ margin-bottom: 12px;
+ }
+
+ .mat-card-content {
+ flex: 0 0 auto;
+ display: flex;
+ align-items: flex-start;
+ height: calc(100% - 100px);
+ border-bottom: 1px solid #dddddd;
+ }
+
+ .button-farm {
+ text-align: right;
+ position: absolute;
+ width: 100%;
+ bottom: 10px;
+ .mat-primary {
+ margin-left: 4px;
+ }
+ }
+}
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.ts
index 7cf9d439..bf2cc7d3 100644
--- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.ts
+++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/account/change-password.dialog.component.ts
@@ -6,15 +6,14 @@ import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { Store } from '@ngrx/store';
-import { UserInfo } from '@ucap-webmessenger/protocol-sync';
-import {
- UserInfoSS,
- UserInfoF,
- UserInfoDN
-} from '@ucap-webmessenger/protocol-query';
import { DialogService } from '@ucap-webmessenger/ui';
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
+import { AppAuthenticationService } from '@app/services/authentication.service';
+import { StringUtil, CharactorType } from '@ucap-webmessenger/core';
+
+import * as ServiceStore from '@app/store/messenger/service';
+import CryptoJS from 'crypto-js';
export interface ChangePasswordDialogData {}
@@ -40,6 +39,7 @@ export class ChangePasswordDialogComponent implements OnInit, OnDestroy {
@Inject(MAT_DIALOG_DATA) public data: ChangePasswordDialogData,
private dialogService: DialogService,
private sessionStorageService: SessionStorageService,
+ private appAuthenticationService: AppAuthenticationService,
private store: Store
) {
this.sessionVerinfo = this.sessionStorageService.get(
@@ -53,4 +53,147 @@ export class ChangePasswordDialogComponent implements OnInit, OnDestroy {
ngOnInit() {}
ngOnDestroy(): void {}
+
+ onChangePassword(info: {
+ currentLoginPw: string;
+ newLoginPw: string;
+ newConfirmLoginPw: string;
+ notValid: () => void;
+ }): void {
+ if (!info.currentLoginPw || '' === info.currentLoginPw.trim()) {
+ // 현재 비밀번호를 입력해주세요
+ info.notValid();
+ return;
+ }
+ if (!this.appAuthenticationService.isSameForPassword(info.currentLoginPw)) {
+ // "현재 비밀번호가 다릅니다"
+ info.notValid();
+ return;
+ }
+ if (!info.newLoginPw || '' === info.newLoginPw.trim()) {
+ // 신규 비밀번호를 입력해주세요
+ info.notValid();
+ return;
+ }
+ if (-1 < info.newLoginPw.indexOf(this.loginRes.userId)) {
+ // "사용자 ID를 비밀번호에 포함할 수 없습니다."
+ info.notValid();
+ return;
+ }
+ const phoneNumberOnly = !!this.loginRes.userInfo.hpNumber
+ ? this.loginRes.userInfo.hpNumber.trim().replace(new RegExp('-', 'g'), '')
+ : '';
+
+ switch (phoneNumberOnly.length) {
+ case 11:
+ if (-1 < info.newLoginPw.indexOf(phoneNumberOnly.substr(3, 4))) {
+ // "사용자 휴대폰번호를 비밀번호에 포함할 수 없습니다."
+ info.notValid();
+ return;
+ }
+ if (-1 < info.newLoginPw.indexOf(phoneNumberOnly.substr(7, 4))) {
+ // "사용자 휴대폰번호를 비밀번호에 포함할 수 없습니다."
+ info.notValid();
+ return;
+ }
+ break;
+ case 10:
+ if (-1 < info.newLoginPw.indexOf(phoneNumberOnly.substr(3, 3))) {
+ // "사용자 휴대폰번호를 비밀번호에 포함할 수 없습니다."
+ info.notValid();
+ return;
+ }
+ if (-1 < info.newLoginPw.indexOf(phoneNumberOnly.substr(6, 4))) {
+ // "사용자 휴대폰번호를 비밀번호에 포함할 수 없습니다."
+ info.notValid();
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!info.newConfirmLoginPw || '' === info.newConfirmLoginPw.trim()) {
+ // "신규 비밀번호 확인을 입력해주세요"
+ info.notValid();
+ return;
+ }
+ if (info.newLoginPw !== info.newConfirmLoginPw.trim()) {
+ // "신규 비밀번호와 신규 비밀번호 확인이 다릅니다"
+ info.notValid();
+ return;
+ }
+ if (info.currentLoginPw === info.newLoginPw.trim()) {
+ // "현재 비밀번호와 새로운 비밀번호가 같습니다."
+ info.notValid();
+ return;
+ }
+ if (0 < StringUtil.includes(info.newLoginPw, CharactorType.Space)) {
+ // "비밀번호에는 공백을 입력할 수 없습니다."
+ info.notValid();
+ return;
+ }
+ if (StringUtil.isRepeat(info.newLoginPw, 3)) {
+ // "숫자나 문자를 3번이상 반복적으로 사용할 수 없습니다."
+ info.notValid();
+ return;
+ }
+ if (
+ StringUtil.isIncrements(info.newLoginPw, 3) ||
+ StringUtil.isDecrements(info.newLoginPw, 3)
+ ) {
+ // "연속되는 숫자나 문자를 3번이상 사용할 수 없습니다."
+ info.notValid();
+ return;
+ }
+
+ let combinationCount = 0;
+ if (0 < StringUtil.includes(info.newLoginPw, CharactorType.Special)) {
+ combinationCount++;
+ }
+ if (0 < StringUtil.includes(info.newLoginPw, CharactorType.Number)) {
+ combinationCount++;
+ }
+ if (0 < StringUtil.includes(info.newLoginPw, CharactorType.Charactor)) {
+ combinationCount++;
+ }
+
+ let minPwLen = 0;
+ if (2 < combinationCount) {
+ minPwLen = 8;
+ } else if (2 === combinationCount) {
+ minPwLen = 10;
+ } else if (2 > combinationCount) {
+ // "2종류 이상 조합을 해야 합니다."
+ info.notValid();
+ return;
+ }
+
+ if (info.newLoginPw.length < minPwLen) {
+ // "비밀번호는 %d가지가 조합된 경우 %d자를 넘어야 합니다."
+ info.notValid();
+ return;
+ }
+
+ this.store.dispatch(
+ ServiceStore.userPasswordSet({
+ req: {
+ loginId: this.loginRes.userId,
+ companyCode: this.loginRes.companyCode,
+ oldLoginPw: CryptoJS.enc.Hex.stringify(
+ CryptoJS.SHA256(info.currentLoginPw)
+ ),
+ newLoginPw: CryptoJS.enc.Hex.stringify(
+ CryptoJS.SHA256(info.newLoginPw)
+ )
+ }
+ })
+ );
+ this.dialogRef.close({ choice: true });
+ }
+
+ onClickChoice(choice: boolean): void {
+ this.dialogRef.close({ choice });
+ }
}
diff --git a/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts b/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts
index 346ef739..91d22349 100644
--- a/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts
+++ b/projects/ucap-webmessenger-app/src/app/services/authentication.service.ts
@@ -51,4 +51,11 @@ export class AppAuthenticationService {
this.sessionStorageService.remove(KEY_VER_INFO);
this.sessionStorageService.remove(KEY_LOGIN_INFO);
}
+
+ isSameForPassword(pw: string): boolean {
+ const loginInfo = this.sessionStorageService.get(KEY_LOGIN_INFO);
+ return (
+ loginInfo.loginPw === CryptoJS.enc.Hex.stringify(CryptoJS.SHA256(pw))
+ );
+ }
}
diff --git a/projects/ucap-webmessenger-app/src/app/store/account/authentication/actions.ts b/projects/ucap-webmessenger-app/src/app/store/account/authentication/actions.ts
index ec4aa37f..b61192f1 100644
--- a/projects/ucap-webmessenger-app/src/app/store/account/authentication/actions.ts
+++ b/projects/ucap-webmessenger-app/src/app/store/account/authentication/actions.ts
@@ -96,20 +96,3 @@ export const privacyAgreeFailure = createAction(
export const privacyDisagree = createAction(
'[Account::Authentication] Privacy Disagree'
);
-
-export const changePassword = createAction(
- '[Account::Authentication] Change Password',
- props<{ req: UserPasswordSetRequest }>()
-);
-
-export const changePasswordSuccess = createAction(
- '[Account::Authentication] Change Password Success',
- props<{
- res: UserPasswordSetResponse;
- }>()
-);
-
-export const changePasswordFailure = createAction(
- '[Account::Authentication] Change Password Failure',
- props<{ error: any }>()
-);
diff --git a/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts b/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts
index 5636e48f..e4fb861f 100644
--- a/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts
+++ b/projects/ucap-webmessenger-app/src/app/store/account/authentication/effects.ts
@@ -32,9 +32,6 @@ import {
privacyDisagree,
privacyAgreeFailure,
privacyAgreeSuccess,
- changePassword,
- changePasswordFailure,
- changePasswordSuccess,
increaseLoginFailCount,
initialLoginFailCount,
logoutInitialize
@@ -49,10 +46,7 @@ import { AppAuthenticationService } from '@app/services/authentication.service';
import { NGXLogger } from 'ngx-logger';
import { Store } from '@ngrx/store';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
-import {
- ServiceProtocolService,
- UserPasswordSetResponse
-} from '@ucap-webmessenger/protocol-service';
+
import {
AuthenticationProtocolService,
LoginResponse
@@ -330,23 +324,6 @@ export class Effects {
)
);
- changePassword$ = createEffect(() =>
- this.actions$.pipe(
- ofType(changePassword),
- map(action => action.req),
- exhaustMap(req =>
- this.serviceProtocolService.userPasswordSet(req).pipe(
- map((res: UserPasswordSetResponse) => {
- return changePasswordSuccess({
- res
- });
- }),
- catchError(error => of(changePasswordFailure({ error })))
- )
- )
- )
- );
-
constructor(
private actions$: Actions,
private ngZone: NgZone,
@@ -357,7 +334,7 @@ export class Effects {
private appAuthenticationService: AppAuthenticationService,
private protocolService: ProtocolService,
private authenticationProtocolService: AuthenticationProtocolService,
- private serviceProtocolService: ServiceProtocolService,
+
private dialogService: DialogService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private logger: NGXLogger
diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/index.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/index.ts
index 44ba4d69..3d4d543f 100644
--- a/projects/ucap-webmessenger-app/src/app/store/messenger/index.ts
+++ b/projects/ucap-webmessenger-app/src/app/store/messenger/index.ts
@@ -6,6 +6,7 @@ import * as EventStore from './event';
import * as OptionStore from './option';
import * as QueryStore from './query';
import * as RoomStore from './room';
+import * as ServiceStore from './service';
import * as StatusStore from './status';
import * as SyncStore from './sync';
import * as SettingsStore from './settings';
@@ -16,6 +17,7 @@ export interface State {
option: OptionStore.State;
query: QueryStore.State;
room: RoomStore.State;
+ service: ServiceStore.State;
status: StatusStore.State;
sync: SyncStore.State;
settings: SettingsStore.State;
@@ -27,6 +29,7 @@ export const effects: Type[] = [
OptionStore.Effects,
QueryStore.Effects,
RoomStore.Effects,
+ ServiceStore.Effects,
StatusStore.Effects,
SyncStore.Effects,
SettingsStore.Effects
@@ -39,6 +42,7 @@ export function reducers(state: State | undefined, action: Action) {
option: OptionStore.reducer,
query: QueryStore.reducer,
room: RoomStore.reducer,
+ service: ServiceStore.reducer,
status: StatusStore.reducer,
sync: SyncStore.reducer,
settings: SettingsStore.reducer
@@ -62,6 +66,9 @@ export function selectors(selector: Selector) {
QuerySelector: QueryStore.selectors(
createSelector(selector, (state: State) => state.query)
),
+ ServiceSelector: ServiceStore.selectors(
+ createSelector(selector, (state: State) => state.service)
+ ),
StatusSelector: StatusStore.selectors(
createSelector(selector, (state: State) => state.status)
),
diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/service/actions.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/service/actions.ts
new file mode 100644
index 00000000..f6956adb
--- /dev/null
+++ b/projects/ucap-webmessenger-app/src/app/store/messenger/service/actions.ts
@@ -0,0 +1,23 @@
+import { createAction, props } from '@ngrx/store';
+
+import {
+ UserPasswordSetRequest,
+ UserPasswordSetResponse
+} from '@ucap-webmessenger/protocol-service';
+
+export const userPasswordSet = createAction(
+ '[Account::Authentication] User Password Set',
+ props<{ req: UserPasswordSetRequest }>()
+);
+
+export const userPasswordSetSuccess = createAction(
+ '[Account::Authentication] User Password Set Success',
+ props<{
+ res: UserPasswordSetResponse;
+ }>()
+);
+
+export const userPasswordSetFailure = createAction(
+ '[Account::Authentication] User Password Set Failure',
+ props<{ error: any }>()
+);
diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/service/effects.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/service/effects.ts
new file mode 100644
index 00000000..175351cd
--- /dev/null
+++ b/projects/ucap-webmessenger-app/src/app/store/messenger/service/effects.ts
@@ -0,0 +1,47 @@
+import { Injectable } from '@angular/core';
+
+import { Actions, ofType, createEffect } from '@ngrx/effects';
+
+import { Store } from '@ngrx/store';
+
+import { NGXLogger } from 'ngx-logger';
+import { catchError, exhaustMap, map } from 'rxjs/operators';
+
+import { of } from 'rxjs';
+
+import {
+ userPasswordSet,
+ userPasswordSetSuccess,
+ userPasswordSetFailure
+} from './actions';
+import {
+ ServiceProtocolService,
+ UserPasswordSetResponse
+} from '@ucap-webmessenger/protocol-service';
+
+@Injectable()
+export class Effects {
+ userPasswordSet$ = createEffect(() =>
+ this.actions$.pipe(
+ ofType(userPasswordSet),
+ map(action => action.req),
+ exhaustMap(req =>
+ this.serviceProtocolService.userPasswordSet(req).pipe(
+ map((res: UserPasswordSetResponse) => {
+ return userPasswordSetSuccess({
+ res
+ });
+ }),
+ catchError(error => of(userPasswordSetFailure({ error })))
+ )
+ )
+ )
+ );
+
+ constructor(
+ private actions$: Actions,
+ private serviceProtocolService: ServiceProtocolService,
+ private store: Store,
+ private logger: NGXLogger
+ ) {}
+}
diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/service/index.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/service/index.ts
new file mode 100644
index 00000000..2663cade
--- /dev/null
+++ b/projects/ucap-webmessenger-app/src/app/store/messenger/service/index.ts
@@ -0,0 +1,4 @@
+export * from './actions';
+export * from './effects';
+export * from './reducers';
+export * from './state';
diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/service/reducers.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/service/reducers.ts
new file mode 100644
index 00000000..70e7e209
--- /dev/null
+++ b/projects/ucap-webmessenger-app/src/app/store/messenger/service/reducers.ts
@@ -0,0 +1,4 @@
+import { createReducer, on } from '@ngrx/store';
+import { initialState } from './state';
+
+export const reducer = createReducer(initialState);
diff --git a/projects/ucap-webmessenger-app/src/app/store/messenger/service/state.ts b/projects/ucap-webmessenger-app/src/app/store/messenger/service/state.ts
new file mode 100644
index 00000000..1e05bd08
--- /dev/null
+++ b/projects/ucap-webmessenger-app/src/app/store/messenger/service/state.ts
@@ -0,0 +1,9 @@
+import { Selector, createSelector } from '@ngrx/store';
+
+export interface State {}
+
+export const initialState: State = {};
+
+export function selectors(selector: Selector) {
+ return {};
+}
diff --git a/projects/ucap-webmessenger-core/src/lib/utils/string.util.ts b/projects/ucap-webmessenger-core/src/lib/utils/string.util.ts
index ffd83d2d..f8f68136 100644
--- a/projects/ucap-webmessenger-core/src/lib/utils/string.util.ts
+++ b/projects/ucap-webmessenger-core/src/lib/utils/string.util.ts
@@ -1,3 +1,13 @@
+export enum CharactorType {
+ Number = 'Number',
+ Charactor = 'Charactor',
+ Space = 'Space',
+ Special = 'Special'
+}
+
+const CharCodeZero = '0'.charCodeAt(0);
+const CharCodeNine = '9'.charCodeAt(0);
+
export class StringUtil {
/**
* prefix zero fill
@@ -15,4 +25,160 @@ export class StringUtil {
return StringUtil.zeroFill(str.toString(), len);
}
}
+
+ /**
+ * includes
+ */
+ public static includes(target: string, charactorType: CharactorType): number {
+ let nNum = 0;
+ let nChar = 0;
+ let nSpace = 0;
+ let nElse = 0;
+
+ for (let i = 0; i < target.length; i++) {
+ const char = target.charCodeAt(i);
+ // 숫자
+ if (char >= 48 && char <= 57) {
+ nNum++;
+ } else if (char >= 65 && char <= 90) {
+ // 영어(대문자)
+ nChar++;
+ } else if (char >= 97 && char <= 122) {
+ // 영어(소문자)
+ nChar++;
+ } else if (target.charAt(i) === ' ') {
+ // 공백
+ nSpace++;
+ } else {
+ // 특수기호
+ nElse++;
+ }
+ }
+
+ switch (charactorType) {
+ case CharactorType.Number:
+ return nNum;
+ case CharactorType.Charactor:
+ return nChar;
+ case CharactorType.Space:
+ return nSpace;
+ case CharactorType.Special:
+ return nElse;
+
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * isRepeat
+ */
+ public static isRepeat(target: string, repeatTime: number) {
+ let old = '';
+ let rptCnt = 1;
+ for (let i = 0; i < target.length; i++) {
+ const s = target.charAt(i);
+ if (old === s) {
+ rptCnt++;
+ if (rptCnt === repeatTime) {
+ return true;
+ }
+ } else {
+ old = s;
+ rptCnt = 1;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * isIncrements
+ */
+ public static isIncrements(target: string, repeatTime: number) {
+ for (let i = 0; i < target.length - (repeatTime - 1); i++) {
+ let notNeed = false;
+ const ary: number[] = [];
+ for (let j = 0; j < repeatTime; j++) {
+ const char = target.charCodeAt(i + j);
+ if (!StringUtil.isAlphaNumeric(char)) {
+ notNeed = true;
+ break;
+ }
+ ary.push(char);
+ }
+
+ if (notNeed) {
+ continue;
+ }
+
+ for (let k = 1; k < ary.length; k++) {
+ if (ary[0] !== ary[k] - k) {
+ notNeed = true;
+ break;
+ }
+ }
+
+ if (notNeed) {
+ continue;
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * isIncrements
+ */
+ public static isDecrements(target: string, repeatTime: number) {
+ for (let i = 0; i < target.length - (repeatTime - 1); i++) {
+ let notNeed = false;
+ const ary: number[] = [];
+ for (let j = 0; j < repeatTime; j++) {
+ const char = target.charCodeAt(i + j);
+ if (!StringUtil.isAlphaNumeric(char)) {
+ notNeed = true;
+ break;
+ }
+ ary.push(char);
+ }
+
+ if (notNeed) {
+ continue;
+ }
+
+ for (let k = 1; k < ary.length; k++) {
+ if (ary[0] !== ary[k] + k) {
+ notNeed = true;
+ break;
+ }
+ }
+
+ if (notNeed) {
+ continue;
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * isAlphabet
+ */
+ public static isAlphabet(char: number): boolean {
+ return (char >= 65 && char <= 90) || (char >= 97 && char <= 122);
+ }
+ /**
+ * isNumeric
+ */
+ public static isNumeric(char: number): boolean {
+ return char >= 48 && char <= 57;
+ }
+ /**
+ * isAlphaNumeric
+ */
+ public static isAlphaNumeric(char: number): boolean {
+ return StringUtil.isAlphabet(char) || StringUtil.isNumeric(char);
+ }
}
diff --git a/projects/ucap-webmessenger-ui-account/src/lib/components/change-password.component.html b/projects/ucap-webmessenger-ui-account/src/lib/components/change-password.component.html
index d1be60c8..61744c94 100644
--- a/projects/ucap-webmessenger-ui-account/src/lib/components/change-password.component.html
+++ b/projects/ucap-webmessenger-ui-account/src/lib/components/change-password.component.html
@@ -1,6 +1,4 @@