Commit 2823f02b authored by Emily Jensen's avatar Emily Jensen

Merged in dev

parents 2145db99 5153c04f
FROM node:8-alpine
FROM node:10-alpine
RUN mkdir /dist
WORKDIR /dist
......
import { tap, withLatestFrom, map, exhaustMap } from "rxjs/operators";
import { of as observableOf } from "rxjs";
import {
tap,
withLatestFrom,
map,
exhaustMap,
catchError
} from "rxjs/operators";
import { of, from } from "rxjs";
import { Injectable } from "@angular/core";
import { Router } from "@angular/router";
......@@ -21,34 +27,36 @@ import * as fromAccount from "./account.reducer";
import { IS_EXTENSION } from "../constants";
import { UserService } from "./user";
import { Store } from "@ngrx/store";
import { Store, select } from "@ngrx/store";
import { IState } from "../app.reducers";
import { HttpErrorResponse } from "@angular/common/http";
import { SetPasswordActionTypes } from "./forgot-password/set-password/set-password.actions";
import { SetPasswordActionTypes } from "./reset-password/set-password/set-password.actions";
@Injectable()
export class LoginEffects {
@Effect()
login$ = this.actions$.pipe(
ofType<LoginAction>(AccountActionTypes.LOGIN),
withLatestFrom(this.store.select(fromAccount.getLoginForm)),
withLatestFrom(this.store.pipe(select(fromAccount.getLoginForm))),
map(([action, form]) => form.value),
exhaustMap(auth => {
const callLogin = () => {
return this.userService
.login(
return from(
this.userService.login(
auth.email,
auth.password,
auth.rememberMe ? auth.rememberMe : false
)
.then(resp => new LoginSuccessAction(resp))
.catch(err => new LoginFailureAction(err));
).pipe(
map(resp => new LoginSuccessAction(resp)),
catchError(err => of(new LoginFailureAction(err)))
);
};
const callCheckAndSetUrl = (url: string) => {
return this.userService
.checkAndSetUrl(url)
.then(() => callLogin())
.catch(err => new LoginFailureAction(err));
return this.userService.checkAndSetUrl(url).pipe(
exhaustMap(() => callLogin()),
catchError(err => of(new LoginFailureAction(err)))
);
};
if (auth.url) {
......@@ -60,7 +68,11 @@ export class LoginEffects {
);
@Effect({ dispatch: false })
loginSuccess$ = this.actions$.ofType(AccountActionTypes.LOGIN_SUCCESS, SetPasswordActionTypes.SET_PASSWORD_SUCCESS).pipe(
loginSuccess$ = this.actions$.pipe(
ofType(
AccountActionTypes.LOGIN_SUCCESS,
SetPasswordActionTypes.SET_PASSWORD_SUCCESS
),
tap(() => {
if (IS_EXTENSION) {
this.router.navigate(["/popup"]);
......@@ -71,19 +83,19 @@ export class LoginEffects {
);
@Effect({ dispatch: false })
loginRedirect$ = this.actions$
.ofType(
loginRedirect$ = this.actions$.pipe(
ofType(
AccountActionTypes.LOGIN_REDIRECT,
AccountActionTypes.LOGOUT_SUCCESS
)
.pipe(
tap(() => {
this.router.navigate(["/login"], { replaceUrl: true });
})
);
),
tap(() => {
this.router.navigate(["/login"], { replaceUrl: true });
})
);
@Effect()
logout$ = this.actions$.ofType(AccountActionTypes.LOGOUT).pipe(
logout$ = this.actions$.pipe(
ofType(AccountActionTypes.LOGOUT),
exhaustMap(() =>
this.userService
.logout()
......@@ -93,48 +105,49 @@ export class LoginEffects {
);
@Effect({ dispatch: false })
logoutSuccess$ = this.actions$
.ofType(AccountActionTypes.LOGOUT_SUCCESS)
.pipe(tap(() => localStorage.clear()));
logoutSuccess$ = this.actions$.pipe(
ofType(AccountActionTypes.LOGOUT_SUCCESS),
tap(() => localStorage.clear())
);
@Effect()
handleAPIError$ = this.actions$
.ofType<HandleAPIErrorAction>(AccountActionTypes.HANDLE_API_ERROR)
.pipe(
map(action => action.payload),
exhaustMap(err => {
const res: HttpErrorResponse = err.res;
if (res) {
if (res.status === 0) {
// Not so bad, network is just down
return observableOf(new APIFailureNetworkDownAction());
}
if ([401, 403].includes(res.status)) {
if (
res.status === 403 &&
res.error &&
res.error.detail === "User's email is not confirmed."
) {
return observableOf(new UserMustConfirmEmailAction());
}
return observableOf(new LogoutAction());
handleAPIError$ = this.actions$.pipe(
ofType<HandleAPIErrorAction>(AccountActionTypes.HANDLE_API_ERROR),
map(action => action.payload),
map(err => {
const res: HttpErrorResponse = err.res;
if (res) {
if (res.status === 0) {
// Not so bad, network is just down
return new APIFailureNetworkDownAction();
}
if ([401, 403].includes(res.status)) {
if (
res.status === 403 &&
res.error &&
res.error.detail === "User's email is not confirmed."
) {
return new UserMustConfirmEmailAction();
}
return new LogoutAction();
}
}
if (err.name === "Passit SDK Authentication Error") {
return observableOf(new LogoutAction());
}
if (err.name === "Passit SDK Authentication Error") {
return new LogoutAction();
}
// This should never run
console.error("Unable to use api and unable to handle error", err);
return observableOf(new CriticalAPIErrorAction(err));
})
);
// This should never run
console.error("Unable to use api and unable to handle error", err);
return new CriticalAPIErrorAction(err);
})
);
@Effect({ dispatch: false })
userMustConfirmEmail$ = this.actions$
.ofType(AccountActionTypes.USER_MUST_CONFIRM_EMAIL)
.pipe(tap(() => this.router.navigate(["/confirm-email"])));
userMustConfirmEmail$ = this.actions$.pipe(
ofType(AccountActionTypes.USER_MUST_CONFIRM_EMAIL),
tap(() => this.router.navigate(["/confirm-email"]))
);
constructor(
private actions$: Actions,
......
import { take } from "rxjs/operators";
import { fakeAsync, inject, TestBed } from "@angular/core/testing";
import { Store, StoreModule } from "@ngrx/store";
import { Store, StoreModule, select } from "@ngrx/store";
import "rxjs/add/operator/take";
import { logout } from "../app.reducers";
......@@ -95,7 +95,9 @@ describe("AccountReducer", () => {
it("should logout a user", fakeAsync(
inject([Store], (store: Store<any>) => {
const isLoggedInSelector = store.select(fromAccount.getIsLoggedIn);
const isLoggedInSelector = store.pipe(
select(fromAccount.getIsLoggedIn)
);
// Login first
const user: IAuthStore = {
......
......@@ -19,19 +19,23 @@ import * as fromRegister from "./register/register.reducer";
import * as fromChangePassword from "./change-password/change-password.reducer";
import * as fromDeleteAccount from "./delete/delete.reducer";
import * as fromErrorReporting from "./error-reporting/error-reporting.reducer";
import * as fromForgotPassword from "./forgot-password/forgot-password.reducer";
import * as fromResetPasswordVerify from "./forgot-password/reset-password-verify/reset-password-verify.reducer";
import * as fromSetPassword from "./forgot-password/set-password/set-password.reducer";
import * as fromResetPassword from "./reset-password/reset-password.reducer";
import * as fromResetPasswordVerify from "./reset-password/reset-password-verify/reset-password-verify.reducer";
import * as fromSetPassword from "./reset-password/set-password/set-password.reducer";
import {
ResetPasswordVerifyActionTypes,
ResetPasswordVerifyActionsUnion
<<<<<<< HEAD
} from "./forgot-password/reset-password-verify/reset-password-verify.actions";
import * as fromManageBackupCode from "./manage-backup-code/manage-backup-code.reducer";
=======
} from "./reset-password/reset-password-verify/reset-password-verify.actions";
>>>>>>> dev
import {
SetPasswordSuccess,
SetPasswordActionTypes,
ForceSetPassword
} from "./forgot-password/set-password/set-password.actions";
} from "./reset-password/set-password/set-password.actions";
export interface IAuthState {
email: string | null;
......@@ -126,7 +130,7 @@ export interface IAccountState {
changePassword: fromChangePassword.IChangePasswordState;
deleteAccount: fromDeleteAccount.IDeleteAccountState;
errorReporting: fromErrorReporting.IState;
forgotPassword: fromForgotPassword.IForgotPasswordState;
resetPassword: fromResetPassword.IResetPasswordState;
resetPasswordVerify: fromResetPasswordVerify.IResetPasswordVerifyState;
manageBackupCode: fromManageBackupCode.IState;
setPassword: fromSetPassword.ISetPasswordState;
......@@ -140,7 +144,7 @@ export const reducers: ActionReducerMap<IAccountState> = {
changePassword: fromChangePassword.reducer,
deleteAccount: fromDeleteAccount.reducer,
errorReporting: fromErrorReporting.reducer,
forgotPassword: fromForgotPassword.reducer,
resetPassword: fromResetPassword.reducer,
resetPasswordVerify: fromResetPasswordVerify.reducer,
setPassword: fromSetPassword.reducer,
manageBackupCode: fromManageBackupCode.reducer
......@@ -327,25 +331,25 @@ export const getErrorReportingHasStarted = createSelector(
fromErrorReporting.getHasStarted
);
export const selectForgotPasswordState = createSelector(
export const selectResetPasswordState = createSelector(
selectAccountState,
(state: IAccountState) => state.forgotPassword
(state: IAccountState) => state.resetPassword
);
export const getForgotPasswordForm = createSelector(
selectForgotPasswordState,
fromForgotPassword.getForm
export const getResetPasswordForm = createSelector(
selectResetPasswordState,
fromResetPassword.getForm
);
export const getForgotPasswordHasStarted = createSelector(
selectForgotPasswordState,
fromForgotPassword.getHasStarted
export const getResetPasswordHasStarted = createSelector(
selectResetPasswordState,
fromResetPassword.getHasStarted
);
export const getForgotPasswordHasFinished = createSelector(
selectForgotPasswordState,
fromForgotPassword.getHasFinished
export const getResetPasswordHasFinished = createSelector(
selectResetPasswordState,
fromResetPassword.getHasFinished
);
export const getForgotPasswordErrorMessage = createSelector(
selectForgotPasswordState,
fromForgotPassword.getErrorMessage
export const getResetPasswordErrorMessage = createSelector(
selectResetPasswordState,
fromResetPassword.getErrorMessage
);
export const selectManageBackupCodeState = createSelector(
......
import { take } from "rxjs/operators";
import { Component } from "@angular/core";
import { Store } from "@ngrx/store";
import { Store, select } from "@ngrx/store";
import * as fromRoot from "../../app.reducers";
import * as fromAccount from "../account.reducer";
......@@ -24,7 +24,7 @@ import { SetValueAction } from "ngrx-forms";
`
})
export class ChangePasswordContainer {
form$ = this.store.select(fromAccount.getChangePasswordForm);
form$ = this.store.pipe(select(fromAccount.getChangePasswordForm));
hasStarted = false;
hasFinished = false;
showConfirm: boolean;
......
import { Component, Input, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { Store } from "@ngrx/store";
import { Store, select } from "@ngrx/store";
import * as fromRoot from "../../app.reducers";
import { NgPassitSDK } from "../../ngsdk/sdk";
......@@ -31,19 +31,21 @@ export class ConfirmEmailContainer implements OnInit {
/**
* The error message, passed to the template
*/
errorMessage$ = this.store.select(fromAccount.getConfirmErrorMessage);
errorMessage$ = this.store.pipe(select(fromAccount.getConfirmErrorMessage));
confirmCodeMessage$ = this.store.select(fromAccount.getConfirmEmailMessage);
confirmCodeMessage$ = this.store.pipe(
select(fromAccount.getConfirmEmailMessage)
);
/**
* Lets the template know if a server call has started
// */
hasStarted$ = this.store.select(fromAccount.getConfirmHasStarted);
hasStarted$ = this.store.pipe(select(fromAccount.getConfirmHasStarted));
/**
* Lets the template know if a server call has finished (successfully)
*/
hasFinished$ = this.store.select(fromAccount.getConfirmHasFinished);
hasFinished$ = this.store.pipe(select(fromAccount.getConfirmHasFinished));
/**
* The confirmation code
......@@ -61,7 +63,8 @@ export class ConfirmEmailContainer implements OnInit {
* Confirm email will temporarily appear in two different ways which requires
* logic to distinguish
*/
@Input() inline: boolean;
@Input()
inline: boolean;
/**
* On init, look for the code param in the URL. If it's there, submit it.
......
......@@ -9,7 +9,7 @@ import {
} from "rxjs/operators";
import { HttpErrorResponse } from "@angular/common/http";
import { Router } from "@angular/router";
import { Store } from "@ngrx/store";
import { Store, select } from "@ngrx/store";
import { UserService } from "../user";
import * as fromRoot from "../../app.reducers";
......@@ -68,7 +68,7 @@ export class ConfirmEmailEffects {
@Effect({ dispatch: false })
VerifyEmailSuccess$ = this.actions$.pipe(
ofType<VerifyEmailSuccess>(ConfirmEmailTypes.VERIFY_EMAIL_SUCCESS),
withLatestFrom(this.store.select(getRouterPath)),
withLatestFrom(this.store.pipe(select(getRouterPath))),
map(([action, path]) => path),
tap(path => {
// The confirm email page automatically redirects while register does not
......
import { map, take } from "rxjs/operators";
import { Injectable } from "@angular/core";
import { CanActivate } from "@angular/router";
import { Store } from "@ngrx/store";
import { Store, select } from "@ngrx/store";
import * as fromAccount from "../account.reducer";
......@@ -13,7 +13,7 @@ export class ConfirmEmailGuard implements CanActivate {
* Note this method only checks if they just confirmed, it doesn't check against the server.
*/
public canActivate() {
return this.store.select(fromAccount.getConfirmIsVerified).pipe(
return this.store.pipe(select(fromAccount.getConfirmIsVerified)).pipe(
map(isVerified => !isVerified),
take(1)
);
......
......@@ -3,7 +3,7 @@ import { UserService } from "../user/user.service";
import * as fromRoot from "../../app.reducers";
import * as fromAccount from "../account.reducer";
import { ResetDeleteStateAction } from "../delete/delete.actions";
import { Store } from "@ngrx/store";
import { Store, select } from "@ngrx/store";
@Component({
selector: "app-delete-container",
......@@ -16,11 +16,12 @@ import { Store } from "@ngrx/store";
`
})
export class DeleteContainer implements OnInit {
form$ = this.store.select(fromAccount.getDeleteAccountForm);
formErrors$ = this.store.select(fromAccount.getDeleteFormErrors);
form$ = this.store.pipe(select(fromAccount.getDeleteAccountForm));
formErrors$ = this.store.pipe(select(fromAccount.getDeleteFormErrors));
constructor(
private userService: UserService,
private store: Store<fromRoot.IState>) {}
private store: Store<fromRoot.IState>
) {}
ngOnInit() {
this.store.dispatch(new ResetDeleteStateAction());
......@@ -29,5 +30,4 @@ export class DeleteContainer implements OnInit {
doAccountDelete(password: string) {
this.userService.deleteUserAccount(password);
}
}
import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core";
import * as fromAccount from "../account.reducer";
import * as fromErrorReporting from "./error-reporting.reducer";
import { Store } from "@ngrx/store";
import { Store, select } from "@ngrx/store";
import { SaveForm } from "./error-reporting.actions";
import { SetValueAction } from "ngrx-forms";
......@@ -15,14 +15,18 @@ import { SetValueAction } from "ngrx-forms";
></app-error-reporting>`
})
export class ErrorReportingContainer implements OnInit {
form$ = this.store.select(fromAccount.getErrorReportingForm);
hasStarted$ = this.store.select(fromAccount.getErrorReportingHasStarted);
hasFinished$ = this.store.select(fromAccount.getErrorReportingHasFinished);
form$ = this.store.pipe(select(fromAccount.getErrorReportingForm));
hasStarted$ = this.store.pipe(
select(fromAccount.getErrorReportingHasStarted)
);
hasFinished$ = this.store.pipe(
select(fromAccount.getErrorReportingHasFinished)
);
optInErrorReporting: boolean;
constructor(private store: Store<any>) {
this.store
.select(fromAccount.getOptInErrorReporting)
.pipe(select(fromAccount.getOptInErrorReporting))
.subscribe(optIn => (this.optInErrorReporting = optIn));
}
......
......@@ -15,7 +15,7 @@ import {
switchMap
} from "rxjs/operators";
import * as fromRoot from "../../app.reducers";
import { Store } from "@ngrx/store";
import { Store, select } from "@ngrx/store";
import {
getErrorReportingForm,
getErrorReportingHasFinished
......@@ -28,7 +28,7 @@ export class ErrorReportingEffects {
@Effect()
save$ = this.actions$.pipe(
ofType<SaveForm>(ErrorReportingTypes.SAVE_FORM),
withLatestFrom(this.store.select(getErrorReportingForm)),
withLatestFrom(this.store.pipe(select(getErrorReportingForm))),
map(([action, form]) => form),
exhaustMap(form =>
this.userService
......@@ -40,7 +40,7 @@ export class ErrorReportingEffects {
/** Clear the finished indicator after a little bit */
@Effect()
fadeFinished$ = this.store.select(getErrorReportingHasFinished).pipe(
fadeFinished$ = this.store.pipe(select(getErrorReportingHasFinished)).pipe(
filter(hasFinished => hasFinished === true),
switchMap(() => timer(2000).pipe(map(() => new ClearFinished())))
);
......
import { Injectable } from "@angular/core";
import { Effect, Actions } from "@ngrx/effects";
import {
SubmitForm,
ForgotPasswordActionTypes,
SubmitFormSuccess,
SubmitFormFailure
} from "./forgot-password.actions";
import { withLatestFrom, map, exhaustMap, catchError } from "rxjs/operators";
import { Store } from "@ngrx/store";
import { IState } from "../../app.reducers";
import { getForgotPasswordForm } from "../account.reducer";
import { UserService } from "../user";
import { of } from "rxjs";
@Injectable()
export class ForgotPasswordEffects {
@Effect()
submitForm$ = this.actions$
.ofType<SubmitForm>(ForgotPasswordActionTypes.SUBMIT_FORM)
.pipe(
withLatestFrom(this.store.select(getForgotPasswordForm)),
map(([action, form]) => form.value),
exhaustMap(form => {
const callForgotPassword = () => {
return this.userService.forgotPassword(form.email).pipe(
map(() => new SubmitFormSuccess()),
catchError(err => of(new SubmitFormFailure()))
);
};
const callCheckAndSetUrl = (url: string) => {
return of(this.userService.checkAndSetUrl(url)).pipe(
exhaustMap(() => callForgotPassword()),
catchError(err => of(new SubmitFormFailure()))
);
};
if (form.url) {
return callCheckAndSetUrl(form.url);
} else {
return callForgotPassword();
}
})
);
constructor(
private actions$: Actions,
private store: Store<IState>,
private userService: UserService
) {}
}
......@@ -33,14 +33,15 @@ import { ErrorReportingComponent } from "./error-reporting/error-reporting.compo
import { ErrorReportingContainer } from "./error-reporting/error-reporting.container";
import { ErrorReportingEffects } from "./error-reporting/error-reporting.effects";
import { BackupCodeComponent } from "./backup-code/backup-code.component";
import { ForgotPasswordComponent } from "./forgot-password/forgot-password.component";
import { ResetPasswordComponent } from "./reset-password/reset-password.component";
import { MarketingFrameComponent } from "./marketing-frame/marketing-frame.component";
import { ForgotPasswordContainer } from "./forgot-password/forgot-password.container";
import { ForgotPasswordEffects } from "./forgot-password/forgot-password.effects";
import { ResetPasswordVerifyComponent } from "./forgot-password/reset-password-verify/reset-password-verify.component";
import { ResetPasswordVerifyContainer } from "./forgot-password/reset-password-verify/reset-password-verify.container";
import { ResetPasswordVerifyEffects } from "./forgot-password/reset-password-verify/reset-password-verify.effects";
import { ResetPasswordContainer } from "./reset-password/reset-password.container";
import { ResetPasswordEffects } from "./reset-password/reset-password.effects";
import { ResetPasswordVerifyComponent } from "./reset-password/reset-password-verify/reset-password-verify.component";
import { ResetPasswordVerifyContainer } from "./reset-password/reset-password-verify/reset-password-verify.container";
import { ResetPasswordVerifyEffects } from "./reset-password/reset-password-verify/reset-password-verify.effects";
import { PasswordInputComponent } from "./change-password/password-input/password-input.component";
<<<<<<< HEAD
import { SetPasswordComponent } from "./forgot-password/set-password/set-password.component";
import { ManageBackupCodeComponent } from "./manage-backup-code/manage-backup-code.component";
import { ManageBackupCodeContainer } from "./manage-backup-code/manage-backup-code.container";
......@@ -48,6 +49,12 @@ import { SetPasswordContainer } from "./forgot-password/set-password/set-passwor
import { SetPasswordEffects } from "./forgot-password/set-password/set-password.effects";
import { ManageBackupCodeEffects } from "./manage-backup-code/manage-backup-code.effects";
import { BackupCodePdfService } from "./backup-code-pdf.service";
=======
import { SetPasswordComponent } from "./reset-password/set-password/set-password.component";
import { SetPasswordContainer } from "./reset-password/set-password/set-password.container";
import { SetPasswordEffects } from "./reset-password/set-password/set-password.effects";
import { ServerSelectComponent } from "./shared/server-select.component";
>>>>>>> dev
export const COMPONENTS = [
AccountComponent,
......@@ -65,15 +72,20 @@ export const COMPONENTS = [
RegisterComponent,
RegisterContainer,
BackupCodeComponent,
ForgotPasswordComponent,
ForgotPasswordContainer,
ResetPasswordComponent,
ResetPasswordContainer,
ResetPasswordVerifyComponent,
ResetPasswordVerifyContainer,
SetPasswordComponent,
MarketingFrameComponent,
SetPasswordContainer,
<<<<<<< HEAD
ManageBackupCodeComponent,
ManageBackupCodeContainer,
=======
MarketingFrameComponent,
ServerSelectComponent
>>>>>>> dev
];
export const SERVICES = [UserService, ConfirmEmailGuard, BackupCodePdfService];
......@@ -95,7 +107,7 @@ export const SERVICES = [UserService, ConfirmEmailGuard, BackupCodePdfService];
RegisterEffects,
ConfirmEmailEffects,
ErrorReportingEffects,
ForgotPasswordEffects,
ResetPasswordEffects,
ResetPasswordVerifyEffects,
SetPasswordEffects,
ManageBackupCodeEffects
......
......@@ -57,39 +57,15 @@
[control]="form.controls.rememberMe"
tabindex="4"
></app-checkbox>
</div>
<div *ngIf="isExtension">
<app-checkbox
title="My company has its own Passit server"
subtext="If you signed up for Passit anywhere other than app.passit.io (e.g. passit.mycompany.com, or your self-hosted server), you’ll need to specify where you want to log&nbsp;in."
[control]="form.controls.showUrl"
tabindex="5"
></app-checkbox>
<div *ngIf="form.value.showUrl" class="form-field form-field--large">
<label [for]="form.controls.url.id" class="form-field__label">Server URL</label>
<input
type="url"
[ngrxFormControlState]="form.controls.url"
class="form-field__input"
[class.form-field__input--invalid]="form.errors._url"
[class.form-field__input--one-action]="form.controls.url.isValidationPending"
tabindex="6"
>
<div class="form-field__actions" *ngIf="form.controls.url.isValidationPending">
<progress-indicator
[inProgress]="form.controls.url.isValidationPending"
[inputAction]="true"></progress-indicator>
</div>
</div>
<ul class="form-field__error-list">
<li *ngIf="form.errors._url?.$exists" class="form-field__error">
Cannot connect to {{ form.errors._url?.$exists }}
</li>
</ul>
</div>
<app-server-select
*ngIf="isExtension"
[showUrlControl]="form.controls.showUrl"
[urlControl]="form.controls.url"
[formErrors]="form.errors"
></app-server-select>
<div class="auth-form__actions">
<button
id="loginSubmit"
......@@ -104,7 +80,8 @@
[inProgress]="hasLoginStarted"