From b1ef8220ee32931bcfa5a78b80b66057a89ce2c8 Mon Sep 17 00:00:00 2001 From: Zach H Date: Wed, 20 Oct 2021 22:07:35 -0400 Subject: [PATCH] Support Registration on Webatrice with a baseline of handling. (#4436) * Support Registration on Webatrice with a baseline of handling. Still needs to support activation tokens & unit testing. * Add support for account activation with token * Activate Account refactor * Fix typo * Add Unit Testing for Commands/Events * Changes based on review feedback --- webclient/src/api/AuthenticationService.tsx | 18 +- webclient/src/containers/Account/Account.tsx | 2 +- webclient/src/containers/Server/Server.tsx | 15 +- .../src/forms/RegisterForm/RegisterForm.tsx | 4 +- .../src/store/server/server.interfaces.ts | 16 ++ webclient/src/types/server.tsx | 30 ++- webclient/src/websocket/WebClient.ts | 15 +- .../commands/SessionCommands.spec.ts | 198 +++++++++++++++++- .../src/websocket/commands/SessionCommands.ts | 130 +++++++++++- .../websocket/events/SessionEvents.spec.ts | 57 +++-- .../src/websocket/events/SessionEvents.ts | 32 ++- .../persistence/SessionPersistence.ts | 8 + .../websocket/services/WebSocketService.ts | 9 + .../src/websocket/utils/NormalizeService.ts | 17 ++ 14 files changed, 483 insertions(+), 68 deletions(-) diff --git a/webclient/src/api/AuthenticationService.tsx b/webclient/src/api/AuthenticationService.tsx index 153c853f..f53c3546 100644 --- a/webclient/src/api/AuthenticationService.tsx +++ b/webclient/src/api/AuthenticationService.tsx @@ -1,10 +1,18 @@ -import { ServerConnectParams } from "store"; -import { StatusEnum, User} from "types"; -import { webClient, SessionCommands } from "websocket"; +import {StatusEnum, User} from "types"; +import {SessionCommands, webClient} from "websocket"; +import {WebSocketConnectReason, WebSocketOptions} from "../websocket/services/WebSocketService"; export default class AuthenticationService { - static connect(options: ServerConnectParams): void { - SessionCommands.connect(options); + static connect(options: WebSocketOptions): void { + SessionCommands.connect(options, WebSocketConnectReason.LOGIN); + } + + static register(options: WebSocketOptions): void { + SessionCommands.connect(options, WebSocketConnectReason.REGISTER); + } + + static activateAccount(options: WebSocketOptions): void { + SessionCommands.connect(options, WebSocketConnectReason.ACTIVATE_ACCOUNT); } static disconnect(): void { diff --git a/webclient/src/containers/Account/Account.tsx b/webclient/src/containers/Account/Account.tsx index 8678b305..3b75c4c0 100644 --- a/webclient/src/containers/Account/Account.tsx +++ b/webclient/src/containers/Account/Account.tsx @@ -76,7 +76,7 @@ class Account extends Component { {name}

{name}

-

Location: ({country.toUpperCase()})

+

Location: ({country?.toUpperCase()})

User Level: {userLevel}

Account Age: {accountageSecs}

Real Name: {realName}

diff --git a/webclient/src/containers/Server/Server.tsx b/webclient/src/containers/Server/Server.tsx index 4d086b0b..5f3edf4a 100644 --- a/webclient/src/containers/Server/Server.tsx +++ b/webclient/src/containers/Server/Server.tsx @@ -25,7 +25,6 @@ class Server extends Component { this.showDescription = this.showDescription.bind(this); this.showRegisterForm = this.showRegisterForm.bind(this); this.hideRegisterForm = this.hideRegisterForm.bind(this); - this.onRegister = this.onRegister.bind(this); this.state = { register: false @@ -47,10 +46,6 @@ class Server extends Component { this.setState({register: false}); } - onRegister(fields) { - console.log("register", fields); - } - render() { const { message, rooms, joinedRooms, history, state, description, users } = this.props; const { register } = this.state; @@ -66,7 +61,7 @@ class Server extends Component { { register - ? ( ) + ? ( ) : ( ) } @@ -106,7 +101,7 @@ const ServerRooms = ({ rooms, joinedRooms, history, message, users}) => ( Users connected to server: {users.length} users[index].name } + itemKey={(index) => users[index].name } items={ users.map(user => ( @@ -122,13 +117,13 @@ const ServerRooms = ({ rooms, joinedRooms, history, message, users}) => ( const Connect = ({register}) => (
- {/*{}*/} +
); -const Register = ({ onRegister, connect }) => ( +const Register = ({ connect }) => (
- onRegister(event)} /> +
); diff --git a/webclient/src/forms/RegisterForm/RegisterForm.tsx b/webclient/src/forms/RegisterForm/RegisterForm.tsx index bc8f28fa..9eb2d781 100644 --- a/webclient/src/forms/RegisterForm/RegisterForm.tsx +++ b/webclient/src/forms/RegisterForm/RegisterForm.tsx @@ -19,10 +19,10 @@ const RegisterForm = ({ handleSubmit }) => (
- +
- +
diff --git a/webclient/src/store/server/server.interfaces.ts b/webclient/src/store/server/server.interfaces.ts index 42f0b3c2..c86952b2 100644 --- a/webclient/src/store/server/server.interfaces.ts +++ b/webclient/src/store/server/server.interfaces.ts @@ -7,6 +7,22 @@ export interface ServerConnectParams { pass: string; } +export interface ServerRegisterParams { + host: string; + port: string; + user: string; + pass: string; + passAgain: string; + email: string; + country: string; + realName: string; +} + +export interface AccountActivationParams extends ServerRegisterParams { + activationCode: string; + clientid: string; +} + export interface ServerState { buddyList: User[]; ignoreList: User[]; diff --git a/webclient/src/types/server.tsx b/webclient/src/types/server.tsx index fc6e32a2..23a70d25 100644 --- a/webclient/src/types/server.tsx +++ b/webclient/src/types/server.tsx @@ -4,20 +4,30 @@ export interface ServerStatus { } export enum StatusEnum { - DISCONNECTED = 0, - CONNECTING = 1, - CONNECTED = 2, - LOGGINGIN = 3, - LOGGEDIN = 4, + DISCONNECTED, + CONNECTING, + CONNECTED, + LOGGINGIN, + LOGGEDIN, + REGISTERING, + REGISTERED, + ACTIVATING_ACCOUNT, + ACCOUNT_ACTIVATED, + RECOVERING_PASSWORD, DISCONNECTING = 99 } export enum StatusEnumLabel { - "Disconnected" = 0, - "Connecting" = 1, - "Connected" = 2, - "Loggingin" = 3, - "Loggedin" = 4, + "Disconnected", + "Connecting" , + "Connected" , + "Loggingin", + "Loggedin", + "Registering", + "Registered", + "ActivatingAccount", + "AccountActivated", + "RecoveringPassword", "Disconnecting" = 99 } diff --git a/webclient/src/websocket/WebClient.ts b/webclient/src/websocket/WebClient.ts index 2878df21..bab60d77 100644 --- a/webclient/src/websocket/WebClient.ts +++ b/webclient/src/websocket/WebClient.ts @@ -1,10 +1,9 @@ -import { ServerConnectParams } from "store"; -import { ServerStatus, StatusEnum } from "types"; +import {ServerStatus, StatusEnum} from "types"; -import { ProtobufService } from './services/ProtobufService'; -import { WebSocketService, WebSocketOptions } from "./services/WebSocketService"; +import {ProtobufService} from './services/ProtobufService'; +import {WebSocketOptions, WebSocketService} from "./services/WebSocketService"; -import { RoomPersistence, SessionPersistence } from './persistence'; +import {RoomPersistence, SessionPersistence} from './persistence'; export class WebClient { public socket = new WebSocketService(this); @@ -36,6 +35,8 @@ export class WebClient { port: "", user: "", pass: "", + clientid: null, + reason: null, autojoinrooms: true, keepalive: 5000 }; @@ -52,8 +53,8 @@ export class WebClient { console.log(this); } - public connect(options: ServerConnectParams) { - this.options = { ...this.options, ...options }; + public connect(options: WebSocketOptions) { + this.options = {...this.options, ...options}; this.socket.connect(this.options); } diff --git a/webclient/src/websocket/commands/SessionCommands.spec.ts b/webclient/src/websocket/commands/SessionCommands.spec.ts index 3a73d639..39755b2d 100644 --- a/webclient/src/websocket/commands/SessionCommands.spec.ts +++ b/webclient/src/websocket/commands/SessionCommands.spec.ts @@ -1,9 +1,11 @@ -import { StatusEnum } from 'types'; +import {StatusEnum} from 'types'; -import { SessionCommands } from './SessionCommands'; +import {SessionCommands} from './SessionCommands'; -import { RoomPersistence, SessionPersistence } from '../persistence'; +import {RoomPersistence, SessionPersistence} from '../persistence'; import webClient from '../WebClient'; +import {WebSocketConnectReason} from "../services/WebSocketService"; +import {AccountActivationParams, ServerRegisterParams} from "../../store"; describe('SessionCommands', () => { const roomId = 1; @@ -21,23 +23,49 @@ describe('SessionCommands', () => { webClient.protobuf.controller.SessionCommand = { create: args => args }; }); + describe('connect', () => { - it('should call SessionCommands.updateStatus and webClient.connect', () => { + let options; + + beforeEach(() => { spyOn(webClient, 'connect'); - const options = { + options = { host: 'host', port: 'port', user: 'user', pass: 'pass', }; + }); - SessionCommands.connect(options); + it('should call SessionCommands.updateStatus and webClient.connect when logging in', () => { + SessionCommands.connect(options, WebSocketConnectReason.LOGIN); expect(SessionCommands.updateStatus).toHaveBeenCalled(); - expect(SessionCommands.updateStatus).toHaveBeenCalledWith(StatusEnum.CONNECTING, 'Connecting...'); + expect(SessionCommands.updateStatus).toHaveBeenCalledWith(StatusEnum.CONNECTING, expect.any(String)); expect(webClient.connect).toHaveBeenCalled(); - expect(webClient.connect).toHaveBeenCalledWith(options); + expect(webClient.connect).toHaveBeenCalledWith({ ...options, reason: WebSocketConnectReason.LOGIN }); + }); + + it('should call SessionCommands.updateStatus and webClient.connect when registering', () => { + SessionCommands.connect(options, WebSocketConnectReason.REGISTER); + + expect(SessionCommands.updateStatus).toHaveBeenCalled(); + expect(SessionCommands.updateStatus).toHaveBeenCalledWith(StatusEnum.REGISTERING, expect.any(String)); + + expect(webClient.connect).toHaveBeenCalled(); + expect(webClient.connect).toHaveBeenCalledWith({ ...options, reason: WebSocketConnectReason.REGISTER }); + }); + + + it('should call SessionCommands.updateStatus and webClient.connect when activating account', () => { + SessionCommands.connect(options, WebSocketConnectReason.ACTIVATE_ACCOUNT); + + expect(SessionCommands.updateStatus).toHaveBeenCalled(); + expect(SessionCommands.updateStatus).toHaveBeenCalledWith(StatusEnum.ACTIVATING_ACCOUNT, expect.any(String)); + + expect(webClient.connect).toHaveBeenCalled(); + expect(webClient.connect).toHaveBeenCalledWith({ ...options, reason: WebSocketConnectReason.ACTIVATE_ACCOUNT }); }); }); @@ -215,6 +243,160 @@ describe('SessionCommands', () => { }); }); + describe('register', () => { + beforeEach(() => { + webClient.protobuf.controller.Command_Register = { create: args => args }; + webClient.options = { + ...webClient.options, + user: 'user', + pass: 'pass', + email: 'email@example.com', + country: 'us', + realName: 'realName', + clientid: 'abcdefg' + } as any; + }); + + it('should call protobuf controller methods and sendCommand', () => { + SessionCommands.register(); + + const options = webClient.options as unknown as ServerRegisterParams; + + expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalled(); + expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith({ + '.Command_Register.ext': { + ...webClient.clientConfig, + userName: options.user, + password: options.pass, + email: options.email, + country: options.country, + realName: options.realName, + clientid: jasmine.any(String) + } + }, jasmine.any(Function)); + }); + + describe('response', () => { + const RespRegistrationAccepted = 'RespRegistrationAccepted'; + const respKey = '.Response_Register.ext'; + let response; + + beforeEach(() => { + response = { + responseCode: RespRegistrationAccepted, + [respKey]: { + reasonStr: "", + endTime: 10000000 + } + }; + + webClient.protobuf.controller.Response = { ResponseCode: { RespRegistrationAccepted }}; + + sendSessionCommandSpy.and.callFake((_, callback) => callback(response)); + }) + + it("should login user if registration accepted without email verification", () => { + spyOn(SessionCommands, 'login'); + spyOn(SessionPersistence, 'accountAwaitingActivation'); + + SessionCommands.register(); + + expect(SessionCommands.login).toHaveBeenCalled(); + expect(SessionPersistence.accountAwaitingActivation).not.toHaveBeenCalled(); + }); + + it("should prompt user if registration accepted with email verification", () => { + const RespRegistrationAcceptedNeedsActivation = 'RespRegistrationAcceptedNeedsActivation'; + response.responseCode = RespRegistrationAcceptedNeedsActivation; + webClient.protobuf.controller.Response.ResponseCode.RespRegistrationAcceptedNeedsActivation = RespRegistrationAcceptedNeedsActivation; + + spyOn(SessionCommands, 'login'); + spyOn(SessionPersistence, 'accountAwaitingActivation'); + + SessionCommands.register(); + + expect(SessionCommands.login).not.toHaveBeenCalled(); + expect(SessionPersistence.accountAwaitingActivation).toHaveBeenCalled(); + }); + + it("should disconnect user if registration fails due to registration being disabled", () => { + const RespRegistrationDisabled = 'RespRegistrationDisabled'; + response.responseCode = RespRegistrationDisabled; + webClient.protobuf.controller.Response.ResponseCode.RespRegistrationDisabled = RespRegistrationDisabled; + + SessionCommands.register(); + + expect(SessionCommands.updateStatus).toHaveBeenCalledWith(StatusEnum.DISCONNECTED, expect.any(String)); + }); + }); + }); + + describe('activateAccount', () => { + beforeEach(() => { + webClient.protobuf.controller.Command_Activate = { create: args => args }; + webClient.options = { + ...webClient.options, + user: 'user', + activationCode: 'token', + clientid: 'abcdefg' + } as any; + }); + + it('should call protobuf controller methods and sendCommand', () => { + SessionCommands.activateAccount(); + + const options = webClient.options as unknown as AccountActivationParams; + + expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith({ + '.Command_Activate.ext': { + ...webClient.clientConfig, + userName: options.user, + token: options.activationCode, + clientid: jasmine.any(String) + } + }, jasmine.any(Function)); + }); + + describe('response', () => { + const RespActivationAccepted = 'RespActivationAccepted'; + const respKey = '.Response_Activate.ext'; + let response; + + beforeEach(() => { + response = { + responseCode: RespActivationAccepted, + [respKey]: { + + } + }; + + webClient.protobuf.controller.Response = { ResponseCode: { RespActivationAccepted }}; + + sendSessionCommandSpy.and.callFake((_, callback) => callback(response)); + spyOn(SessionCommands, 'login'); + spyOn(SessionPersistence, 'accountActivationFailed'); + }); + + it('should activate user and login if correct activation token used', () => { + SessionCommands.activateAccount(); + + expect(SessionCommands.login).toHaveBeenCalled(); + expect(SessionPersistence.accountActivationFailed).not.toHaveBeenCalled(); + }); + + it('should disconnect user if activation failed for any reason', () => { + const RespActivationFailed = 'RespActivationFailed'; + response.responseCode = RespActivationFailed; + webClient.protobuf.controller.Response.ResponseCode.RespActivationFailed = RespActivationFailed; + + SessionCommands.activateAccount(); + + expect(SessionCommands.login).not.toHaveBeenCalled(); + expect(SessionPersistence.accountActivationFailed).toHaveBeenCalled(); + }); + }); + }); + describe('listUsers', () => { beforeEach(() => { webClient.protobuf.controller.Command_ListUsers = { create: () => ({}) }; diff --git a/webclient/src/websocket/commands/SessionCommands.ts b/webclient/src/websocket/commands/SessionCommands.ts index 3cd987d2..3b61b30c 100644 --- a/webclient/src/websocket/commands/SessionCommands.ts +++ b/webclient/src/websocket/commands/SessionCommands.ts @@ -1,14 +1,33 @@ -import { ServerConnectParams } from 'store'; -import { StatusEnum } from 'types'; +import {StatusEnum} from 'types'; -import { RoomPersistence, SessionPersistence } from '../persistence'; +import {RoomPersistence, SessionPersistence} from '../persistence'; import webClient from '../WebClient'; -import { guid } from '../utils'; +import {guid} from '../utils'; +import {WebSocketConnectReason, WebSocketOptions} from "../services/WebSocketService"; +import {ServerRegisterParams, AccountActivationParams} from "../../store"; +import NormalizeService from "../utils/NormalizeService"; export class SessionCommands { - static connect(options: ServerConnectParams): void { - SessionCommands.updateStatus(StatusEnum.CONNECTING, 'Connecting...'); - webClient.connect(options); + static connect(options: WebSocketOptions, reason: WebSocketConnectReason): void { + switch (reason) { + case WebSocketConnectReason.LOGIN: + SessionCommands.updateStatus(StatusEnum.CONNECTING, 'Connecting...'); + break; + case WebSocketConnectReason.REGISTER: + SessionCommands.updateStatus(StatusEnum.REGISTERING, 'Registering...'); + break; + case WebSocketConnectReason.ACTIVATE_ACCOUNT: + SessionCommands.updateStatus(StatusEnum.ACTIVATING_ACCOUNT, 'Activating Account...'); + break; + case WebSocketConnectReason.RECOVER_PASSWORD: + SessionCommands.updateStatus(StatusEnum.RECOVERING_PASSWORD, 'Recovering Password...'); + break; + default: + console.error('Connection Failed', reason); + break; + } + + webClient.connect({ ...options, reason }); } static disconnect(): void { @@ -78,6 +97,7 @@ export class SessionCommands { case webClient.protobuf.controller.Response.ResponseCode.RespAccountNotActivated: SessionCommands.updateStatus(StatusEnum.DISCONNECTED, 'Login failed: account not activated'); + SessionPersistence.accountAwaitingActivation(); break; default: @@ -86,6 +106,102 @@ export class SessionCommands { }); } + static register(): void { + const options = webClient.options as unknown as ServerRegisterParams; + + const registerConfig = { + ...webClient.clientConfig, + userName: options.user, + password: options.pass, + email: options.email, + country: options.country, + realName: options.realName, + clientid: 'webatrice' + }; + + const CmdRegister = webClient.protobuf.controller.Command_Register.create(registerConfig); + + const sc = webClient.protobuf.controller.SessionCommand.create({ + '.Command_Register.ext' : CmdRegister + }); + + webClient.protobuf.sendSessionCommand(sc, raw => { + let error; + + switch (raw.responseCode) { + case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationAccepted: + SessionCommands.login(); + break; + case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationAcceptedNeedsActivation: + SessionCommands.updateStatus(StatusEnum.REGISTERED, "Registration Successful"); + SessionPersistence.accountAwaitingActivation(); + break; + case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationDisabled: + error = 'Registration is currently disabled'; + break; + case webClient.protobuf.controller.Response.ResponseCode.RespUserAlreadyExists: + error = 'There is already an existing user with this username'; + break; + case webClient.protobuf.controller.Response.ResponseCode.RespEmailRequiredToRegister: + error = 'A valid email address is required to register'; + break; + case webClient.protobuf.controller.Response.ResponseCode.RespEmailBlackListed: + error = 'The email address provider used has been blocked from use'; + break; + case webClient.protobuf.controller.Response.ResponseCode.RespTooManyRequests: + error = 'This email address already has the maximum number of accounts you can register'; + break; + case webClient.protobuf.controller.Response.ResponseCode.RespPasswordTooShort: + error = 'Your password was too short'; + break; + case webClient.protobuf.controller.Response.ResponseCode.RespUserIsBanned: + error = NormalizeService.normalizeBannedUserError(raw.reasonStr, raw.endTime); + break; + case webClient.protobuf.controller.Response.ResponseCode.RespUsernameInvalid: + console.error("ResponseCode.RespUsernameInvalid", raw.reasonStr); + error = 'Invalid username'; + break; + case webClient.protobuf.controller.Response.ResponseCode.RespRegistrationFailed: + default: + console.error("ResponseCode Type", raw.responseCode); + error = 'Registration failed due to a server issue'; + break; + } + + if (error) { + SessionCommands.updateStatus(StatusEnum.DISCONNECTED, `Registration Failed: ${error}`); + } + }); + }; + + static activateAccount(): void { + const options = webClient.options as unknown as AccountActivationParams; + + const accountActivationConfig = { + ...webClient.clientConfig, + userName: options.user, + clientid: options.clientid, + token: options.activationCode + }; + + const CmdActivate = webClient.protobuf.controller.Command_Activate.create(accountActivationConfig); + + const sc = webClient.protobuf.controller.SessionCommand.create({ + '.Command_Activate.ext': CmdActivate + }); + + webClient.protobuf.sendSessionCommand(sc, raw => { + if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespActivationAccepted) { + SessionCommands.updateStatus(StatusEnum.ACCOUNT_ACTIVATED, 'Account Activation Successful'); + SessionCommands.login(); + } else { + SessionCommands.updateStatus(StatusEnum.DISCONNECTED, 'Account Activation Failed'); + SessionPersistence.accountActivationFailed(); + } + }); + + } + static listUsers(): void { const CmdListUsers = webClient.protobuf.controller.Command_ListUsers.create(); diff --git a/webclient/src/websocket/events/SessionEvents.spec.ts b/webclient/src/websocket/events/SessionEvents.spec.ts index f01547b1..fa3f1a83 100644 --- a/webclient/src/websocket/events/SessionEvents.spec.ts +++ b/webclient/src/websocket/events/SessionEvents.spec.ts @@ -1,21 +1,21 @@ -import { StatusEnum } from "types"; +import {StatusEnum} from "types"; import { - SessionEvents, - SessionEvent, AddToListData, ConnectionClosedData, ListRoomsData, RemoveFromListData, ServerIdentificationData, ServerMessageData, + SessionEvents, UserJoinedData, UserLeftData, } from './SessionEvents'; -import { SessionCommands } from "../commands"; -import { RoomPersistence, SessionPersistence } from '../persistence'; +import {SessionCommands} from "../commands"; +import {RoomPersistence, SessionPersistence} from '../persistence'; import webClient from '../WebClient'; +import {WebSocketConnectReason} from "../services/WebSocketService"; describe('SessionEvents', () => { const roomId = 1; @@ -277,24 +277,57 @@ describe('SessionEvents', () => { }); describe('.Event_ServerIdentification.ext', () => { - it('update status/info and login', () => { - spyOn(SessionPersistence, 'updateInfo'); - spyOn(SessionCommands, 'login'); + let data: ServerIdentificationData; + let event; + beforeEach(() => { webClient.protocolVersion = 0; - const data: ServerIdentificationData = { + event = SessionEvents['.Event_ServerIdentification.ext']; + data = { serverName: 'serverName', serverVersion: 'serverVersion', protocolVersion: 0, }; - SessionEvents['.Event_ServerIdentification.ext'](data); + spyOn(SessionPersistence, 'updateInfo'); + }); + + it('update status/info and login', () => { + spyOn(SessionCommands, 'login'); + + webClient.options.reason = WebSocketConnectReason.LOGIN; + + event(data); expect(SessionPersistence.updateInfo).toHaveBeenCalledWith(data.serverName, data.serverVersion); - expect(SessionCommands.updateStatus).toHaveBeenCalledWith(StatusEnum.LOGGINGIN, 'Logging in...'); + expect(SessionCommands.updateStatus).toHaveBeenCalledWith(StatusEnum.LOGGINGIN, expect.any(String)); expect(SessionCommands.login).toHaveBeenCalled(); }); + it('should update stat/info and register', () => { + spyOn(SessionCommands, 'register'); + + webClient.options.reason = WebSocketConnectReason.REGISTER; + + event(data); + + expect(SessionPersistence.updateInfo).toHaveBeenCalledWith(data.serverName, data.serverVersion); + expect(SessionCommands.updateStatus).toHaveBeenCalledWith(StatusEnum.REGISTERING, expect.any(String)); + expect(SessionCommands.register).toHaveBeenCalled(); + }); + + it('should update stat/info and activate account', () => { + spyOn(SessionCommands, 'activateAccount'); + + webClient.options.reason = WebSocketConnectReason.ACTIVATE_ACCOUNT; + + event(data); + + expect(SessionPersistence.updateInfo).toHaveBeenCalledWith(data.serverName, data.serverVersion); + expect(SessionCommands.updateStatus).toHaveBeenCalledWith(StatusEnum.ACTIVATING_ACCOUNT, expect.any(String)); + expect(SessionCommands.activateAccount).toHaveBeenCalled(); + }); + it('should disconnect if protocolVersion mismatched', () => { spyOn(SessionCommands, 'login'); spyOn(SessionCommands, 'disconnect'); @@ -306,7 +339,7 @@ describe('SessionEvents', () => { protocolVersion: 1, }; - SessionEvents['.Event_ServerIdentification.ext'](data); + event(data); expect(SessionCommands.disconnect).toHaveBeenCalled(); expect(SessionCommands.updateStatus).toHaveBeenCalledWith(StatusEnum.DISCONNECTED, `Protocol version mismatch: ${data.protocolVersion}`); diff --git a/webclient/src/websocket/events/SessionEvents.ts b/webclient/src/websocket/events/SessionEvents.ts index 00fc0ee0..97a24565 100644 --- a/webclient/src/websocket/events/SessionEvents.ts +++ b/webclient/src/websocket/events/SessionEvents.ts @@ -1,9 +1,10 @@ -import { Room, StatusEnum, User } from 'types'; +import {Room, StatusEnum, User} from 'types'; -import { SessionCommands } from '../commands'; -import { RoomPersistence, SessionPersistence } from '../persistence'; -import { ProtobufEvents } from '../services/ProtobufService'; +import {SessionCommands} from '../commands'; +import {RoomPersistence, SessionPersistence} from '../persistence'; +import {ProtobufEvents} from '../services/ProtobufService'; import webClient from '../WebClient'; +import {WebSocketConnectReason} from "../services/WebSocketService"; export const SessionEvents: ProtobufEvents = { '.Event_AddToList.ext': addToList, @@ -120,9 +121,28 @@ function serverIdentification(info: ServerIdentificationData) { return; } + switch (webClient.options.reason) { + case WebSocketConnectReason.LOGIN: + SessionCommands.updateStatus(StatusEnum.LOGGINGIN, 'Logging in...'); + SessionCommands.login(); + break; + case WebSocketConnectReason.REGISTER: + SessionCommands.updateStatus(StatusEnum.REGISTERING, 'Registering...'); + SessionCommands.register(); + break; + case WebSocketConnectReason.ACTIVATE_ACCOUNT: + SessionCommands.updateStatus(StatusEnum.ACTIVATING_ACCOUNT, 'Activating account...'); + SessionCommands.activateAccount(); + break; + case WebSocketConnectReason.RECOVER_PASSWORD: + console.log('ServerIdentificationData.recoverPassword'); + break; + default: + console.error("Undefined type", webClient.options.reason); + break; + } + SessionPersistence.updateInfo(serverName, serverVersion); - SessionCommands.updateStatus(StatusEnum.LOGGINGIN, 'Logging in...'); - SessionCommands.login(); } function serverMessage({ message }: ServerMessageData) { diff --git a/webclient/src/websocket/persistence/SessionPersistence.ts b/webclient/src/websocket/persistence/SessionPersistence.ts index b70258f8..3b9ea3de 100644 --- a/webclient/src/websocket/persistence/SessionPersistence.ts +++ b/webclient/src/websocket/persistence/SessionPersistence.ts @@ -72,4 +72,12 @@ export class SessionPersistence { static serverMessage(message: string) { ServerDispatch.serverMessage(sanitizeHtml(message)); } + + static accountAwaitingActivation() { + console.log("Open Modal for Activation Code input"); + } + + static accountActivationFailed() { + console.log("Account activation failed, show an action here"); + } } diff --git a/webclient/src/websocket/services/WebSocketService.ts b/webclient/src/websocket/services/WebSocketService.ts index 0818d679..b4c1e2de 100644 --- a/webclient/src/websocket/services/WebSocketService.ts +++ b/webclient/src/websocket/services/WebSocketService.ts @@ -12,6 +12,15 @@ export interface WebSocketOptions { pass: string; autojoinrooms: boolean; keepalive: number; + clientid: string; + reason: WebSocketConnectReason; +} + +export enum WebSocketConnectReason { + LOGIN, + REGISTER, + ACTIVATE_ACCOUNT, + RECOVER_PASSWORD, } export class WebSocketService { diff --git a/webclient/src/websocket/utils/NormalizeService.ts b/webclient/src/websocket/utils/NormalizeService.ts index 9cba74b8..f0009436 100644 --- a/webclient/src/websocket/utils/NormalizeService.ts +++ b/webclient/src/websocket/utils/NormalizeService.ts @@ -43,4 +43,21 @@ export default class NormalizeService { message.message = `${name}: ${message.message}`; } } + + // Banned reason string is not being exposed by the server + static normalizeBannedUserError(reasonStr: string, endTime: number): string { + let error; + + if (endTime) { + error = 'You are banned until ' + new Date(endTime).toString(); + } else { + error = 'You are permanently banned'; + } + + if (reasonStr) { + error += '\n\n' + reasonStr; + } + + return error; + } }