From 41d1d075011174e73dccb74006181a92a618d7b4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 13 Jul 2021 11:05:15 +0200 Subject: Introduce login command --- shared/extra-utils/requests/requests.ts | 4 + shared/extra-utils/server/clients.ts | 20 ---- shared/extra-utils/server/servers.ts | 4 +- shared/extra-utils/shared/abstract-command.ts | 10 +- shared/extra-utils/users/index.ts | 1 + shared/extra-utils/users/login-command.ts | 134 ++++++++++++++++++++++++++ shared/extra-utils/users/login.ts | 120 +---------------------- shared/extra-utils/users/notifications.ts | 4 +- shared/extra-utils/users/users.ts | 5 +- 9 files changed, 156 insertions(+), 146 deletions(-) delete mode 100644 shared/extra-utils/server/clients.ts create mode 100644 shared/extra-utils/users/login-command.ts (limited to 'shared') diff --git a/shared/extra-utils/requests/requests.ts b/shared/extra-utils/requests/requests.ts index f9d112aca..c5ee63e05 100644 --- a/shared/extra-utils/requests/requests.ts +++ b/shared/extra-utils/requests/requests.ts @@ -27,6 +27,7 @@ function makeGetRequest (options: { range?: string redirects?: number accept?: string + host?: string }) { if (!options.statusCodeExpected) options.statusCodeExpected = HttpStatusCode.BAD_REQUEST_400 if (options.contentType === undefined) options.contentType = 'application/json' @@ -38,6 +39,7 @@ function makeGetRequest (options: { if (options.query) req.query(options.query) if (options.range) req.set('Range', options.range) if (options.accept) req.set('Accept', options.accept) + if (options.host) req.set('Host', options.host) if (options.redirects) req.redirects(options.redirects) return req.expect(options.statusCodeExpected) @@ -113,6 +115,7 @@ function makePostBodyRequest (options: { path: string token?: string fields?: { [ fieldName: string ]: any } + type?: string statusCodeExpected?: HttpStatusCode }) { if (!options.fields) options.fields = {} @@ -123,6 +126,7 @@ function makePostBodyRequest (options: { .set('Accept', 'application/json') if (options.token) req.set('Authorization', 'Bearer ' + options.token) + if (options.type) req.type(options.type) return req.send(options.fields) .expect(options.statusCodeExpected) diff --git a/shared/extra-utils/server/clients.ts b/shared/extra-utils/server/clients.ts deleted file mode 100644 index 894fe4911..000000000 --- a/shared/extra-utils/server/clients.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as request from 'supertest' -import { URL } from 'url' -import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' - -function getClient (url: string) { - const path = '/api/v1/oauth-clients/local' - - return request(url) - .get(path) - .set('Host', new URL(url).host) - .set('Accept', 'application/json') - .expect(HttpStatusCode.OK_200) - .expect('Content-Type', /json/) -} - -// --------------------------------------------------------------------------- - -export { - getClient -} diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts index f5dc0326f..4d9599680 100644 --- a/shared/extra-utils/server/servers.ts +++ b/shared/extra-utils/server/servers.ts @@ -16,7 +16,7 @@ import { AbusesCommand } from '../moderation' import { OverviewsCommand } from '../overviews' import { SearchCommand } from '../search' import { SocketIOCommand } from '../socket' -import { AccountsCommand, BlocklistCommand, NotificationsCommand, SubscriptionsCommand } from '../users' +import { AccountsCommand, BlocklistCommand, LoginCommand, NotificationsCommand, SubscriptionsCommand } from '../users' import { BlacklistCommand, CaptionsCommand, @@ -126,6 +126,7 @@ interface ServerInfo { sqlCommand?: SQLCommand notificationsCommand?: NotificationsCommand serversCommand?: ServersCommand + loginCommand?: LoginCommand } function flushAndRunMultipleServers (totalServers: number, configOverride?: Object) { @@ -357,6 +358,7 @@ function assignCommands (server: ServerInfo) { server.sqlCommand = new SQLCommand(server) server.notificationsCommand = new NotificationsCommand(server) server.serversCommand = new ServersCommand(server) + server.loginCommand = new LoginCommand(server) } async function reRunServer (server: ServerInfo, configOverride?: any) { diff --git a/shared/extra-utils/shared/abstract-command.ts b/shared/extra-utils/shared/abstract-command.ts index 4e61554a2..af9ecd926 100644 --- a/shared/extra-utils/shared/abstract-command.ts +++ b/shared/extra-utils/shared/abstract-command.ts @@ -33,6 +33,7 @@ interface InternalGetCommandOptions extends InternalCommonCommandOptions { accept?: string redirects?: number range?: string + host?: string } abstract class AbstractCommand { @@ -78,7 +79,7 @@ abstract class AbstractCommand { } protected getRequest (options: InternalGetCommandOptions) { - const { redirects, query, contentType, accept, range } = options + const { redirects, query, contentType, accept, range, host } = options return makeGetRequest({ ...this.buildCommonRequestOptions(options), @@ -87,6 +88,7 @@ abstract class AbstractCommand { query, contentType, range, + host, accept }) } @@ -109,13 +111,15 @@ abstract class AbstractCommand { protected postBodyRequest (options: InternalCommonCommandOptions & { fields?: { [ fieldName: string ]: any } + type?: string }) { - const { fields } = options + const { type, fields } = options return makePostBodyRequest({ ...this.buildCommonRequestOptions(options), - fields + fields, + type }) } diff --git a/shared/extra-utils/users/index.ts b/shared/extra-utils/users/index.ts index ed166c756..b200ae705 100644 --- a/shared/extra-utils/users/index.ts +++ b/shared/extra-utils/users/index.ts @@ -2,6 +2,7 @@ export * from './accounts-command' export * from './accounts' export * from './blocklist-command' export * from './login' +export * from './login-command' export * from './notifications' export * from './notifications-command' export * from './subscriptions-command' diff --git a/shared/extra-utils/users/login-command.ts b/shared/extra-utils/users/login-command.ts new file mode 100644 index 000000000..97efcb766 --- /dev/null +++ b/shared/extra-utils/users/login-command.ts @@ -0,0 +1,134 @@ +import { PeerTubeRequestError } from '@server/helpers/requests' +import { HttpStatusCode } from '@shared/core-utils' +import { PeerTubeProblemDocument } from '@shared/models' +import { unwrapBody } from '../requests' +import { AbstractCommand, OverrideCommandOptions } from '../shared' + +export class LoginCommand extends AbstractCommand { + + login (options: OverrideCommandOptions & { + client?: { id?: string, secret?: string } + user?: { username: string, password: string } + } = {}) { + const { client = this.server.client, user = this.server.user } = options + const path = '/api/v1/users/token' + + const body = { + client_id: client.id, + client_secret: client.secret, + username: user.username, + password: user.password, + response_type: 'code', + grant_type: 'password', + scope: 'upload' + } + + return unwrapBody<{ access_token: string, refresh_token: string } & PeerTubeProblemDocument>(this.postBodyRequest({ + ...options, + + path, + type: 'form', + fields: body, + implicitToken: false, + defaultExpectedStatus: HttpStatusCode.OK_200 + })) + } + + getAccessToken (user?: { username: string, password: string }): Promise + getAccessToken (username: string, password: string): Promise + async getAccessToken (arg1?: { username: string, password: string } | string, password?: string) { + let user: { username: string, password: string } + + if (!arg1) user = this.server.user + else if (typeof arg1 === 'object') user = arg1 + else user = { username: arg1, password } + + try { + const body = await this.login({ user }) + + return body.access_token + } catch (err) { + throw new Error('Cannot authenticate. Please check your username/password.') + } + } + + loginUsingExternalToken (options: OverrideCommandOptions & { + username: string + externalAuthToken: string + }) { + const { username, externalAuthToken } = options + const path = '/api/v1/users/token' + + const body = { + client_id: this.server.client.id, + client_secret: this.server.client.secret, + username: username, + response_type: 'code', + grant_type: 'password', + scope: 'upload', + externalAuthToken + } + + return this.postBodyRequest({ + ...options, + + path, + type: 'form', + fields: body, + implicitToken: false, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } + + logout (options: OverrideCommandOptions & { + token: string + }) { + const path = '/api/v1/users/revoke-token' + + return unwrapBody<{ redirectUrl: string }>(this.postBodyRequest({ + ...options, + + path, + type: 'form', + implicitToken: false, + defaultExpectedStatus: HttpStatusCode.OK_200 + })) + } + + refreshToken (options: OverrideCommandOptions & { + refreshToken: string + }) { + const path = '/api/v1/users/token' + + const body = { + client_id: this.server.client.id, + client_secret: this.server.client.secret, + refresh_token: options.refreshToken, + response_type: 'code', + grant_type: 'refresh_token' + } + + return this.postBodyRequest({ + ...options, + + path, + type: 'form', + fields: body, + implicitToken: false, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } + + getClient (options: OverrideCommandOptions = {}) { + const path = '/api/v1/oauth-clients/local' + + return this.getRequestBody<{ client_id: string, client_secret: string }>({ + ...options, + + path, + host: this.server.host, + implicitToken: false, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } +} diff --git a/shared/extra-utils/users/login.ts b/shared/extra-utils/users/login.ts index c14367542..d4ee8e517 100644 --- a/shared/extra-utils/users/login.ts +++ b/shared/extra-utils/users/login.ts @@ -1,133 +1,19 @@ -import * as request from 'supertest' - import { ServerInfo } from '../server/servers' -import { getClient } from '../server/clients' -import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' - -type Client = { id?: string, secret?: string } -type User = { username: string, password: string } -type Server = { url?: string, client?: Client, user?: User } - -function login (url: string, client: Client, user: User, expectedStatus = HttpStatusCode.OK_200) { - const path = '/api/v1/users/token' - - const body = { - client_id: client.id, - client_secret: client.secret, - username: user.username, - password: user.password, - response_type: 'code', - grant_type: 'password', - scope: 'upload' - } - - return request(url) - .post(path) - .type('form') - .send(body) - .expect(expectedStatus) -} - -function logout (url: string, token: string, expectedStatus = HttpStatusCode.OK_200) { - const path = '/api/v1/users/revoke-token' - - return request(url) - .post(path) - .set('Authorization', 'Bearer ' + token) - .type('form') - .expect(expectedStatus) -} - -async function serverLogin (server: Server) { - const res = await login(server.url, server.client, server.user, HttpStatusCode.OK_200) - - return res.body.access_token as string -} - -function refreshToken (server: ServerInfo, refreshToken: string, expectedStatus = HttpStatusCode.OK_200) { - const path = '/api/v1/users/token' - - const body = { - client_id: server.client.id, - client_secret: server.client.secret, - refresh_token: refreshToken, - response_type: 'code', - grant_type: 'refresh_token' - } - - return request(server.url) - .post(path) - .type('form') - .send(body) - .expect(expectedStatus) -} - -async function userLogin (server: Server, user: User, expectedStatus = HttpStatusCode.OK_200) { - const res = await login(server.url, server.client, user, expectedStatus) - - return res.body.access_token as string -} - -async function getAccessToken (url: string, username: string, password: string) { - const resClient = await getClient(url) - const client = { - id: resClient.body.client_id, - secret: resClient.body.client_secret - } - - const user = { username, password } - - try { - const res = await login(url, client, user) - return res.body.access_token - } catch (err) { - throw new Error('Cannot authenticate. Please check your username/password.') - } -} function setAccessTokensToServers (servers: ServerInfo[]) { const tasks: Promise[] = [] for (const server of servers) { - const p = serverLogin(server).then(t => { server.accessToken = t }) + const p = server.loginCommand.getAccessToken() + .then(t => { server.accessToken = t }) tasks.push(p) } return Promise.all(tasks) } -function loginUsingExternalToken (server: Server, username: string, externalAuthToken: string, expectedStatus = HttpStatusCode.OK_200) { - const path = '/api/v1/users/token' - - const body = { - client_id: server.client.id, - client_secret: server.client.secret, - username: username, - response_type: 'code', - grant_type: 'password', - scope: 'upload', - externalAuthToken - } - - return request(server.url) - .post(path) - .type('form') - .send(body) - .expect(expectedStatus) -} - // --------------------------------------------------------------------------- export { - login, - logout, - serverLogin, - refreshToken, - userLogin, - getAccessToken, - setAccessTokensToServers, - Server, - Client, - User, - loginUsingExternalToken + setAccessTokensToServers } diff --git a/shared/extra-utils/users/notifications.ts b/shared/extra-utils/users/notifications.ts index 81f0729fa..79cb6f617 100644 --- a/shared/extra-utils/users/notifications.ts +++ b/shared/extra-utils/users/notifications.ts @@ -7,7 +7,7 @@ import { UserNotification, UserNotificationSetting, UserNotificationSettingValue import { MockSmtpServer } from '../mock-servers/mock-email' import { doubleFollow } from '../server/follows' import { flushAndRunMultipleServers, ServerInfo } from '../server/servers' -import { setAccessTokensToServers, userLogin } from './login' +import { setAccessTokensToServers } from './login' import { createUser, getMyUserInformation } from './users' function getAllNotificationsSettings (): UserNotificationSetting { @@ -662,7 +662,7 @@ async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: an password: user.password, videoQuota: 10 * 1000 * 1000 }) - const userAccessToken = await userLogin(servers[0], user) + const userAccessToken = await servers[0].loginCommand.getAccessToken(user) await servers[0].notificationsCommand.updateMySettings({ token: userAccessToken, settings: getAllNotificationsSettings() }) await servers[0].notificationsCommand.updateMySettings({ settings: getAllNotificationsSettings() }) diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts index 0f15962ad..835ad08ba 100644 --- a/shared/extra-utils/users/users.ts +++ b/shared/extra-utils/users/users.ts @@ -7,7 +7,6 @@ import { UserRegister } from '../../models/users/user-register.model' import { UserRole } from '../../models/users/user-role' import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateImageRequest } from '../requests/requests' import { ServerInfo } from '../server/servers' -import { userLogin } from './login' function createUser (parameters: { url: string @@ -55,7 +54,7 @@ async function generateUser (server: ServerInfo, username: string) { const password = 'my super password' const resCreate = await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) - const token = await userLogin(server, { username, password }) + const token = await server.loginCommand.getAccessToken({ username, password }) const resMe = await getMyUserInformation(server.url, token) @@ -70,7 +69,7 @@ async function generateUserAccessToken (server: ServerInfo, username: string) { const password = 'my super password' await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) - return userLogin(server, { username, password }) + return server.loginCommand.getAccessToken({ username, password }) } function registerUser (url: string, username: string, password: string, specialStatus = HttpStatusCode.NO_CONTENT_204) { -- cgit v1.2.3