aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared
diff options
context:
space:
mode:
Diffstat (limited to 'shared')
-rw-r--r--shared/models/users/index.ts1
-rw-r--r--shared/models/users/two-factor-enable-result.model.ts7
-rw-r--r--shared/models/users/user.model.ts2
-rw-r--r--shared/server-commands/server/server.ts12
-rw-r--r--shared/server-commands/users/index.ts1
-rw-r--r--shared/server-commands/users/login-command.ts73
-rw-r--r--shared/server-commands/users/two-factor-command.ts92
-rw-r--r--shared/server-commands/users/users-command.ts3
8 files changed, 166 insertions, 25 deletions
diff --git a/shared/models/users/index.ts b/shared/models/users/index.ts
index b25978587..32f7a441c 100644
--- a/shared/models/users/index.ts
+++ b/shared/models/users/index.ts
@@ -1,3 +1,4 @@
1export * from './two-factor-enable-result.model'
1export * from './user-create-result.model' 2export * from './user-create-result.model'
2export * from './user-create.model' 3export * from './user-create.model'
3export * from './user-flag.model' 4export * from './user-flag.model'
diff --git a/shared/models/users/two-factor-enable-result.model.ts b/shared/models/users/two-factor-enable-result.model.ts
new file mode 100644
index 000000000..1fc801f0a
--- /dev/null
+++ b/shared/models/users/two-factor-enable-result.model.ts
@@ -0,0 +1,7 @@
1export interface TwoFactorEnableResult {
2 otpRequest: {
3 requestToken: string
4 secret: string
5 uri: string
6 }
7}
diff --git a/shared/models/users/user.model.ts b/shared/models/users/user.model.ts
index 63c5c8a92..7b6494ff8 100644
--- a/shared/models/users/user.model.ts
+++ b/shared/models/users/user.model.ts
@@ -62,6 +62,8 @@ export interface User {
62 pluginAuth: string | null 62 pluginAuth: string | null
63 63
64 lastLoginDate: Date | null 64 lastLoginDate: Date | null
65
66 twoFactorEnabled: boolean
65} 67}
66 68
67export interface MyUserSpecialPlaylist { 69export interface MyUserSpecialPlaylist {
diff --git a/shared/server-commands/server/server.ts b/shared/server-commands/server/server.ts
index a8f8c1d84..7096faf21 100644
--- a/shared/server-commands/server/server.ts
+++ b/shared/server-commands/server/server.ts
@@ -13,7 +13,15 @@ import { AbusesCommand } from '../moderation'
13import { OverviewsCommand } from '../overviews' 13import { OverviewsCommand } from '../overviews'
14import { SearchCommand } from '../search' 14import { SearchCommand } from '../search'
15import { SocketIOCommand } from '../socket' 15import { SocketIOCommand } from '../socket'
16import { AccountsCommand, BlocklistCommand, LoginCommand, NotificationsCommand, SubscriptionsCommand, UsersCommand } from '../users' 16import {
17 AccountsCommand,
18 BlocklistCommand,
19 LoginCommand,
20 NotificationsCommand,
21 SubscriptionsCommand,
22 TwoFactorCommand,
23 UsersCommand
24} from '../users'
17import { 25import {
18 BlacklistCommand, 26 BlacklistCommand,
19 CaptionsCommand, 27 CaptionsCommand,
@@ -136,6 +144,7 @@ export class PeerTubeServer {
136 videos?: VideosCommand 144 videos?: VideosCommand
137 videoStats?: VideoStatsCommand 145 videoStats?: VideoStatsCommand
138 views?: ViewsCommand 146 views?: ViewsCommand
147 twoFactor?: TwoFactorCommand
139 148
140 constructor (options: { serverNumber: number } | { url: string }) { 149 constructor (options: { serverNumber: number } | { url: string }) {
141 if ((options as any).url) { 150 if ((options as any).url) {
@@ -417,5 +426,6 @@ export class PeerTubeServer {
417 this.videoStudio = new VideoStudioCommand(this) 426 this.videoStudio = new VideoStudioCommand(this)
418 this.videoStats = new VideoStatsCommand(this) 427 this.videoStats = new VideoStatsCommand(this)
419 this.views = new ViewsCommand(this) 428 this.views = new ViewsCommand(this)
429 this.twoFactor = new TwoFactorCommand(this)
420 } 430 }
421} 431}
diff --git a/shared/server-commands/users/index.ts b/shared/server-commands/users/index.ts
index f6f93b4d2..1afc02dc1 100644
--- a/shared/server-commands/users/index.ts
+++ b/shared/server-commands/users/index.ts
@@ -5,4 +5,5 @@ export * from './login'
5export * from './login-command' 5export * from './login-command'
6export * from './notifications-command' 6export * from './notifications-command'
7export * from './subscriptions-command' 7export * from './subscriptions-command'
8export * from './two-factor-command'
8export * from './users-command' 9export * from './users-command'
diff --git a/shared/server-commands/users/login-command.ts b/shared/server-commands/users/login-command.ts
index 54070e426..f2fc6d1c5 100644
--- a/shared/server-commands/users/login-command.ts
+++ b/shared/server-commands/users/login-command.ts
@@ -2,34 +2,27 @@ import { HttpStatusCode, PeerTubeProblemDocument } from '@shared/models'
2import { unwrapBody } from '../requests' 2import { unwrapBody } from '../requests'
3import { AbstractCommand, OverrideCommandOptions } from '../shared' 3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4 4
5type LoginOptions = OverrideCommandOptions & {
6 client?: { id?: string, secret?: string }
7 user?: { username: string, password?: string }
8 otpToken?: string
9}
10
5export class LoginCommand extends AbstractCommand { 11export class LoginCommand extends AbstractCommand {
6 12
7 login (options: OverrideCommandOptions & { 13 async login (options: LoginOptions = {}) {
8 client?: { id?: string, secret?: string } 14 const res = await this._login(options)
9 user?: { username: string, password?: string }
10 } = {}) {
11 const { client = this.server.store.client, user = this.server.store.user } = options
12 const path = '/api/v1/users/token'
13 15
14 const body = { 16 return this.unwrapLoginBody(res.body)
15 client_id: client.id, 17 }
16 client_secret: client.secret,
17 username: user.username,
18 password: user.password ?? 'password',
19 response_type: 'code',
20 grant_type: 'password',
21 scope: 'upload'
22 }
23 18
24 return unwrapBody<{ access_token: string, refresh_token: string } & PeerTubeProblemDocument>(this.postBodyRequest({ 19 async loginAndGetResponse (options: LoginOptions = {}) {
25 ...options, 20 const res = await this._login(options)
26 21
27 path, 22 return {
28 requestType: 'form', 23 res,
29 fields: body, 24 body: this.unwrapLoginBody(res.body)
30 implicitToken: false, 25 }
31 defaultExpectedStatus: HttpStatusCode.OK_200
32 }))
33 } 26 }
34 27
35 getAccessToken (arg1?: { username: string, password?: string }): Promise<string> 28 getAccessToken (arg1?: { username: string, password?: string }): Promise<string>
@@ -129,4 +122,38 @@ export class LoginCommand extends AbstractCommand {
129 defaultExpectedStatus: HttpStatusCode.OK_200 122 defaultExpectedStatus: HttpStatusCode.OK_200
130 }) 123 })
131 } 124 }
125
126 private _login (options: LoginOptions) {
127 const { client = this.server.store.client, user = this.server.store.user, otpToken } = options
128 const path = '/api/v1/users/token'
129
130 const body = {
131 client_id: client.id,
132 client_secret: client.secret,
133 username: user.username,
134 password: user.password ?? 'password',
135 response_type: 'code',
136 grant_type: 'password',
137 scope: 'upload'
138 }
139
140 const headers = otpToken
141 ? { 'x-peertube-otp': otpToken }
142 : {}
143
144 return this.postBodyRequest({
145 ...options,
146
147 path,
148 headers,
149 requestType: 'form',
150 fields: body,
151 implicitToken: false,
152 defaultExpectedStatus: HttpStatusCode.OK_200
153 })
154 }
155
156 private unwrapLoginBody (body: any) {
157 return body as { access_token: string, refresh_token: string } & PeerTubeProblemDocument
158 }
132} 159}
diff --git a/shared/server-commands/users/two-factor-command.ts b/shared/server-commands/users/two-factor-command.ts
new file mode 100644
index 000000000..5542acfda
--- /dev/null
+++ b/shared/server-commands/users/two-factor-command.ts
@@ -0,0 +1,92 @@
1import { TOTP } from 'otpauth'
2import { HttpStatusCode, TwoFactorEnableResult } from '@shared/models'
3import { unwrapBody } from '../requests'
4import { AbstractCommand, OverrideCommandOptions } from '../shared'
5
6export class TwoFactorCommand extends AbstractCommand {
7
8 static buildOTP (options: {
9 secret: string
10 }) {
11 const { secret } = options
12
13 return new TOTP({
14 issuer: 'PeerTube',
15 algorithm: 'SHA1',
16 digits: 6,
17 period: 30,
18 secret
19 })
20 }
21
22 request (options: OverrideCommandOptions & {
23 userId: number
24 currentPassword?: string
25 }) {
26 const { currentPassword, userId } = options
27
28 const path = '/api/v1/users/' + userId + '/two-factor/request'
29
30 return unwrapBody<TwoFactorEnableResult>(this.postBodyRequest({
31 ...options,
32
33 path,
34 fields: { currentPassword },
35 implicitToken: true,
36 defaultExpectedStatus: HttpStatusCode.OK_200
37 }))
38 }
39
40 confirmRequest (options: OverrideCommandOptions & {
41 userId: number
42 requestToken: string
43 otpToken: string
44 }) {
45 const { userId, requestToken, otpToken } = options
46
47 const path = '/api/v1/users/' + userId + '/two-factor/confirm-request'
48
49 return this.postBodyRequest({
50 ...options,
51
52 path,
53 fields: { requestToken, otpToken },
54 implicitToken: true,
55 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
56 })
57 }
58
59 disable (options: OverrideCommandOptions & {
60 userId: number
61 currentPassword?: string
62 }) {
63 const { userId, currentPassword } = options
64 const path = '/api/v1/users/' + userId + '/two-factor/disable'
65
66 return this.postBodyRequest({
67 ...options,
68
69 path,
70 fields: { currentPassword },
71 implicitToken: true,
72 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
73 })
74 }
75
76 async requestAndConfirm (options: OverrideCommandOptions & {
77 userId: number
78 currentPassword?: string
79 }) {
80 const { userId, currentPassword } = options
81
82 const { otpRequest } = await this.request({ userId, currentPassword })
83
84 await this.confirmRequest({
85 userId,
86 requestToken: otpRequest.requestToken,
87 otpToken: TwoFactorCommand.buildOTP({ secret: otpRequest.secret }).generate()
88 })
89
90 return otpRequest
91 }
92}
diff --git a/shared/server-commands/users/users-command.ts b/shared/server-commands/users/users-command.ts
index e7d021059..811b9685b 100644
--- a/shared/server-commands/users/users-command.ts
+++ b/shared/server-commands/users/users-command.ts
@@ -202,7 +202,8 @@ export class UsersCommand extends AbstractCommand {
202 token, 202 token,
203 userId: user.id, 203 userId: user.id,
204 userChannelId: me.videoChannels[0].id, 204 userChannelId: me.videoChannels[0].id,
205 userChannelName: me.videoChannels[0].name 205 userChannelName: me.videoChannels[0].name,
206 password
206 } 207 }
207 } 208 }
208 209