diff options
-rw-r--r-- | client/src/app/login/login.component.html | 10 | ||||
-rw-r--r-- | client/src/app/login/login.component.scss | 9 | ||||
-rw-r--r-- | client/src/app/login/login.component.ts | 7 | ||||
-rw-r--r-- | server/lib/auth.ts | 18 | ||||
-rw-r--r-- | server/lib/client-html.ts | 2 | ||||
-rw-r--r-- | server/lib/plugins/register-helpers-store.ts | 4 | ||||
-rw-r--r-- | server/models/server/plugin.ts | 44 |
7 files changed, 61 insertions, 33 deletions
diff --git a/client/src/app/login/login.component.html b/client/src/app/login/login.component.html index b0639d8ca..a935c86c3 100644 --- a/client/src/app/login/login.component.html +++ b/client/src/app/login/login.component.html | |||
@@ -3,7 +3,11 @@ | |||
3 | Login | 3 | Login |
4 | </div> | 4 | </div> |
5 | 5 | ||
6 | <ng-container *ngIf="!isAuthenticatedWithExternalAuth"> | 6 | <div class="alert alert-danger" i18n *ngIf="externalAuthError"> |
7 | Sorry but there was an issue with the external login process. Please <a routerLink="/about">contact an administrator</a>. | ||
8 | </div> | ||
9 | |||
10 | <ng-container *ngIf="!externalAuthError && !isAuthenticatedWithExternalAuth"> | ||
7 | <div class="alert alert-info" *ngIf="signupAllowed === false" role="alert"> | 11 | <div class="alert alert-info" *ngIf="signupAllowed === false" role="alert"> |
8 | <h6 class="alert-heading" i18n> | 12 | <h6 class="alert-heading" i18n> |
9 | If you are looking for an account… | 13 | If you are looking for an account… |
@@ -63,8 +67,8 @@ | |||
63 | <div class="external-login-blocks" *ngIf="getExternalLogins().length !== 0"> | 67 | <div class="external-login-blocks" *ngIf="getExternalLogins().length !== 0"> |
64 | <div class="block-title" i18n>Or sign in with</div> | 68 | <div class="block-title" i18n>Or sign in with</div> |
65 | 69 | ||
66 | <div class="external-login-block"> | 70 | <div> |
67 | <a *ngFor="let auth of getExternalLogins()" [href]="getAuthHref(auth)" role="button"> | 71 | <a class="external-login-block" *ngFor="let auth of getExternalLogins()" [href]="getAuthHref(auth)" role="button"> |
68 | {{ auth.authDisplayName }} | 72 | {{ auth.authDisplayName }} |
69 | </a> | 73 | </a> |
70 | </div> | 74 | </div> |
diff --git a/client/src/app/login/login.component.scss b/client/src/app/login/login.component.scss index ccc98c12a..db9f78f7c 100644 --- a/client/src/app/login/login.component.scss +++ b/client/src/app/login/login.component.scss | |||
@@ -38,7 +38,6 @@ input[type=submit] { | |||
38 | } | 38 | } |
39 | 39 | ||
40 | .external-login-blocks { | 40 | .external-login-blocks { |
41 | padding: 0 10px 10px 10px; | ||
42 | min-width: 200px; | 41 | min-width: 200px; |
43 | 42 | ||
44 | .block-title { | 43 | .block-title { |
@@ -46,9 +45,12 @@ input[type=submit] { | |||
46 | } | 45 | } |
47 | 46 | ||
48 | .external-login-block { | 47 | .external-login-block { |
48 | @include disable-default-a-behaviour; | ||
49 | |||
49 | cursor: pointer; | 50 | cursor: pointer; |
50 | border: 1px solid #d1d7e0; | 51 | border: 1px solid #d1d7e0; |
51 | border-radius: 5px; | 52 | border-radius: 5px; |
53 | color: var(--mainForegroundColor); | ||
52 | margin: 10px 10px 0 0; | 54 | margin: 10px 10px 0 0; |
53 | display: flex; | 55 | display: flex; |
54 | justify-content: center; | 56 | justify-content: center; |
@@ -59,11 +61,6 @@ input[type=submit] { | |||
59 | &:hover { | 61 | &:hover { |
60 | background-color: rgba(209, 215, 224, 0.5) | 62 | background-color: rgba(209, 215, 224, 0.5) |
61 | } | 63 | } |
62 | |||
63 | a { | ||
64 | @include disable-default-a-behaviour; | ||
65 | color: var(--mainForegroundColor); | ||
66 | } | ||
67 | } | 64 | } |
68 | } | 65 | } |
69 | } | 66 | } |
diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts index 5db8d3dbb..5d935cb49 100644 --- a/client/src/app/login/login.component.ts +++ b/client/src/app/login/login.component.ts | |||
@@ -23,7 +23,9 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni | |||
23 | 23 | ||
24 | error: string = null | 24 | error: string = null |
25 | forgotPasswordEmail = '' | 25 | forgotPasswordEmail = '' |
26 | |||
26 | isAuthenticatedWithExternalAuth = false | 27 | isAuthenticatedWithExternalAuth = false |
28 | externalAuthError = false | ||
27 | externalLogins: string[] = [] | 29 | externalLogins: string[] = [] |
28 | 30 | ||
29 | private openedForgotPasswordModal: NgbModalRef | 31 | private openedForgotPasswordModal: NgbModalRef |
@@ -61,6 +63,11 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni | |||
61 | return | 63 | return |
62 | } | 64 | } |
63 | 65 | ||
66 | if (snapshot.queryParams.externalAuthError) { | ||
67 | this.externalAuthError = true | ||
68 | return | ||
69 | } | ||
70 | |||
64 | this.buildForm({ | 71 | this.buildForm({ |
65 | username: this.loginValidatorsService.LOGIN_USERNAME, | 72 | username: this.loginValidatorsService.LOGIN_USERNAME, |
66 | password: this.loginValidatorsService.LOGIN_PASSWORD | 73 | password: this.loginValidatorsService.LOGIN_PASSWORD |
diff --git a/server/lib/auth.ts b/server/lib/auth.ts index 1fa896f6e..7c1dd1139 100644 --- a/server/lib/auth.ts +++ b/server/lib/auth.ts | |||
@@ -83,10 +83,13 @@ async function onExternalUserAuthenticated (options: { | |||
83 | return | 83 | return |
84 | } | 84 | } |
85 | 85 | ||
86 | if (!isAuthResultValid(npmName, authName, authResult)) return | ||
87 | |||
88 | const { res } = authResult | 86 | const { res } = authResult |
89 | 87 | ||
88 | if (!isAuthResultValid(npmName, authName, authResult)) { | ||
89 | res.redirect('/login?externalAuthError=true') | ||
90 | return | ||
91 | } | ||
92 | |||
90 | logger.info('Generating auth bypass token for %s in auth %s of plugin %s.', authResult.username, authName, npmName) | 93 | logger.info('Generating auth bypass token for %s in auth %s of plugin %s.', authResult.username, authName, npmName) |
91 | 94 | ||
92 | const bypassToken = await generateRandomString(32) | 95 | const bypassToken = await generateRandomString(32) |
@@ -238,24 +241,27 @@ function proxifyExternalAuthBypass (req: express.Request, res: express.Response) | |||
238 | 241 | ||
239 | function isAuthResultValid (npmName: string, authName: string, result: RegisterServerAuthenticatedResult) { | 242 | function isAuthResultValid (npmName: string, authName: string, result: RegisterServerAuthenticatedResult) { |
240 | if (!isUserUsernameValid(result.username)) { | 243 | if (!isUserUsernameValid(result.username)) { |
241 | logger.error('Auth method %s of plugin %s did not provide a valid username.', authName, npmName, { result }) | 244 | logger.error('Auth method %s of plugin %s did not provide a valid username.', authName, npmName, { username: result.username }) |
242 | return false | 245 | return false |
243 | } | 246 | } |
244 | 247 | ||
245 | if (!result.email) { | 248 | if (!result.email) { |
246 | logger.error('Auth method %s of plugin %s did not provide a valid email.', authName, npmName, { result }) | 249 | logger.error('Auth method %s of plugin %s did not provide a valid email.', authName, npmName, { email: result.email }) |
247 | return false | 250 | return false |
248 | } | 251 | } |
249 | 252 | ||
250 | // role is optional | 253 | // role is optional |
251 | if (result.role && !isUserRoleValid(result.role)) { | 254 | if (result.role && !isUserRoleValid(result.role)) { |
252 | logger.error('Auth method %s of plugin %s did not provide a valid role.', authName, npmName, { result }) | 255 | logger.error('Auth method %s of plugin %s did not provide a valid role.', authName, npmName, { role: result.role }) |
253 | return false | 256 | return false |
254 | } | 257 | } |
255 | 258 | ||
256 | // display name is optional | 259 | // display name is optional |
257 | if (result.displayName && !isUserDisplayNameValid(result.displayName)) { | 260 | if (result.displayName && !isUserDisplayNameValid(result.displayName)) { |
258 | logger.error('Auth method %s of plugin %s did not provide a valid display name.', authName, npmName, { result }) | 261 | logger.error( |
262 | 'Auth method %s of plugin %s did not provide a valid display name.', | ||
263 | authName, npmName, { displayName: result.displayName } | ||
264 | ) | ||
259 | return false | 265 | return false |
260 | } | 266 | } |
261 | 267 | ||
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index 572bd03bd..4a4b0d12f 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts | |||
@@ -119,7 +119,7 @@ export class ClientHtml { | |||
119 | // Save locale in cookies | 119 | // Save locale in cookies |
120 | res.cookie('clientLanguage', lang, { | 120 | res.cookie('clientLanguage', lang, { |
121 | secure: WEBSERVER.SCHEME === 'https', | 121 | secure: WEBSERVER.SCHEME === 'https', |
122 | sameSite: true, | 122 | sameSite: 'none', |
123 | maxAge: 1000 * 3600 * 24 * 90 // 3 months | 123 | maxAge: 1000 * 3600 * 24 * 90 // 3 months |
124 | }) | 124 | }) |
125 | 125 | ||
diff --git a/server/lib/plugins/register-helpers-store.ts b/server/lib/plugins/register-helpers-store.ts index a3ec7ef6a..e337b1cb0 100644 --- a/server/lib/plugins/register-helpers-store.ts +++ b/server/lib/plugins/register-helpers-store.ts | |||
@@ -230,9 +230,9 @@ export class RegisterHelpersStore { | |||
230 | 230 | ||
231 | private buildSettingsManager (): PluginSettingsManager { | 231 | private buildSettingsManager (): PluginSettingsManager { |
232 | return { | 232 | return { |
233 | getSetting: (name: string) => PluginModel.getSetting(this.plugin.name, this.plugin.type, name), | 233 | getSetting: (name: string) => PluginModel.getSetting(this.plugin.name, this.plugin.type, name, this.settings), |
234 | 234 | ||
235 | getSettings: (names: string[]) => PluginModel.getSettings(this.plugin.name, this.plugin.type, names), | 235 | getSettings: (names: string[]) => PluginModel.getSettings(this.plugin.name, this.plugin.type, names, this.settings), |
236 | 236 | ||
237 | setSetting: (name: string, value: string) => PluginModel.setSetting(this.plugin.name, this.plugin.type, name, value), | 237 | setSetting: (name: string, value: string) => PluginModel.setSetting(this.plugin.name, this.plugin.type, name, value), |
238 | 238 | ||
diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts index 83c873c5b..3f88ac26d 100644 --- a/server/models/server/plugin.ts +++ b/server/models/server/plugin.ts | |||
@@ -1,5 +1,10 @@ | |||
1 | import * as Bluebird from 'bluebird' | ||
2 | import { FindAndCountOptions, json } from 'sequelize' | ||
1 | import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 3 | import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { getSort, throwIfNotValid } from '../utils' | 4 | import { MPlugin, MPluginFormattable } from '@server/typings/models' |
5 | import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model' | ||
6 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | ||
7 | import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' | ||
3 | import { | 8 | import { |
4 | isPluginDescriptionValid, | 9 | isPluginDescriptionValid, |
5 | isPluginHomepage, | 10 | isPluginHomepage, |
@@ -7,12 +12,7 @@ import { | |||
7 | isPluginTypeValid, | 12 | isPluginTypeValid, |
8 | isPluginVersionValid | 13 | isPluginVersionValid |
9 | } from '../../helpers/custom-validators/plugins' | 14 | } from '../../helpers/custom-validators/plugins' |
10 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | 15 | import { getSort, throwIfNotValid } from '../utils' |
11 | import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model' | ||
12 | import { FindAndCountOptions, json } from 'sequelize' | ||
13 | import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' | ||
14 | import * as Bluebird from 'bluebird' | ||
15 | import { MPlugin, MPluginFormattable } from '@server/typings/models' | ||
16 | 16 | ||
17 | @DefaultScope(() => ({ | 17 | @DefaultScope(() => ({ |
18 | attributes: { | 18 | attributes: { |
@@ -112,7 +112,7 @@ export class PluginModel extends Model<PluginModel> { | |||
112 | return PluginModel.findOne(query) | 112 | return PluginModel.findOne(query) |
113 | } | 113 | } |
114 | 114 | ||
115 | static getSetting (pluginName: string, pluginType: PluginType, settingName: string) { | 115 | static getSetting (pluginName: string, pluginType: PluginType, settingName: string, registeredSettings: RegisterServerSettingOptions[]) { |
116 | const query = { | 116 | const query = { |
117 | attributes: [ 'settings' ], | 117 | attributes: [ 'settings' ], |
118 | where: { | 118 | where: { |
@@ -123,13 +123,23 @@ export class PluginModel extends Model<PluginModel> { | |||
123 | 123 | ||
124 | return PluginModel.findOne(query) | 124 | return PluginModel.findOne(query) |
125 | .then(p => { | 125 | .then(p => { |
126 | if (!p || !p.settings) return undefined | 126 | if (!p || p.settings === undefined) { |
127 | const registered = registeredSettings.find(s => s.name === settingName) | ||
128 | if (!registered || registered.default === undefined) return undefined | ||
129 | |||
130 | return registered.default | ||
131 | } | ||
127 | 132 | ||
128 | return p.settings[settingName] | 133 | return p.settings[settingName] |
129 | }) | 134 | }) |
130 | } | 135 | } |
131 | 136 | ||
132 | static getSettings (pluginName: string, pluginType: PluginType, settingNames: string[]) { | 137 | static getSettings ( |
138 | pluginName: string, | ||
139 | pluginType: PluginType, | ||
140 | settingNames: string[], | ||
141 | registeredSettings: RegisterServerSettingOptions[] | ||
142 | ) { | ||
133 | const query = { | 143 | const query = { |
134 | attributes: [ 'settings' ], | 144 | attributes: [ 'settings' ], |
135 | where: { | 145 | where: { |
@@ -140,13 +150,17 @@ export class PluginModel extends Model<PluginModel> { | |||
140 | 150 | ||
141 | return PluginModel.findOne(query) | 151 | return PluginModel.findOne(query) |
142 | .then(p => { | 152 | .then(p => { |
143 | if (!p || !p.settings) return {} | 153 | const result: { [settingName: string ]: string | boolean } = {} |
144 | 154 | ||
145 | const result: { [settingName: string ]: string } = {} | 155 | for (const name of settingNames) { |
156 | if (!p || p.settings[name] === undefined) { | ||
157 | const registered = registeredSettings.find(s => s.name === name) | ||
146 | 158 | ||
147 | for (const key of Object.keys(p.settings)) { | 159 | if (registered?.default !== undefined) { |
148 | if (settingNames.includes(key)) { | 160 | result[name] = registered.default |
149 | result[key] = p.settings[key] | 161 | } |
162 | } else { | ||
163 | result[name] = p.settings[name] | ||
150 | } | 164 | } |
151 | } | 165 | } |
152 | 166 | ||