diff options
-rw-r--r-- | client/src/app/login/login.component.html | 68 | ||||
-rw-r--r-- | client/src/app/login/login.component.scss | 42 | ||||
-rw-r--r-- | client/src/app/login/login.component.ts | 24 | ||||
-rw-r--r-- | client/src/sass/include/_variables.scss | 2 | ||||
-rw-r--r-- | server/controllers/api/config.ts | 4 | ||||
-rw-r--r-- | server/lib/plugins/plugin-manager.ts | 16 | ||||
-rw-r--r-- | shared/models/server/server-config.model.ts | 4 |
7 files changed, 122 insertions, 38 deletions
diff --git a/client/src/app/login/login.component.html b/client/src/app/login/login.component.html index 3e53e5854..b0639d8ca 100644 --- a/client/src/app/login/login.component.html +++ b/client/src/app/login/login.component.html | |||
@@ -23,40 +23,54 @@ | |||
23 | <span *ngIf="error === 'User email is not verified.'"> <a i18n routerLink="/verify-account/ask-send-email">Request new verification email.</a></span> | 23 | <span *ngIf="error === 'User email is not verified.'"> <a i18n routerLink="/verify-account/ask-send-email">Request new verification email.</a></span> |
24 | </div> | 24 | </div> |
25 | 25 | ||
26 | <form role="form" (ngSubmit)="login()" [formGroup]="form"> | 26 | <div class="login-form-and-externals"> |
27 | <div class="form-group"> | 27 | |
28 | <div> | 28 | <form role="form" (ngSubmit)="login()" [formGroup]="form"> |
29 | <label i18n for="username">User</label> | 29 | <div class="form-group"> |
30 | <input | 30 | <div> |
31 | type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1" | 31 | <label i18n for="username">User</label> |
32 | formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" #emailInput | 32 | <input |
33 | > | 33 | type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1" |
34 | <a i18n *ngIf="signupAllowed === true" routerLink="/signup" class="create-an-account"> | 34 | formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" #usernameInput |
35 | or create an account | 35 | > |
36 | </a> | 36 | <a i18n *ngIf="signupAllowed === true" routerLink="/signup" class="create-an-account"> |
37 | or create an account | ||
38 | </a> | ||
39 | </div> | ||
40 | |||
41 | <div *ngIf="formErrors.username" class="form-error"> | ||
42 | {{ formErrors.username }} | ||
43 | </div> | ||
37 | </div> | 44 | </div> |
38 | 45 | ||
39 | <div *ngIf="formErrors.username" class="form-error"> | 46 | <div class="form-group"> |
40 | {{ formErrors.username }} | 47 | <label i18n for="password">Password</label> |
48 | <div> | ||
49 | <input | ||
50 | type="password" name="password" id="password" i18n-placeholder placeholder="Password" required tabindex="2" autocomplete="current-password" | ||
51 | formControlName="password" class="form-control" [ngClass]="{ 'input-error': formErrors['password'] }" | ||
52 | > | ||
53 | <a i18n-title class="forgot-password-button" (click)="openForgotPasswordModal()" title="Click here to reset your password">I forgot my password</a> | ||
54 | </div> | ||
55 | <div *ngIf="formErrors.password" class="form-error"> | ||
56 | {{ formErrors.password }} | ||
57 | </div> | ||
41 | </div> | 58 | </div> |
42 | </div> | ||
43 | 59 | ||
44 | <div class="form-group"> | 60 | <input type="submit" i18n-value value="Login" [disabled]="!form.valid"> |
45 | <label i18n for="password">Password</label> | 61 | </form> |
46 | <div> | 62 | |
47 | <input | 63 | <div class="external-login-blocks" *ngIf="getExternalLogins().length !== 0"> |
48 | type="password" name="password" id="password" i18n-placeholder placeholder="Password" required tabindex="2" autocomplete="current-password" | 64 | <div class="block-title" i18n>Or sign in with</div> |
49 | formControlName="password" class="form-control" [ngClass]="{ 'input-error': formErrors['password'] }" | 65 | |
50 | > | 66 | <div class="external-login-block"> |
51 | <a i18n-title class="forgot-password-button" (click)="openForgotPasswordModal()" title="Click here to reset your password">I forgot my password</a> | 67 | <a *ngFor="let auth of getExternalLogins()" [href]="getAuthHref(auth)" role="button"> |
52 | </div> | 68 | {{ auth.authDisplayName }} |
53 | <div *ngIf="formErrors.password" class="form-error"> | 69 | </a> |
54 | {{ formErrors.password }} | ||
55 | </div> | 70 | </div> |
56 | </div> | 71 | </div> |
72 | </div> | ||
57 | 73 | ||
58 | <input type="submit" i18n-value value="Login" [disabled]="!form.valid"> | ||
59 | </form> | ||
60 | </ng-container> | 74 | </ng-container> |
61 | </div> | 75 | </div> |
62 | 76 | ||
diff --git a/client/src/app/login/login.component.scss b/client/src/app/login/login.component.scss index 8ac231475..ccc98c12a 100644 --- a/client/src/app/login/login.component.scss +++ b/client/src/app/login/login.component.scss | |||
@@ -21,9 +21,49 @@ input[type=submit] { | |||
21 | color: var(--mainForegroundColor); | 21 | color: var(--mainForegroundColor); |
22 | cursor: pointer; | 22 | cursor: pointer; |
23 | transition: opacity cubic-bezier(0.39, 0.575, 0.565, 1); | 23 | transition: opacity cubic-bezier(0.39, 0.575, 0.565, 1); |
24 | 24 | ||
25 | &:hover { | 25 | &:hover { |
26 | text-decoration: none !important; | 26 | text-decoration: none !important; |
27 | opacity: .7 !important; | 27 | opacity: .7 !important; |
28 | } | 28 | } |
29 | } | 29 | } |
30 | |||
31 | .login-form-and-externals { | ||
32 | display: flex; | ||
33 | flex-wrap: wrap; | ||
34 | font-size: 15px; | ||
35 | |||
36 | form { | ||
37 | margin: 0 50px 20px 0; | ||
38 | } | ||
39 | |||
40 | .external-login-blocks { | ||
41 | padding: 0 10px 10px 10px; | ||
42 | min-width: 200px; | ||
43 | |||
44 | .block-title { | ||
45 | font-weight: $font-semibold; | ||
46 | } | ||
47 | |||
48 | .external-login-block { | ||
49 | cursor: pointer; | ||
50 | border: 1px solid #d1d7e0; | ||
51 | border-radius: 5px; | ||
52 | margin: 10px 10px 0 0; | ||
53 | display: flex; | ||
54 | justify-content: center; | ||
55 | align-items: center; | ||
56 | min-height: 35px; | ||
57 | min-width: 100px; | ||
58 | |||
59 | &:hover { | ||
60 | background-color: rgba(209, 215, 224, 0.5) | ||
61 | } | ||
62 | |||
63 | a { | ||
64 | @include disable-default-a-behaviour; | ||
65 | color: var(--mainForegroundColor); | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | } | ||
diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts index 9c8f5c52e..5db8d3dbb 100644 --- a/client/src/app/login/login.component.ts +++ b/client/src/app/login/login.component.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, OnInit, ViewChild, AfterViewInit } from '@angular/core' |
2 | import { Notifier, RedirectService } from '@app/core' | 2 | import { Notifier, RedirectService } from '@app/core' |
3 | import { UserService } from '@app/shared' | 3 | import { UserService } from '@app/shared' |
4 | import { AuthService } from '../core' | 4 | import { AuthService } from '../core' |
@@ -8,7 +8,8 @@ import { FormValidatorService } from '@app/shared/forms/form-validators/form-val | |||
8 | import { LoginValidatorsService } from '@app/shared/forms/form-validators/login-validators.service' | 8 | import { LoginValidatorsService } from '@app/shared/forms/form-validators/login-validators.service' |
9 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' | 9 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' |
10 | import { ActivatedRoute } from '@angular/router' | 10 | import { ActivatedRoute } from '@angular/router' |
11 | import { ServerConfig } from '@shared/models/server/server-config.model' | 11 | import { ServerConfig, RegisteredExternalAuthConfig } from '@shared/models/server/server-config.model' |
12 | import { environment } from 'src/environments/environment' | ||
12 | 13 | ||
13 | @Component({ | 14 | @Component({ |
14 | selector: 'my-login', | 15 | selector: 'my-login', |
@@ -16,13 +17,14 @@ import { ServerConfig } from '@shared/models/server/server-config.model' | |||
16 | styleUrls: [ './login.component.scss' ] | 17 | styleUrls: [ './login.component.scss' ] |
17 | }) | 18 | }) |
18 | 19 | ||
19 | export class LoginComponent extends FormReactive implements OnInit { | 20 | export class LoginComponent extends FormReactive implements OnInit, AfterViewInit { |
20 | @ViewChild('emailInput', { static: true }) input: ElementRef | 21 | @ViewChild('usernameInput', { static: false }) usernameInput: ElementRef |
21 | @ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef | 22 | @ViewChild('forgotPasswordModal', { static: true }) forgotPasswordModal: ElementRef |
22 | 23 | ||
23 | error: string = null | 24 | error: string = null |
24 | forgotPasswordEmail = '' | 25 | forgotPasswordEmail = '' |
25 | isAuthenticatedWithExternalAuth = false | 26 | isAuthenticatedWithExternalAuth = false |
27 | externalLogins: string[] = [] | ||
26 | 28 | ||
27 | private openedForgotPasswordModal: NgbModalRef | 29 | private openedForgotPasswordModal: NgbModalRef |
28 | private serverConfig: ServerConfig | 30 | private serverConfig: ServerConfig |
@@ -63,8 +65,20 @@ export class LoginComponent extends FormReactive implements OnInit { | |||
63 | username: this.loginValidatorsService.LOGIN_USERNAME, | 65 | username: this.loginValidatorsService.LOGIN_USERNAME, |
64 | password: this.loginValidatorsService.LOGIN_PASSWORD | 66 | password: this.loginValidatorsService.LOGIN_PASSWORD |
65 | }) | 67 | }) |
68 | } | ||
69 | |||
70 | ngAfterViewInit () { | ||
71 | if (this.usernameInput) { | ||
72 | this.usernameInput.nativeElement.focus() | ||
73 | } | ||
74 | } | ||
75 | |||
76 | getExternalLogins () { | ||
77 | return this.serverConfig.plugin.registeredExternalAuths | ||
78 | } | ||
66 | 79 | ||
67 | this.input.nativeElement.focus() | 80 | getAuthHref (auth: RegisteredExternalAuthConfig) { |
81 | return environment.apiUrl + `/plugins/${auth.name}/${auth.version}/auth/${auth.authName}` | ||
68 | } | 82 | } |
69 | 83 | ||
70 | login () { | 84 | login () { |
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss index 72eb7b61e..46f1e99f7 100644 --- a/client/src/sass/include/_variables.scss +++ b/client/src/sass/include/_variables.scss | |||
@@ -53,8 +53,6 @@ $sub-menu-color: #F7F7F7; | |||
53 | $footer-height: 30px; | 53 | $footer-height: 30px; |
54 | $footer-margin: 30px; | 54 | $footer-margin: 30px; |
55 | 55 | ||
56 | $footer-border-color: $header-border-color; | ||
57 | |||
58 | $separator-border-color: rgba(0, 0, 0, 0.10); | 56 | $separator-border-color: rgba(0, 0, 0, 0.10); |
59 | 57 | ||
60 | $video-miniature-width: 238px; | 58 | $video-miniature-width: 238px; |
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index e8941bc73..85f3ad3d9 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -278,6 +278,8 @@ function getIdAndPassAuthPlugins () { | |||
278 | for (const auth of p.idAndPassAuths) { | 278 | for (const auth of p.idAndPassAuths) { |
279 | result.push({ | 279 | result.push({ |
280 | npmName: p.npmName, | 280 | npmName: p.npmName, |
281 | name: p.name, | ||
282 | version: p.version, | ||
281 | authName: auth.authName, | 283 | authName: auth.authName, |
282 | weight: auth.getWeight() | 284 | weight: auth.getWeight() |
283 | }) | 285 | }) |
@@ -294,6 +296,8 @@ function getExternalAuthsPlugins () { | |||
294 | for (const auth of p.externalAuths) { | 296 | for (const auth of p.externalAuths) { |
295 | result.push({ | 297 | result.push({ |
296 | npmName: p.npmName, | 298 | npmName: p.npmName, |
299 | name: p.name, | ||
300 | version: p.version, | ||
297 | authName: auth.authName, | 301 | authName: auth.authName, |
298 | authDisplayName: auth.authDisplayName | 302 | authDisplayName: auth.authDisplayName |
299 | }) | 303 | }) |
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts index 38336bcc6..f7b84b1ff 100644 --- a/server/lib/plugins/plugin-manager.ts +++ b/server/lib/plugins/plugin-manager.ts | |||
@@ -106,14 +106,24 @@ export class PluginManager implements ServerHook { | |||
106 | 106 | ||
107 | getIdAndPassAuths () { | 107 | getIdAndPassAuths () { |
108 | return this.getRegisteredPlugins() | 108 | return this.getRegisteredPlugins() |
109 | .map(p => ({ npmName: p.npmName, idAndPassAuths: p.registerHelpersStore.getIdAndPassAuths() })) | 109 | .map(p => ({ |
110 | npmName: p.npmName, | ||
111 | name: p.name, | ||
112 | version: p.version, | ||
113 | idAndPassAuths: p.registerHelpersStore.getIdAndPassAuths() | ||
114 | })) | ||
110 | .filter(v => v.idAndPassAuths.length !== 0) | 115 | .filter(v => v.idAndPassAuths.length !== 0) |
111 | } | 116 | } |
112 | 117 | ||
113 | getExternalAuths () { | 118 | getExternalAuths () { |
114 | return this.getRegisteredPlugins() | 119 | return this.getRegisteredPlugins() |
115 | .map(p => ({ npmName: p.npmName, externalAuths: p.registerHelpersStore.getExternalAuths() })) | 120 | .map(p => ({ |
116 | .filter(v => v.externalAuths.length !== 0) | 121 | npmName: p.npmName, |
122 | name: p.name, | ||
123 | version: p.version, | ||
124 | externalAuths: p.registerHelpersStore.getExternalAuths() | ||
125 | })) | ||
126 | .filter(v => v.externalAuths.length !== 0) | ||
117 | } | 127 | } |
118 | 128 | ||
119 | getRegisteredSettings (npmName: string) { | 129 | getRegisteredSettings (npmName: string) { |
diff --git a/shared/models/server/server-config.model.ts b/shared/models/server/server-config.model.ts index 0ff079216..a1f9b3b5d 100644 --- a/shared/models/server/server-config.model.ts +++ b/shared/models/server/server-config.model.ts | |||
@@ -14,12 +14,16 @@ export interface ServerConfigTheme extends ServerConfigPlugin { | |||
14 | 14 | ||
15 | export interface RegisteredExternalAuthConfig { | 15 | export interface RegisteredExternalAuthConfig { |
16 | npmName: string | 16 | npmName: string |
17 | name: string | ||
18 | version: string | ||
17 | authName: string | 19 | authName: string |
18 | authDisplayName: string | 20 | authDisplayName: string |
19 | } | 21 | } |
20 | 22 | ||
21 | export interface RegisteredIdAndPassAuthConfig { | 23 | export interface RegisteredIdAndPassAuthConfig { |
22 | npmName: string | 24 | npmName: string |
25 | name: string | ||
26 | version: string | ||
23 | authName: string | 27 | authName: string |
24 | weight: number | 28 | weight: number |
25 | } | 29 | } |