Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
de2ad81f8e | |||
e4041f9e41 | |||
00a01bc81b | |||
2b3d6009f1 | |||
21fa7b5e64 | |||
5f01174764 | |||
56fd5e8c89 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -29,7 +29,6 @@ build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
.cache/
|
||||
|
||||
node_modules
|
||||
bundle.js
|
||||
|
@ -1,51 +0,0 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**
|
||||
!**/src/test/**
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
.cache/
|
||||
|
||||
node_modules
|
||||
bundle.js
|
||||
bundle.min.js
|
||||
adminBundle.js
|
||||
adminBundle.min.js
|
||||
worker.js
|
||||
chatWorker.js
|
||||
chatWorker.min.js
|
||||
node
|
||||
src/main/javascript/node/
|
||||
dist
|
||||
out
|
||||
yarn-error.log
|
||||
gen-password.txt
|
||||
|
||||
**/static/**
|
||||
|
||||
License.md
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@stomp/stompjs": "^5.4.4",
|
||||
"@types/bootbox": "^5.2.0",
|
||||
"@types/datatables.net": "^1.10.19",
|
||||
"@types/datatables.net-buttons": "^1.4.3",
|
||||
@ -8,6 +9,7 @@
|
||||
"@types/jquery": "^3.3.31",
|
||||
"@types/markdown-it": "^0.0.9",
|
||||
"@types/sjcl": "^1.0.29",
|
||||
"@types/sockjs-client": "^1.1.1",
|
||||
"@types/sprintf-js": "^1.1.2",
|
||||
"alertifyjs": "^1.12.0",
|
||||
"bootbox": "^5.4.0",
|
||||
@ -24,6 +26,7 @@
|
||||
"moment": "^2.24.0",
|
||||
"promise-worker": "^2.0.1",
|
||||
"sjcl": "^1.0.8",
|
||||
"sockjs-client": "^1.5.0",
|
||||
"sprintf-js": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -34,7 +37,6 @@
|
||||
"grunt-banner": "^0.6.0",
|
||||
"grunt-browserify": "^5.3.0",
|
||||
"grunt-terser": "^1.0.0",
|
||||
"prettier": "2.0.5",
|
||||
"tsify": "^4.0.1",
|
||||
"typescript": "^3.7.4",
|
||||
"uglify-js": "^3.7.5"
|
||||
@ -57,10 +59,11 @@
|
||||
"depends": [
|
||||
"@types/jquery"
|
||||
]
|
||||
}
|
||||
},
|
||||
"sockjs-client": "global:SockJS"
|
||||
},
|
||||
"scripts": {
|
||||
"watch": "watchify src/main/frontend/chat/main.ts -p [ tsify --target ES6 --noImplicitAny ] --debug -o src/main/resources/static/js/bundle.js",
|
||||
"watch:admin": "watchify src/main/frontend/admin/main.ts -p [ tsify --target ES6 --noImplicitAny ] --debug -o src/main/resources/static/js/adminBundle.js"
|
||||
}
|
||||
}
|
||||
}
|
31
pom.xml
31
pom.xml
@ -15,7 +15,7 @@
|
||||
<description>A self hosted minimal E2E chat application</description>
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<gruntArg></gruntArg>
|
||||
<gruntArg />
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -60,11 +60,6 @@
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>com.github.ulisesbocchio</groupId>
|
||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
<version>2.1.2</version>
|
||||
</dependency> -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
@ -108,6 +103,11 @@
|
||||
<artifactId>Either.java</artifactId>
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
@ -137,14 +137,6 @@
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-maven-plugin</artifactId>
|
||||
<version>5.2.4</version>
|
||||
<!-- <executions>
|
||||
<execution>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>migrate</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions> -->
|
||||
<configuration>
|
||||
<driver>${spring.datasource.driverClassName}</driver>
|
||||
<url>jdbc:mysql://${chatto.datasource.url}:${chatto.datasource.port}/${chatto.datasource.database-name}</url>
|
||||
@ -223,17 +215,6 @@
|
||||
</excludeProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code.formatter</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
<version>2.12.0</version>
|
||||
<configuration>
|
||||
<configFile>${project.basedir}/eclipse-formatter.xml</configFile>
|
||||
<compilerSource>11</compilerSource>
|
||||
<compilerCompliance>11</compilerCompliance>
|
||||
<compilerTargetPlatform>11</compilerTargetPlatform>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
56
src/main/frontend/chat/component/ChatComponent.ts
Normal file
56
src/main/frontend/chat/component/ChatComponent.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { UserModel } from "../model/UserModel";
|
||||
import { ChatModel } from "../model/ChatModel";
|
||||
import { UserView } from "../view/UserView";
|
||||
import { ChatView } from "../view/ChatView";
|
||||
import { UserController } from "../controller/UserController";
|
||||
import { ChatController } from "../controller/ChatController";
|
||||
import { Client } from "@stomp/stompjs";
|
||||
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO";
|
||||
import log from "loglevel";
|
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
|
||||
|
||||
export interface Props {
|
||||
// userModel: UserModel;
|
||||
// chatModel: ChatModel;
|
||||
// userView: UserView;
|
||||
// chatView: ChatView;
|
||||
userController: UserController;
|
||||
chatController: ChatController;
|
||||
chatStompClient: Client;
|
||||
}
|
||||
|
||||
export interface State {}
|
||||
|
||||
export class ChatComponent {
|
||||
private readonly _props: Props;
|
||||
private readonly _state: State;
|
||||
|
||||
constructor(props: Props, state: State) {
|
||||
this._props = props;
|
||||
this._state = state;
|
||||
}
|
||||
|
||||
private async wsHandler() {
|
||||
this._props.chatStompClient.onConnect = () => {
|
||||
this._props.chatStompClient.subscribe("/user/queue/reply", (reply) => {
|
||||
const data = JSON.parse(reply.body) as ChatMessageDTO;
|
||||
const plainText = this._props.userController.decryptForUser(
|
||||
data,
|
||||
data.fromUser
|
||||
);
|
||||
this._props.chatController.store(data.fromUser, <ChatMessageViewModel>{
|
||||
...data,
|
||||
message: plainText,
|
||||
messageTime: new Date(data.messageTime),
|
||||
});
|
||||
// log.debug(
|
||||
// message,
|
||||
// this._props.encryptionService.decrypt(
|
||||
// "password",
|
||||
// message.messageCipher
|
||||
// )
|
||||
// );
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
@ -1,41 +1,42 @@
|
||||
import { Controller } from "./AbstractController";
|
||||
import "../model/AbstractModel"
|
||||
import "../model/UserModel"
|
||||
import "../view/AbstractView"
|
||||
import "../view/UserView"
|
||||
import "../model/AbstractModel";
|
||||
import "../model/UserModel";
|
||||
import "../view/AbstractView";
|
||||
import "../view/UserView";
|
||||
import { Model } from "../model/AbstractModel";
|
||||
import { View } from "../view/AbstractView";
|
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
|
||||
import { ChatModel } from "../model/ChatModel";
|
||||
import { ChatView } from "../view/ChatView";
|
||||
import { Client } from "@stomp/stompjs";
|
||||
import { EncryptionService } from "../../common/service/EncryptionService";
|
||||
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO";
|
||||
import log from "loglevel";
|
||||
|
||||
export interface Props {
|
||||
model: ChatModel;
|
||||
view: ChatView;
|
||||
chatStompClient: Client;
|
||||
encryptionService: EncryptionService;
|
||||
}
|
||||
|
||||
export class ChatController {
|
||||
private _model: ChatModel;
|
||||
private _view: ChatView;
|
||||
private readonly _props: Props;
|
||||
constructor(props: Props) {
|
||||
this._props = props;
|
||||
}
|
||||
|
||||
public test(): void {
|
||||
const chatMessageViewModels: ChatMessageViewModel[] = [];
|
||||
let chatMessageViewModelMock = new ChatMessageViewModel();
|
||||
chatMessageViewModelMock.fromUser = "user1";
|
||||
chatMessageViewModelMock.toUser = "user2";
|
||||
chatMessageViewModelMock.message = "";
|
||||
chatMessageViewModelMock.messageTime = new Date();
|
||||
chatMessageViewModels.push(chatMessageViewModelMock);
|
||||
}
|
||||
|
||||
constructor(model: ChatModel, view: ChatView) {
|
||||
this._model = model;
|
||||
this._view = view;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* eventHandler
|
||||
*/
|
||||
public eventHandler(vm: ChatMessageViewModel[]): void {
|
||||
this._model.someBusinessMethod(vm);
|
||||
}
|
||||
|
||||
public test(): void {
|
||||
const chatMessageViewModels: ChatMessageViewModel[] = [];
|
||||
let chatMessageViewModelMock = new ChatMessageViewModel();
|
||||
chatMessageViewModelMock.fromUser = "user1";
|
||||
chatMessageViewModelMock.toUser = "user2";
|
||||
chatMessageViewModelMock.message = "";
|
||||
chatMessageViewModelMock.messageTime = new Date();
|
||||
chatMessageViewModels.push(chatMessageViewModelMock);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public store(userName: string, message: ChatMessageViewModel): void {
|
||||
this._props.model.storeUserMessages(userName, Array(message), "new");
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +1,30 @@
|
||||
import { Controller } from "./AbstractController";
|
||||
import "../model/AbstractModel"
|
||||
import "../model/UserModel"
|
||||
import "../view/AbstractView"
|
||||
import "../view/UserView"
|
||||
import "../model/AbstractModel";
|
||||
import "../model/UserModel";
|
||||
import "../view/AbstractView";
|
||||
import "../view/UserView";
|
||||
import { Model } from "../model/AbstractModel";
|
||||
import { View } from "../view/AbstractView";
|
||||
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
|
||||
import { UserView } from "../view/UserView";
|
||||
import { UserModel } from "../model/UserModel";
|
||||
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO";
|
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
|
||||
|
||||
export class UserController {
|
||||
private _model: UserModel;
|
||||
private _view: UserView;
|
||||
private _model: UserModel;
|
||||
private _view: UserView;
|
||||
|
||||
constructor(model: UserModel, view: UserView) {
|
||||
this._model = model;
|
||||
this._view = view;
|
||||
}
|
||||
|
||||
constructor(model: UserModel, view: UserView) {
|
||||
this._model = model;
|
||||
this._view = view;
|
||||
}
|
||||
// public getActiveUsers(): void {
|
||||
// this._model.getActiveUsers();
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* eventHandler
|
||||
*/
|
||||
public eventHandler(vm: ActiveUserViewModel[]): void {
|
||||
this._model.someBusinessMethod(vm);
|
||||
}
|
||||
|
||||
public test(): void {
|
||||
const activeUsersMock: ActiveUserViewModel[] = [];
|
||||
let activeUserViewModelMock = new ActiveUserViewModel();
|
||||
activeUserViewModelMock.userName = "some user";
|
||||
// activeUserViewModelMock.lastActive = "3 hrs ago";
|
||||
activeUserViewModelMock.online = true;
|
||||
activeUsersMock.push(activeUserViewModelMock);
|
||||
activeUserViewModelMock = new ActiveUserViewModel();
|
||||
// activeUserViewModelMock.lastActive = "3 hrs ago";
|
||||
activeUserViewModelMock.online = true;
|
||||
activeUserViewModelMock.userName = "some user 2";
|
||||
activeUsersMock.push(activeUserViewModelMock);
|
||||
this.eventHandler(activeUsersMock);
|
||||
}
|
||||
|
||||
public getActiveUsers(): void {
|
||||
this._model.getActiveUsers();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
decryptForUser(dto: ChatMessageDTO, userName: string): string {
|
||||
return this._model.decryptForUser(dto, userName);
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,46 @@
|
||||
import { Client } from "@stomp/stompjs";
|
||||
import { Builder } from "builder-pattern";
|
||||
import * as Handlebars from "handlebars";
|
||||
import * as log from "loglevel";
|
||||
import { ChatController } from "./controller/ChatController";
|
||||
import { UserController } from "./controller/UserController";
|
||||
import { ChatModel } from "./model/ChatModel";
|
||||
import { ChatModelHelper } from "./model/ChatModelHelper";
|
||||
import { UserModel } from "./model/UserModel";
|
||||
import log from "loglevel";
|
||||
import moment from "moment";
|
||||
import SockJS from "sockjs-client";
|
||||
import { AlertifyNotificationService } from "../common/service/AlertifyNotificationService";
|
||||
import { EncryptionServiceFactory } from "../common/service/EncryptionServiceFactory";
|
||||
import { FuseSearchService } from "../common/service/FuseSearchService";
|
||||
import { MarkDownItMarkDownService } from "../common/service/MarkDownItMarkDownService";
|
||||
import { NotificationService } from "../common/service/NotificationService";
|
||||
import { SearchService } from "../common/service/SearchService";
|
||||
import { ChatController } from "./controller/ChatController";
|
||||
import { UserController } from "./controller/UserController";
|
||||
import { ChatModel } from "./model/ChatModel";
|
||||
import { ChatModelHelper } from "./model/ChatModelHelper";
|
||||
import { UserModel } from "./model/UserModel";
|
||||
import { TemplateFactory } from "./template/TemplateFactory";
|
||||
import { ChatView } from "./view/ChatView";
|
||||
import { ChatViewDeps } from "./view/ChatViewDeps";
|
||||
import { UserView } from "./view/UserView";
|
||||
import { UserViewDeps } from "./view/UserViewDeps";
|
||||
import { ActiveUserViewModel } from "./viewmodel/ActiveUserViewModel";
|
||||
import moment = require("moment");
|
||||
import { Credentials } from "../common/global/Credentials";
|
||||
|
||||
// log.setLevel("TRACE");
|
||||
const authToken = Credentials.authToken;
|
||||
|
||||
const chatStompClient = new Client();
|
||||
chatStompClient.webSocketFactory = () => new SockJS("/chat");
|
||||
chatStompClient.activate();
|
||||
|
||||
const pingStompClient = new Client();
|
||||
pingStompClient.webSocketFactory = () => new SockJS("/ping");
|
||||
pingStompClient.activate();
|
||||
|
||||
pingStompClient.onConnect = () => {
|
||||
pingStompClient.publish({ destination: "/app/ping" });
|
||||
pingStompClient.subscribe("/user/queue/ping", (message) => {
|
||||
log.debug(message);
|
||||
log.debug(message.body);
|
||||
});
|
||||
};
|
||||
|
||||
const usersListElement = document.getElementById("contacts-box");
|
||||
const userSearchButton = document.getElementById("user-search");
|
||||
@ -38,7 +58,11 @@ const encryptionService = EncryptionServiceFactory.getEncryptionService();
|
||||
|
||||
const chatModelHelper = new ChatModelHelper(encryptionService, ns);
|
||||
const chatModel = new ChatModel(chatModelHelper);
|
||||
const userModel = new UserModel(ns);
|
||||
const userModel = new UserModel({
|
||||
notificationService: ns,
|
||||
encryptionService,
|
||||
authToken,
|
||||
});
|
||||
const cvDeps: ChatViewDeps = {
|
||||
chatModel: chatModel,
|
||||
// @ts-ignore: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.
|
||||
@ -48,13 +72,19 @@ const cvDeps: ChatViewDeps = {
|
||||
),
|
||||
messageReceiveTemplate: TemplateFactory.getTemplate("msg_container_template"),
|
||||
markdownService: new MarkDownItMarkDownService(),
|
||||
encryptionService: encryptionService,
|
||||
encryptionService,
|
||||
notificationService: ns,
|
||||
userModel: userModel,
|
||||
chatStompClient,
|
||||
};
|
||||
const chatView = new ChatView(cvDeps);
|
||||
chatModel.attach(chatView);
|
||||
const chatController = new ChatController(chatModel, chatView);
|
||||
const chatController = new ChatController({
|
||||
model: chatModel,
|
||||
view: chatView,
|
||||
chatStompClient,
|
||||
encryptionService,
|
||||
});
|
||||
|
||||
const uvDeps: UserViewDeps = {
|
||||
model: userModel,
|
||||
@ -78,7 +108,7 @@ const uvDeps: UserViewDeps = {
|
||||
const userView = new UserView(uvDeps);
|
||||
userModel.attach(userView);
|
||||
const userController = new UserController(userModel, userView);
|
||||
userController.getActiveUsers();
|
||||
// userController.getActiveUsers();
|
||||
|
||||
// @ts-ignore
|
||||
Handlebars.registerHelper("moment", require("helper-moment"));
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Subject } from "../observe/Observable";
|
||||
import { Observer } from "../observe/Observer";
|
||||
import { ObserverData } from "../observe/ObserverData";
|
||||
import { JsonAPI } from "../singleton/JsonAPI";
|
||||
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
|
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
|
||||
import { ChatModelHelper } from "./ChatModelHelper";
|
||||
import log = require("loglevel");
|
||||
import { ObserverData } from "../observe/ObserverData";
|
||||
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
|
||||
import moment = require("moment");
|
||||
import log from "loglevel";
|
||||
import moment from "moment";
|
||||
|
||||
interface Params {
|
||||
userName: string;
|
||||
@ -44,7 +44,7 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
|
||||
log.info("Subject: Detached an observer.");
|
||||
}
|
||||
|
||||
private storeUserMessages(
|
||||
public storeUserMessages(
|
||||
username: string,
|
||||
messages: ChatMessageViewModel[],
|
||||
op: string
|
||||
@ -52,25 +52,28 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
|
||||
switch (op) {
|
||||
case "clear":
|
||||
this._messagesMap.set(username, []);
|
||||
this.notify({ userName: username, data: [], op: op });
|
||||
break;
|
||||
case "page":
|
||||
this._messagesMap.set(
|
||||
username,
|
||||
messages.concat(this.getStoredUserMessages(username))
|
||||
);
|
||||
this.notify({ userName: username, data: messages, op: op });
|
||||
break;
|
||||
// case "page": this._messagesMap.set(username, messages);
|
||||
case "new":
|
||||
this._messagesMap.set(
|
||||
username,
|
||||
this.getStoredUserMessages(username).concat(messages)
|
||||
);
|
||||
this.notify({ userName: username, data: messages, op: op });
|
||||
break;
|
||||
case "update":
|
||||
this._messagesMap.set(
|
||||
username,
|
||||
this.getStoredUserMessages(username).concat(messages)
|
||||
);
|
||||
this.notify({ userName: username, data: messages, op: op });
|
||||
break;
|
||||
default:
|
||||
new Error("Invalid option");
|
||||
@ -102,47 +105,18 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "new":
|
||||
{
|
||||
const od: ObserverData<ChatMessageViewModel> = {
|
||||
data: p.data,
|
||||
op: p.op,
|
||||
};
|
||||
for (const observer of this._observers) {
|
||||
observer.update(od);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "page":
|
||||
{
|
||||
const od: ObserverData<ChatMessageViewModel> = {
|
||||
data: p.data,
|
||||
op: p.op,
|
||||
};
|
||||
for (const observer of this._observers) {
|
||||
observer.update(od);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "update":
|
||||
{
|
||||
const od: ObserverData<ChatMessageViewModel> = {
|
||||
data: p.data,
|
||||
op: p.op,
|
||||
};
|
||||
for (const observer of this._observers) {
|
||||
observer.update(od);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
log.error("error");
|
||||
const od: ObserverData<ChatMessageViewModel> = {
|
||||
data: p.data,
|
||||
op: p.op,
|
||||
};
|
||||
for (const observer of this._observers) {
|
||||
observer.update(od);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public someBusinessMethod(chatMessageList: ChatMessageViewModel[]): void {}
|
||||
|
||||
public clear(): void {
|
||||
log.info("Clearing model");
|
||||
this._messagePageMap.set(JsonAPI.contactName!, 0);
|
||||
@ -200,7 +174,7 @@ export class ChatModel implements Subject<ChatMessageViewModel> {
|
||||
}
|
||||
|
||||
this.storeUserMessages(aVm.userName!, cVMsFiltered, op);
|
||||
this.notify({ userName: aVm.userName!, data: cVMsFiltered, op: op });
|
||||
// this.notify({ userName: aVm.userName!, data: cVMsFiltered, op: op });
|
||||
}
|
||||
|
||||
return cVMsFiltered;
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as log from "loglevel";
|
||||
import log from "loglevel";
|
||||
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO";
|
||||
import { Sprintf } from "../../common/global/Sprintf";
|
||||
import { EncryptionService } from "../../common/service/EncryptionService";
|
||||
import { NotificationService } from "../../common/service/NotificationService";
|
||||
import { JsonAPI } from "../singleton/JsonAPI";
|
||||
import { Sprintf } from "../../common/global/Sprintf";
|
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
|
||||
import { fetchErrorHandler } from "./FetchErrorHandler";
|
||||
|
||||
@ -88,7 +88,7 @@ export class ChatModelHelper {
|
||||
const vm = new ChatMessageViewModel();
|
||||
vm.fromUser = chatMessageDTO.fromUser;
|
||||
vm.toUser = chatMessageDTO.toUser;
|
||||
vm.messageTime = chatMessageDTO.messageTime;
|
||||
vm.messageTime = new Date(chatMessageDTO.messageTime);
|
||||
vm.message = await this._encryptionService.decryptAsPromise(
|
||||
passphrase,
|
||||
chatMessageDTO.messageCipher
|
||||
@ -106,7 +106,7 @@ export class ChatModelHelper {
|
||||
// vm.messageTime = chatMessageDTO.messageTime;
|
||||
chatMessageDTO.messageTime == null
|
||||
? log.error("Message time somehow null")
|
||||
: (vm.messageTime = chatMessageDTO.messageTime);
|
||||
: (vm.messageTime = new Date(chatMessageDTO.messageTime));
|
||||
vm.message = this._encryptionService.decrypt(
|
||||
passphrase,
|
||||
chatMessageDTO.messageCipher
|
||||
|
@ -1,11 +1,21 @@
|
||||
import * as log from "loglevel";
|
||||
import log from "loglevel";
|
||||
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO";
|
||||
import { NotificationService } from "../../common/service/NotificationService";
|
||||
import { Subject } from "../observe/Observable";
|
||||
import { Observer } from "../observe/Observer";
|
||||
import { JsonAPI } from "../singleton/JsonAPI";
|
||||
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
|
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
|
||||
import { fetchErrorHandler } from "./FetchErrorHandler";
|
||||
import { NotificationService } from "../../common/service/NotificationService";
|
||||
import { EncryptionService } from "../../common/service/EncryptionService";
|
||||
import { createApiHeaders } from "../../common/ajax/util";
|
||||
import { getActiveUsers } from "../../common/ajax/Users";
|
||||
|
||||
export interface Props {
|
||||
encryptionService: EncryptionService;
|
||||
notificationService: NotificationService;
|
||||
authToken: string;
|
||||
}
|
||||
export class UserModel implements Subject<ActiveUserViewModel> {
|
||||
/**
|
||||
* @type {Observer[]} List of subscribers. In real life, the list of
|
||||
@ -14,12 +24,18 @@ export class UserModel implements Subject<ActiveUserViewModel> {
|
||||
*/
|
||||
private readonly observers: Observer<ActiveUserViewModel>[] = [];
|
||||
|
||||
private _activeUsersList: ActiveUserViewModel[] = Array();
|
||||
private readonly _notificationService: NotificationService;
|
||||
private readonly _activeUsersList: ActiveUserViewModel[] = Array();
|
||||
private readonly _props: Props;
|
||||
|
||||
constructor(notificationService: NotificationService) {
|
||||
constructor(props: Props) {
|
||||
this._activeUsersList = [];
|
||||
this._notificationService = notificationService;
|
||||
this._props = props;
|
||||
|
||||
getActiveUsers(props.authToken).then((data) => {
|
||||
log.debug(data);
|
||||
this._activeUsersList.push(...data);
|
||||
this.notify();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,63 +70,16 @@ export class UserModel implements Subject<ActiveUserViewModel> {
|
||||
}
|
||||
}
|
||||
|
||||
public someBusinessMethod(activeuserList: ActiveUserViewModel[]): void {
|
||||
this._activeUsersList = activeuserList;
|
||||
this.helperMethod();
|
||||
log.info(`Subject: My state has just changed`);
|
||||
log.trace(activeuserList);
|
||||
this.notify();
|
||||
}
|
||||
|
||||
updateLastActive(username: String, lastActive: Date): void {
|
||||
this._activeUsersList
|
||||
.filter(u => u.userName == username)
|
||||
.forEach(u => u.lastActive = lastActive)
|
||||
.filter((u) => u.userName == username)
|
||||
.forEach((u) => (u.lastActive = lastActive));
|
||||
}
|
||||
|
||||
/**
|
||||
* getActiveUsers
|
||||
*/
|
||||
public getActiveUsers(): void {
|
||||
if (JsonAPI.authToken != null) {
|
||||
this._getActiveUsersAjax(JsonAPI.authToken).then((data) => {
|
||||
// // activeUsers = data;
|
||||
// sessionStorage.setItem('activeUsers', JSON.stringify(data));
|
||||
// log.trace(sessionStorage.getItem('activeUsers'));
|
||||
log.info(`Subject: received ajax active users`);
|
||||
data.map((d: any) => {
|
||||
if (d.lastActive == null) return null;
|
||||
|
||||
d.lastActive = new Date(d.lastActive);
|
||||
return d;
|
||||
});
|
||||
this._activeUsersList = data;
|
||||
this.notify();
|
||||
});
|
||||
} else {
|
||||
log.error("Auth token is null");
|
||||
}
|
||||
public decryptForUser(dto: ChatMessageDTO, userName: string): string {
|
||||
const passphrase =
|
||||
this._activeUsersList.find((u) => u.userName === userName)?.passphrase ||
|
||||
"";
|
||||
return this._props.encryptionService.decrypt(passphrase, dto.messageCipher);
|
||||
}
|
||||
|
||||
private async _getActiveUsersAjax(authToken: string): Promise<any> {
|
||||
let headers = new Headers();
|
||||
headers.append("X-AUTH-TOKEN", authToken);
|
||||
let response = await fetch(JsonAPI.ACTIVE_USERS_GET, {
|
||||
method: "GET",
|
||||
headers: headers,
|
||||
});
|
||||
log.debug(response.clone());
|
||||
if (fetchErrorHandler(response.clone(), this._notificationService)) {
|
||||
return null;
|
||||
}
|
||||
let data = await response.json();
|
||||
// return data;
|
||||
return new Promise((resolve, reject) => {
|
||||
if (data != null) {
|
||||
resolve(data);
|
||||
} else reject("Response data null");
|
||||
});
|
||||
}
|
||||
|
||||
private helperMethod() { }
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import * as DOMPurify from "dompurify";
|
||||
import * as log from "loglevel";
|
||||
import DOMPurify from "dompurify";
|
||||
import log from "loglevel";
|
||||
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO";
|
||||
import { MessageCipherDTO } from "../../common/dto/MessageCipherDTO";
|
||||
import { ChatModel } from "../model/ChatModel";
|
||||
import { Observer } from "../observe/Observer";
|
||||
import { EncryptionService } from "../../common/service/EncryptionService";
|
||||
import { MarkDownService } from "../../common/service/MarkDownService";
|
||||
import { NotificationService } from "../../common/service/NotificationService";
|
||||
import { ChatModel } from "../model/ChatModel";
|
||||
import { UserModel } from "../model/UserModel";
|
||||
import { Observer } from "../observe/Observer";
|
||||
import { ObserverData } from "../observe/ObserverData";
|
||||
import { JsonAPI } from "../singleton/JsonAPI";
|
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
|
||||
import { ChatViewDeps } from "./ChatViewDeps";
|
||||
import { fetchHandler } from "./FetchHandler";
|
||||
import { ObserverData } from "../observe/ObserverData";
|
||||
import { NotificationService } from "../../common/service/NotificationService";
|
||||
import { UserModel } from "../model/UserModel";
|
||||
|
||||
export class ChatView implements Observer<ChatMessageViewModel> {
|
||||
private readonly _chatModel: ChatModel;
|
||||
@ -27,6 +27,7 @@ export class ChatView implements Observer<ChatMessageViewModel> {
|
||||
private readonly _encryptionService: EncryptionService;
|
||||
private readonly _notificationService: NotificationService;
|
||||
private readonly _userModel: UserModel;
|
||||
private readonly _deps: ChatViewDeps;
|
||||
|
||||
constructor(deps: ChatViewDeps) {
|
||||
this._messageContainer = deps.messageContainer;
|
||||
@ -37,6 +38,7 @@ export class ChatView implements Observer<ChatMessageViewModel> {
|
||||
this._encryptionService = deps.encryptionService;
|
||||
this._notificationService = deps.notificationService;
|
||||
this._userModel = deps.userModel;
|
||||
this._deps = deps;
|
||||
this._initEventListeners();
|
||||
|
||||
$(document).ready(function () {
|
||||
@ -181,7 +183,7 @@ export class ChatView implements Observer<ChatMessageViewModel> {
|
||||
};
|
||||
|
||||
this.update({ data: new Array(context), op: "new" });
|
||||
this._userModel.updateLastActive(contactName, msgTime)
|
||||
this._userModel.updateLastActive(contactName, msgTime);
|
||||
this._userModel.notify();
|
||||
|
||||
let messageCipher: MessageCipherDTO = this._encryptionService.encrypt(
|
||||
@ -194,28 +196,17 @@ export class ChatView implements Observer<ChatMessageViewModel> {
|
||||
messageCipher: messageCipher,
|
||||
messageTime: msgTime.toISOString(),
|
||||
};
|
||||
this._sendMessageAJAX(chatMessageDTO);
|
||||
// this._sendMessageAJAX(chatMessageDTO);
|
||||
this._sendMessageWS(chatMessageDTO);
|
||||
}
|
||||
|
||||
private _sendMessageAJAX(chatMessageDTO: any): void {
|
||||
let headers = new Headers();
|
||||
// console.log("Token = " + btoa("hmm" + ":" + "hmm"))
|
||||
|
||||
// headers.append('Accept','application/json')
|
||||
headers.append("Content-Type", "application/json");
|
||||
// headers.append('Authorization', basicAuthToken);
|
||||
// @ts-ignore
|
||||
headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
|
||||
fetch(JsonAPI.MESSAGE_POST, {
|
||||
method: "POST",
|
||||
headers: headers,
|
||||
private _sendMessageWS(chatMessageDTO: ChatMessageDTO): void {
|
||||
// if (this._deps.chatStompClinet.connected) {
|
||||
this._deps.chatStompClient.publish({
|
||||
body: JSON.stringify(chatMessageDTO),
|
||||
})
|
||||
.then((response) => {
|
||||
log.debug(response);
|
||||
return response.clone();
|
||||
})
|
||||
.then((response) => fetchHandler(response, this._notificationService));
|
||||
destination: "/app/chat",
|
||||
});
|
||||
// }
|
||||
}
|
||||
|
||||
private _chatMessagePageLoadAjax() {
|
||||
|
@ -4,14 +4,16 @@ import { MarkDownService } from "../../common/service/MarkDownService";
|
||||
import { EncryptionService } from "../../common/service/EncryptionService";
|
||||
import { NotificationService } from "../../common/service/NotificationService";
|
||||
import { UserModel } from "../model/UserModel";
|
||||
import { Client } from "@stomp/stompjs";
|
||||
|
||||
export interface ChatViewDeps {
|
||||
chatModel: ChatModel;
|
||||
messageContainer: HTMLElement;
|
||||
messageSendTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
|
||||
messageReceiveTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
|
||||
markdownService: MarkDownService;
|
||||
encryptionService: EncryptionService;
|
||||
notificationService: NotificationService;
|
||||
userModel: UserModel
|
||||
}
|
||||
chatModel: ChatModel;
|
||||
messageContainer: HTMLElement;
|
||||
messageSendTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
|
||||
messageReceiveTemplate: Handlebars.TemplateDelegate<ChatMessageViewModel>;
|
||||
markdownService: MarkDownService;
|
||||
encryptionService: EncryptionService;
|
||||
notificationService: NotificationService;
|
||||
userModel: UserModel;
|
||||
chatStompClient: Client;
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { Observer } from "../observe/Observer";
|
||||
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
|
||||
import { ChatModel } from "../model/ChatModel";
|
||||
import log = require("loglevel");
|
||||
import * as DOMPurify from "dompurify";
|
||||
import DOMPurify from "dompurify";
|
||||
import { NotificationService } from "../../common/service/NotificationService";
|
||||
import { SearchService } from "../../common/service/SearchService";
|
||||
import { ChatModel } from "../model/ChatModel";
|
||||
import { UserModel } from "../model/UserModel";
|
||||
import { UserViewDeps } from "./UserViewDeps";
|
||||
import { Observer } from "../observe/Observer";
|
||||
import { ObserverData } from "../observe/ObserverData";
|
||||
import { JsonAPI } from "../singleton/JsonAPI";
|
||||
import { NotificationService } from "../../common/service/NotificationService";
|
||||
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
|
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
|
||||
import { UserViewDeps } from "./UserViewDeps";
|
||||
import log from "loglevel";
|
||||
|
||||
export class UserView implements Observer<ActiveUserViewModel> {
|
||||
private readonly _model: UserModel;
|
||||
@ -89,58 +89,63 @@ export class UserView implements Observer<ActiveUserViewModel> {
|
||||
JsonAPI.contactName = userName;
|
||||
// @ts-ignore: Object is possibly 'null'.
|
||||
document.getElementById("user-name-span").innerText = userName;
|
||||
let currentUser = this._model.activeUsersList.find((vm) => vm.userName === userName) || new ActiveUserViewModel();
|
||||
let currentUser =
|
||||
this._model.activeUsersList.find((vm) => vm.userName === userName) ||
|
||||
new ActiveUserViewModel();
|
||||
currentUser.userName = userName;
|
||||
|
||||
if (!currentUser?.passphrase) {
|
||||
this._notificationService.passphrasePrompt(
|
||||
async (result) => {
|
||||
if (result) {
|
||||
|
||||
const valid = await this._chatModel.isPassphraseValid(result, currentUser.userName!);
|
||||
if (!valid) {
|
||||
bootbox.alert("Some error occured. Please check your password");
|
||||
log.error("invalid password");
|
||||
return;
|
||||
}
|
||||
currentUser.unlocked = true;
|
||||
currentUser.passphrase = result;
|
||||
const chatMessages: ChatMessageViewModel[] = await this._chatModel.getMessages(currentUser, "new");
|
||||
|
||||
this._model.activeUsersList
|
||||
.filter((v) => v.userName == currentUser!.userName)
|
||||
.forEach((v) => {
|
||||
v.passphrase = result;
|
||||
v.unlocked = true;
|
||||
if (chatMessages.length > 0) {
|
||||
v.lastMessageTime = new Date(
|
||||
chatMessages[chatMessages.length - 1].messageTime
|
||||
);
|
||||
const lastMessageText = (v.lastMessageText =
|
||||
chatMessages[chatMessages.length - 1].message);
|
||||
if (lastMessageText.length > 15) {
|
||||
v.lastMessageText =
|
||||
chatMessages[chatMessages.length - 1].message.slice(
|
||||
0,
|
||||
15
|
||||
) + "...";
|
||||
}
|
||||
}
|
||||
});
|
||||
this._promptHandler(currentUser);
|
||||
|
||||
this._notificationService.passphrasePrompt(async (result) => {
|
||||
if (result) {
|
||||
const valid = await this._chatModel.isPassphraseValid(
|
||||
result,
|
||||
currentUser.userName!
|
||||
);
|
||||
if (!valid) {
|
||||
bootbox.alert("Some error occured. Please check your password");
|
||||
log.error("invalid password");
|
||||
return;
|
||||
}
|
||||
currentUser.unlocked = true;
|
||||
currentUser.passphrase = result;
|
||||
const chatMessages: ChatMessageViewModel[] = await this._chatModel.getMessages(
|
||||
currentUser,
|
||||
"new"
|
||||
);
|
||||
|
||||
this._model.activeUsersList
|
||||
.filter((v) => v.userName == currentUser!.userName)
|
||||
.forEach((v) => {
|
||||
v.passphrase = result;
|
||||
v.unlocked = true;
|
||||
if (chatMessages.length > 0) {
|
||||
v.lastMessageTime = new Date(
|
||||
chatMessages[chatMessages.length - 1].messageTime
|
||||
);
|
||||
const lastMessageText = (v.lastMessageText =
|
||||
chatMessages[chatMessages.length - 1].message);
|
||||
if (lastMessageText.length > 15) {
|
||||
v.lastMessageText =
|
||||
chatMessages[chatMessages.length - 1].message.slice(0, 15) +
|
||||
"...";
|
||||
}
|
||||
}
|
||||
});
|
||||
this._promptHandler(currentUser);
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
});
|
||||
} else {
|
||||
this._chatModel.getMessages(currentUser, "new");
|
||||
}
|
||||
|
||||
el.className += " active";
|
||||
if (currentUser.unlocked && currentUser.lastMessageTime) {
|
||||
this._newMessagesLoop = setInterval(
|
||||
this._chatModel.getMessages.bind(this._chatModel, currentUser, "update"),
|
||||
this._chatModel.getMessages.bind(
|
||||
this._chatModel,
|
||||
currentUser,
|
||||
"update"
|
||||
),
|
||||
10_000
|
||||
);
|
||||
this._model.notify();
|
||||
@ -177,9 +182,8 @@ export class UserView implements Observer<ActiveUserViewModel> {
|
||||
let searchResult = this._searchService.search(list, searchTerm);
|
||||
this.update({ data: searchResult, op: "" });
|
||||
log.debug(searchResult);
|
||||
}
|
||||
else {
|
||||
this._notificationService.error("Please enter a name")
|
||||
} else {
|
||||
this._notificationService.error("Please enter a name");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -28,17 +28,11 @@ export async function sendReencryptedMessages(
|
||||
rencryptionDTOs: ReencryptionDTO[],
|
||||
authToken: string
|
||||
) {
|
||||
let headers = new Headers();
|
||||
// console.log("Token = " + btoa("hmm" + ":" + "hmm"))
|
||||
|
||||
// headers.append('Accept','application/json')
|
||||
headers.append("Content-Type", "application/json");
|
||||
headers.append("X-AUTH-TOKEN", authToken);
|
||||
fetch(Routes.Admin.reencrypt, {
|
||||
return fetch(Routes.Admin.reencrypt, {
|
||||
method: "POST",
|
||||
headers: headers,
|
||||
headers: createApiHeaders(authToken),
|
||||
body: JSON.stringify(rencryptionDTOs),
|
||||
}).then((response) => console.log(response));
|
||||
});
|
||||
}
|
||||
|
||||
export async function getOneMessage(
|
||||
@ -52,10 +46,6 @@ export async function getOneMessage(
|
||||
method: "GET",
|
||||
headers: createApiHeaders(authToken),
|
||||
});
|
||||
log.debug(response.clone());
|
||||
// if (fetchErrorHandler(response.clone(), this._notificationService)) {
|
||||
// return null;
|
||||
// }
|
||||
const data: Promise<any> = await response.json();
|
||||
function func(data: any) {
|
||||
const d1 = data.map((d: any) => {
|
||||
@ -77,3 +67,15 @@ export async function getStats(authToken: string) {
|
||||
});
|
||||
return (await response.json()) as StatsDTO;
|
||||
}
|
||||
|
||||
export async function sendMessageAJAX(
|
||||
chatMessageDTO: ChatMessageDTO,
|
||||
authToken: string
|
||||
) {
|
||||
const response = fetch(JsonAPI.MESSAGE_POST, {
|
||||
method: "POST",
|
||||
headers: createApiHeaders(authToken),
|
||||
body: JSON.stringify(chatMessageDTO),
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Routes } from "../routes/Routes";
|
||||
import { createApiHeaders } from "./util";
|
||||
import { AdminUserDTO } from "../dto/AdminUserDTO";
|
||||
import { ActiveUserViewModel } from "../../chat/viewmodel/ActiveUserViewModel";
|
||||
import { JsonAPI } from "../../chat/singleton/JsonAPI";
|
||||
|
||||
export async function getOtherUsers(
|
||||
authToken: string
|
||||
@ -13,3 +15,20 @@ export async function getOtherUsers(
|
||||
let data = (await response.json()) as AdminUserDTO[];
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getActiveUsers(
|
||||
authToken: string
|
||||
): Promise<ActiveUserViewModel[]> {
|
||||
const response = await fetch(JsonAPI.ACTIVE_USERS_GET, {
|
||||
method: "GET",
|
||||
headers: createApiHeaders(authToken),
|
||||
});
|
||||
const data = await response.json();
|
||||
const data2 = data.map((d: any) => {
|
||||
if (d.lastActive == null) return d;
|
||||
|
||||
d.lastActive = new Date(d.lastActive);
|
||||
return d;
|
||||
}) as ActiveUserViewModel[];
|
||||
return data2;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { MessageCipherDTO } from "./MessageCipherDTO";
|
||||
|
||||
export class ChatMessageDTO {
|
||||
public toUser: string = "";
|
||||
public fromUser: string = "";
|
||||
public messageCipher!: MessageCipherDTO;
|
||||
public messageTime: Date = new Date();
|
||||
export interface ChatMessageDTO {
|
||||
toUser: string;
|
||||
fromUser: string;
|
||||
messageCipher: MessageCipherDTO;
|
||||
messageTime: string;
|
||||
}
|
||||
|
@ -1,40 +1,12 @@
|
||||
// export class MessageCipherDTO {
|
||||
// // iv!: string;
|
||||
// // v!: number;
|
||||
// // iterations!: number;
|
||||
// // keySize!: number;
|
||||
// // tagSize!: number;
|
||||
// // mode!: string;
|
||||
// // adata!: string;
|
||||
// // cipher!: string;
|
||||
// // salt!: string;
|
||||
// // cipherText!: string;
|
||||
|
||||
// iv!: string;
|
||||
// v!: number;
|
||||
// iter!: number;
|
||||
// ks!: number;
|
||||
// ts!: number;
|
||||
// mode!: string;
|
||||
// adata!: string;
|
||||
// cipher!: string;
|
||||
// salt!: string;
|
||||
// ct!: string;
|
||||
|
||||
// // public toMessageCipherDTO {
|
||||
|
||||
// // }
|
||||
// }
|
||||
|
||||
export interface MessageCipherDTO {
|
||||
iv: string,
|
||||
v: number,
|
||||
iter: number,
|
||||
ks: number,
|
||||
ts: number,
|
||||
mode: string,
|
||||
adata: string,
|
||||
cipher: string,
|
||||
salt: string,
|
||||
ct: string,
|
||||
}
|
||||
iv: string;
|
||||
v: number;
|
||||
iter: number;
|
||||
ks: number;
|
||||
ts: number;
|
||||
mode: string;
|
||||
adata: string;
|
||||
cipher: string;
|
||||
salt: string;
|
||||
ct: string;
|
||||
}
|
||||
|
@ -1,34 +1,34 @@
|
||||
{
|
||||
"properties" : [
|
||||
{
|
||||
"name" : "chatto.token.timeout-duration",
|
||||
"type" : "java.lang.String",
|
||||
"description" : "The duration for auth token validity. Token expires after this period of inactivity"
|
||||
},
|
||||
{
|
||||
"name" : "chatto.frontend.log-level",
|
||||
"type" : "java.lang.String",
|
||||
"description" : "The log level for the frontend JS application"
|
||||
},
|
||||
{
|
||||
"name" : "chatto.frontend.chat-page-size",
|
||||
"type" : "java.lang.Integer",
|
||||
"description" : "The pagination size for the chat area"
|
||||
},
|
||||
{
|
||||
"name" : "chat-worker-bundle",
|
||||
"type" : "java.lang.String",
|
||||
"description" : "Name of the chat worker js bundle"
|
||||
},
|
||||
{
|
||||
"name" : "chat-bundle",
|
||||
"type" : "java.lang.String",
|
||||
"description" : "Name of the chatjs bundle"
|
||||
},
|
||||
{
|
||||
"name" : "admin-bundle",
|
||||
"type" : "java.lang.String",
|
||||
"description" : "Name of the admin js bundle"
|
||||
}
|
||||
]
|
||||
"properties": [
|
||||
{
|
||||
"name": "chatto.token.timeout-duration",
|
||||
"type": "java.lang.String",
|
||||
"description": "The duration for auth token validity. Token expires after this period of inactivity"
|
||||
},
|
||||
{
|
||||
"name": "chatto.frontend.log-level",
|
||||
"type": "java.lang.String",
|
||||
"description": "The log level for the frontend JS application"
|
||||
},
|
||||
{
|
||||
"name": "chatto.frontend.chat-page-size",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "The pagination size for the chat area"
|
||||
},
|
||||
{
|
||||
"name": "chat-worker-bundle",
|
||||
"type": "java.lang.String",
|
||||
"description": "Name of the chat worker js bundle"
|
||||
},
|
||||
{
|
||||
"name": "chat-bundle",
|
||||
"type": "java.lang.String",
|
||||
"description": "Name of the chatjs bundle"
|
||||
},
|
||||
{
|
||||
"name": "admin-bundle",
|
||||
"type": "java.lang.String",
|
||||
"description": "Name of the admin js bundle"
|
||||
}
|
||||
]
|
||||
}
|
@ -2,9 +2,6 @@ package db.migration;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.security.SecureRandom;
|
||||
import java.sql.PreparedStatement;
|
||||
|
||||
@ -15,75 +12,50 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
public class V3__add_default_admin extends BaseJavaMigration {
|
||||
|
||||
private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
|
||||
private final SecureRandom random = new SecureRandom();
|
||||
private final SecureRandom random = new SecureRandom();
|
||||
|
||||
/** different dictionaries used */
|
||||
private final String ALPHA_CAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private final String ALPHA = "abcdefghijklmnopqrstuvwxyz";
|
||||
private final String NUMERIC = "0123456789";
|
||||
private final String SPECIAL_CHARS = "!@#$%^&*_=+-/";
|
||||
/** different dictionaries used */
|
||||
private final String ALPHA_CAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private final String ALPHA = "abcdefghijklmnopqrstuvwxyz";
|
||||
private final String NUMERIC = "0123456789";
|
||||
private final String SPECIAL_CHARS = "!@#$%^&*_=+-/";
|
||||
|
||||
/**
|
||||
* Method will generate random string based on the parameters
|
||||
*
|
||||
* @param len
|
||||
* the length of the random string
|
||||
* @param dic
|
||||
* the dictionary used to generate the password
|
||||
* @return the random password
|
||||
*/
|
||||
public String generatePassword(int len, String dic) {
|
||||
String result = "";
|
||||
for (int i = 0; i < len; i++) {
|
||||
int index = random.nextInt(dic.length());
|
||||
result += dic.charAt(index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Method will generate random string based on the parameters
|
||||
*
|
||||
* @param len the length of the random string
|
||||
* @param dic the dictionary used to generate the password
|
||||
* @return the random password
|
||||
*/
|
||||
public String generatePassword(int len, String dic) {
|
||||
String result = "";
|
||||
for (int i = 0; i < len; i++) {
|
||||
int index = random.nextInt(dic.length());
|
||||
result += dic.charAt(index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(final Context context) throws Exception {
|
||||
try (final PreparedStatement ps = context.getConnection()
|
||||
.prepareStatement(
|
||||
"insert into users (user_id, name, password) values (0,?,?)")) {
|
||||
final String generatedPassword = generatePassword(60,
|
||||
ALPHA_CAPS + ALPHA + SPECIAL_CHARS);
|
||||
final BufferedWriter bw = new BufferedWriter(
|
||||
new FileWriter("gen-password.txt"));
|
||||
@Override
|
||||
public void migrate(final Context context) throws Exception {
|
||||
try (final PreparedStatement ps = context.getConnection()
|
||||
.prepareStatement("insert into users (user_id, name, password) values (0,?,?)")) {
|
||||
final String generatedPassword = generatePassword(60, ALPHA_CAPS + ALPHA + SPECIAL_CHARS);
|
||||
final BufferedWriter bw = new BufferedWriter(new FileWriter("gen-password.txt"));
|
||||
bw.write(generatedPassword);
|
||||
bw.write("\nPlease delete this file");
|
||||
bw.close();
|
||||
ps.setString(1, "admin");
|
||||
ps.setString(2, passwordEncoder.encode(generatedPassword));
|
||||
ps.execute();
|
||||
}
|
||||
|
||||
bw.write(generatedPassword);
|
||||
bw.write("\nPlease delete this file");
|
||||
bw.close();
|
||||
|
||||
final var perms = Files
|
||||
.getPosixFilePermissions(Paths.get("gen-password.txt"));
|
||||
|
||||
// add owners permission
|
||||
perms.add(PosixFilePermission.OWNER_READ);
|
||||
perms.add(PosixFilePermission.OWNER_WRITE);
|
||||
perms.remove(PosixFilePermission.OWNER_EXECUTE);
|
||||
// add group permissions
|
||||
perms.remove(PosixFilePermission.GROUP_READ);
|
||||
perms.remove(PosixFilePermission.GROUP_WRITE);
|
||||
perms.remove(PosixFilePermission.GROUP_EXECUTE);
|
||||
// add others permissions
|
||||
perms.remove(PosixFilePermission.OTHERS_READ);
|
||||
perms.remove(PosixFilePermission.OTHERS_WRITE);
|
||||
perms.remove(PosixFilePermission.OTHERS_EXECUTE);
|
||||
Files.setPosixFilePermissions(Paths.get("gen-password.txt"), perms);
|
||||
|
||||
ps.setString(1, "admin");
|
||||
ps.setString(2, passwordEncoder.encode(generatedPassword));
|
||||
ps.execute();
|
||||
}
|
||||
|
||||
try (final PreparedStatement ps = context.getConnection()
|
||||
.prepareStatement(
|
||||
"insert into users_roles (user_id, role_id) values (1,0)")) {
|
||||
ps.execute();
|
||||
}
|
||||
}
|
||||
try (final PreparedStatement ps = context.getConnection()
|
||||
.prepareStatement("insert into users_roles (user_id, role_id) values (1,0)")) {
|
||||
ps.execute();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -17,8 +17,8 @@ public class ChattoApplication extends SpringBootServletInitializer {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Profile("development")
|
||||
@ComponentScan(lazyInit = true)
|
||||
static class LocalConfig {
|
||||
}
|
||||
@Profile("development")
|
||||
@ComponentScan(lazyInit = true)
|
||||
static class LocalConfig {
|
||||
}
|
||||
}
|
@ -12,21 +12,19 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationEn
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public final class RESTAuthenticationEntryPoint
|
||||
extends BasicAuthenticationEntryPoint {
|
||||
public final class RESTAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request,
|
||||
HttpServletResponse response, AuthenticationException authEx)
|
||||
throws IOException, ServletException {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.println("HTTP ApplicationStatus 401 - " + authEx.getMessage());
|
||||
}
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
|
||||
throws IOException, ServletException {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.println("HTTP ApplicationStatus 401 - " + authEx.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
setRealmName("Chatto");
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
setRealmName("Chatto");
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,7 @@ import org.springframework.boot.web.servlet.support.SpringBootServletInitializer
|
||||
public class ServletInitializer extends SpringBootServletInitializer {
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(
|
||||
SpringApplicationBuilder application) {
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||
return application.sources(ChattoApplication.class);
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,16 @@ import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
// @EnableWebMvc
|
||||
//@EnableWebMvc
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/api/**").allowedOrigins("*")
|
||||
.allowedMethods("POST", "GET", "OPTIONS").allowedHeaders("*")
|
||||
.allowCredentials(false).maxAge(3600);
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/api/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedMethods("POST","GET","OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(false).maxAge(3600);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -4,10 +4,7 @@ import java.awt.image.BufferedImage;
|
||||
|
||||
interface CaptchaBehaviour {
|
||||
public BufferedImage generateCaptcha();
|
||||
|
||||
public BufferedImage generateCaptcha(String captchaText);
|
||||
|
||||
public String getRandomChars(int size);
|
||||
|
||||
public String getRandomChars();
|
||||
}
|
||||
|
@ -6,10 +6,9 @@ import lombok.Builder;
|
||||
|
||||
/*Class for providing your own captcha generator*/
|
||||
@Builder
|
||||
public class ManualCaptchaBehaviour implements CaptchaBehaviour {
|
||||
public class ManualCaptchaBehaviour implements CaptchaBehaviour{
|
||||
private final int length;
|
||||
private final String style;
|
||||
|
||||
@Override
|
||||
public BufferedImage generateCaptcha() {
|
||||
// TODO Auto-generated method stub
|
||||
@ -34,4 +33,5 @@ public class ManualCaptchaBehaviour implements CaptchaBehaviour {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -10,183 +10,174 @@ import java.io.IOException;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This class represents a simple captcha consisting of an image {@code png} and
|
||||
* its text value. Comic Neue Bold Font. Capital english letters {@code ONLY}.
|
||||
* This class represents a simple captcha consisting
|
||||
* of an image {@code png} and its text value.
|
||||
* Comic Neue Bold Font.
|
||||
* Capital english letters {@code ONLY}.
|
||||
*
|
||||
* @since 1.3
|
||||
* @author Gennadiy Golovin
|
||||
*/
|
||||
public final class SimpleCaptcha {
|
||||
|
||||
private BufferedImage imagePng;
|
||||
private char[] text;
|
||||
private BufferedImage imagePng;
|
||||
private char[] text;
|
||||
|
||||
/**
|
||||
* Initializes a newly created default object consisting of 8 capital
|
||||
* english letters.
|
||||
*/
|
||||
public SimpleCaptcha() {
|
||||
this.text = getRandomChars();
|
||||
/**
|
||||
* Initializes a newly created default object
|
||||
* consisting of 8 capital english letters.
|
||||
*/
|
||||
public SimpleCaptcha() {
|
||||
this.text = getRandomChars();
|
||||
|
||||
try {
|
||||
generateCaptcha();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
try {
|
||||
generateCaptcha();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a newly created object, which length depends on the passed
|
||||
* {@code int} parameter, which {@code MUST} be greater than 0. If the
|
||||
* condition is not met, initializes a newly created default object
|
||||
* consisting of 8 symbols.
|
||||
*
|
||||
* @param length
|
||||
* the quantity of symbols, that the captcha consists of, greater
|
||||
* than 0.
|
||||
*/
|
||||
public SimpleCaptcha(int length) {
|
||||
if (length < 1) {
|
||||
this.text = getRandomChars();
|
||||
} else {
|
||||
this.text = getRandomChars(length);
|
||||
}
|
||||
/**
|
||||
* Initializes a newly created object, which length
|
||||
* depends on the passed {@code int} parameter,
|
||||
* which {@code MUST} be greater than 0.
|
||||
* If the condition is not met, initializes a newly
|
||||
* created default object consisting of 8 symbols.
|
||||
*
|
||||
* @param length the quantity of symbols, that the
|
||||
* captcha consists of, greater than 0.
|
||||
*/
|
||||
public SimpleCaptcha(int length) {
|
||||
if (length < 1) {
|
||||
this.text = getRandomChars();
|
||||
} else {
|
||||
this.text = getRandomChars(length);
|
||||
}
|
||||
|
||||
try {
|
||||
generateCaptcha();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
try {
|
||||
generateCaptcha();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a newly created object based on the passed {@link String}
|
||||
* parameter, consisting of capital english letters. If the condition is not
|
||||
* met, initializes a newly created default object consisting of 8 capital
|
||||
* english letters.
|
||||
*
|
||||
* @param text
|
||||
* the text string with the value of the captcha, length greater
|
||||
* than 0.
|
||||
*/
|
||||
public SimpleCaptcha(String text) {
|
||||
if (text == null || text.equals("")) {
|
||||
this.text = getRandomChars();
|
||||
} else {
|
||||
this.text = text.toCharArray();
|
||||
}
|
||||
/**
|
||||
* Initializes a newly created object based on the passed
|
||||
* {@link String} parameter, consisting of capital english
|
||||
* letters. If the condition is not met, initializes a newly
|
||||
* created default object consisting of 8 capital english letters.
|
||||
*
|
||||
* @param text the text string with the value of the captcha,
|
||||
* length greater than 0.
|
||||
*/
|
||||
public SimpleCaptcha(String text) {
|
||||
if (text == null || text.equals("")) {
|
||||
this.text = getRandomChars();
|
||||
} else {
|
||||
this.text = text.toCharArray();
|
||||
}
|
||||
|
||||
try {
|
||||
generateCaptcha();
|
||||
} catch (IOException e) {
|
||||
this.text = getRandomChars();
|
||||
try {
|
||||
generateCaptcha();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
generateCaptcha();
|
||||
} catch (IOException e) {
|
||||
this.text = getRandomChars();
|
||||
try {
|
||||
generateCaptcha();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the picture with captcha
|
||||
*
|
||||
* @return {@link BufferedImage}
|
||||
*/
|
||||
public BufferedImage getImagePng() {
|
||||
return imagePng;
|
||||
}
|
||||
/**
|
||||
* Returns the picture with captcha
|
||||
*
|
||||
* @return {@link BufferedImage}
|
||||
*/
|
||||
public BufferedImage getImagePng() {
|
||||
return imagePng;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text value of the captcha
|
||||
*
|
||||
* @return {@link String}
|
||||
*/
|
||||
public String getText() {
|
||||
return String.valueOf(text);
|
||||
}
|
||||
/**
|
||||
* Returns the text value of the captcha
|
||||
*
|
||||
* @return {@link String}
|
||||
*/
|
||||
public String getText() {
|
||||
return String.valueOf(text);
|
||||
}
|
||||
|
||||
//////// //////// //////// //////// //////// //////// //////// ////////
|
||||
//////// //////// //////// //////// //////// //////// //////// ////////
|
||||
|
||||
private char[] getRandomChars() {
|
||||
return getRandomChars(8);
|
||||
}
|
||||
private char[] getRandomChars() {
|
||||
return getRandomChars(8);
|
||||
}
|
||||
|
||||
private char[] getRandomChars(int quantity) {
|
||||
private char[] getRandomChars(int quantity) {
|
||||
|
||||
char[] randomString = new char[quantity];
|
||||
char[] randomString = new char[quantity];
|
||||
|
||||
Random random = new Random();
|
||||
Random random = new Random();
|
||||
|
||||
int capitalLetter;
|
||||
int capitalLetter;
|
||||
|
||||
for (int i = 0; i < quantity; i++) {
|
||||
capitalLetter = 65 + random.nextInt(26);
|
||||
randomString[i] = (char) capitalLetter;
|
||||
}
|
||||
for (int i = 0; i < quantity; i++) {
|
||||
capitalLetter = 65 + random.nextInt(26);
|
||||
randomString[i] = (char) capitalLetter;
|
||||
}
|
||||
|
||||
return randomString;
|
||||
}
|
||||
return randomString;
|
||||
}
|
||||
|
||||
private void generateCaptcha() throws IOException {
|
||||
int charsQuantity = this.text.length;
|
||||
BufferedImage[] images = new BufferedImage[charsQuantity];
|
||||
private void generateCaptcha() throws IOException {
|
||||
int charsQuantity = this.text.length;
|
||||
BufferedImage[] images = new BufferedImage[charsQuantity];
|
||||
|
||||
for (int i = 0; i < charsQuantity; i++) {
|
||||
images[i] = ImageIO.read(SimpleCaptcha.class
|
||||
.getResourceAsStream("/pictures/" + this.text[i] + ".png"));
|
||||
if (i % 2 == 0) {
|
||||
images[i] = rotateImage(images[i], 25);
|
||||
} else {
|
||||
images[i] = rotateImage(images[i], -20);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < charsQuantity; i++) {
|
||||
images[i] = ImageIO.read(SimpleCaptcha.class.getResourceAsStream("/pictures/" + this.text[i] + ".png"));
|
||||
if (i % 2 == 0) {
|
||||
images[i] = rotateImage(images[i], 25);
|
||||
} else {
|
||||
images[i] = rotateImage(images[i], -20);
|
||||
}
|
||||
}
|
||||
|
||||
int imageSize = 30;
|
||||
int rotatedImageSize = (int) Math.sqrt(imageSize * imageSize * 2);
|
||||
int imageSize = 30;
|
||||
int rotatedImageSize = (int) Math.sqrt(imageSize * imageSize * 2);
|
||||
|
||||
BufferedImage captchaImg = new BufferedImage(
|
||||
rotatedImageSize * (charsQuantity - 1) / 10 * 6
|
||||
+ rotatedImageSize,
|
||||
rotatedImageSize, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D graphics2d = captchaImg.createGraphics();
|
||||
graphics2d.setBackground(Color.WHITE);
|
||||
graphics2d.clearRect(0, 0, captchaImg.getWidth(),
|
||||
captchaImg.getHeight());
|
||||
for (int i = 0; i < charsQuantity; i++) {
|
||||
captchaImg.getGraphics().drawImage(images[i],
|
||||
rotatedImageSize * i / 10 * 6, 0, null);
|
||||
}
|
||||
graphics2d.dispose();
|
||||
this.imagePng = captchaImg;
|
||||
}
|
||||
BufferedImage captchaImg = new BufferedImage(rotatedImageSize * (charsQuantity - 1) / 10 * 6 + rotatedImageSize, rotatedImageSize, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D graphics2d = captchaImg.createGraphics();
|
||||
graphics2d.setBackground(Color.WHITE);
|
||||
graphics2d.clearRect(0, 0, captchaImg.getWidth(), captchaImg.getHeight());
|
||||
for (int i = 0; i < charsQuantity; i++) {
|
||||
captchaImg.getGraphics().drawImage(images[i], rotatedImageSize * i / 10 * 6, 0, null);
|
||||
}
|
||||
graphics2d.dispose();
|
||||
this.imagePng = captchaImg;
|
||||
}
|
||||
|
||||
private BufferedImage rotateImage(BufferedImage buffImage, double angle) {
|
||||
private BufferedImage rotateImage(BufferedImage buffImage, double angle) {
|
||||
|
||||
double radian = Math.toRadians(angle);
|
||||
double sin = Math.abs(Math.sin(radian));
|
||||
double cos = Math.abs(Math.cos(radian));
|
||||
double radian = Math.toRadians(angle);
|
||||
double sin = Math.abs(Math.sin(radian));
|
||||
double cos = Math.abs(Math.cos(radian));
|
||||
|
||||
int width = buffImage.getWidth();
|
||||
int height = buffImage.getHeight();
|
||||
int width = buffImage.getWidth();
|
||||
int height = buffImage.getHeight();
|
||||
|
||||
int nWidth = (int) Math
|
||||
.floor((double) width * cos + (double) height * sin);
|
||||
int nHeight = (int) Math
|
||||
.floor((double) height * cos + (double) width * sin);
|
||||
int nWidth = (int) Math.floor((double) width * cos + (double) height * sin);
|
||||
int nHeight = (int) Math.floor((double) height * cos + (double) width * sin);
|
||||
|
||||
BufferedImage rotatedImage = new BufferedImage(nWidth, nHeight,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
BufferedImage rotatedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
Graphics2D graphics = rotatedImage.createGraphics();
|
||||
Graphics2D graphics = rotatedImage.createGraphics();
|
||||
|
||||
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
graphics.translate((nWidth - width) / 2, (nHeight - height) / 2);
|
||||
graphics.rotate(radian, (double) (width / 2), (double) (height / 2));
|
||||
graphics.drawImage(buffImage, 0, 0, null);
|
||||
graphics.dispose();
|
||||
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
graphics.translate((nWidth - width) / 2, (nHeight - height) / 2);
|
||||
graphics.rotate(radian, (double) (width / 2), (double) (height / 2));
|
||||
graphics.drawImage(buffImage, 0, 0,null);
|
||||
graphics.dispose();
|
||||
|
||||
return rotatedImage;
|
||||
}
|
||||
return rotatedImage;
|
||||
}
|
||||
}
|
@ -9,30 +9,30 @@ public class SimpleCaptchaBehavior implements CaptchaBehaviour {
|
||||
SimpleCaptcha simpleCaptcha = new SimpleCaptcha();
|
||||
return simpleCaptcha.getImagePng();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage generateCaptcha(String captchaText) {
|
||||
SimpleCaptcha simpleCaptcha = new SimpleCaptcha(captchaText);
|
||||
return simpleCaptcha.getImagePng();
|
||||
}
|
||||
|
||||
|
||||
public String getRandomChars() {
|
||||
return getRandomChars(8);
|
||||
}
|
||||
|
||||
public String getRandomChars(int quantity) {
|
||||
return getRandomChars(8);
|
||||
}
|
||||
|
||||
public String getRandomChars(int quantity)
|
||||
{
|
||||
char[] randomString = new char[quantity];
|
||||
|
||||
Random random = new Random();
|
||||
Random random = new Random();
|
||||
|
||||
int capitalLetter;
|
||||
int capitalLetter;
|
||||
|
||||
for (int i = 0; i < quantity; i++) {
|
||||
capitalLetter = 65 + random.nextInt(26);
|
||||
randomString[i] = (char) capitalLetter;
|
||||
}
|
||||
for (int i = 0; i < quantity; i++) {
|
||||
capitalLetter = 65 + random.nextInt(26);
|
||||
randomString[i] = (char) capitalLetter;
|
||||
}
|
||||
|
||||
return new String(randomString);
|
||||
return new String(randomString);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,35 +7,34 @@ import lombok.Builder;
|
||||
@Builder
|
||||
public class WebCaptcha {
|
||||
private final CaptchaBehaviour captchaBehaviour;
|
||||
|
||||
|
||||
public BufferedImage generateCaptcha() {
|
||||
return captchaBehaviour.generateCaptcha();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public BufferedImage generateCaptcha(String captchaText) {
|
||||
return captchaBehaviour.generateCaptcha(captchaText);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getRandomChars() {
|
||||
return captchaBehaviour.getRandomChars();
|
||||
}
|
||||
return captchaBehaviour.getRandomChars();
|
||||
}
|
||||
|
||||
public String getRandomChars(int quantity) {
|
||||
return captchaBehaviour.getRandomChars(quantity);
|
||||
}
|
||||
public String getRandomChars(int quantity) {
|
||||
return captchaBehaviour.getRandomChars(quantity);
|
||||
}
|
||||
}
|
||||
|
||||
// WebCaptcha webCaptcha = WebCaptcha.builder().captchaBehaviour(new
|
||||
// SimpleCaptchaBehavior()).build();
|
||||
// webCaptcha.generateCaptcha();
|
||||
// WebCaptcha webCaptcha = WebCaptcha.builder().captchaBehaviour(new SimpleCaptchaBehavior()).build();
|
||||
// webCaptcha.generateCaptcha();
|
||||
//
|
||||
// // @formatter:off
|
||||
// webCaptcha = WebCaptcha.builder()
|
||||
// .captchaBehaviour(
|
||||
// ManualCaptchaBehaviour.builder()
|
||||
// .length(8)
|
||||
// .style("black")
|
||||
// .build()
|
||||
// ).build();
|
||||
//
|
||||
// // @formatter:on
|
||||
// // @formatter:off
|
||||
// webCaptcha = WebCaptcha.builder()
|
||||
// .captchaBehaviour(
|
||||
// ManualCaptchaBehaviour.builder()
|
||||
// .length(8)
|
||||
// .style("black")
|
||||
// .build()
|
||||
// ).build();
|
||||
//
|
||||
// // @formatter:on
|
||||
|
@ -10,13 +10,13 @@ import lombok.Getter;
|
||||
@PropertySource(value = "classpath:git.properties")
|
||||
@Getter
|
||||
public class BuildInfo {
|
||||
private final String buildVersion;
|
||||
private final String branchName;
|
||||
private final String buildVersion;
|
||||
private final String branchName;
|
||||
|
||||
public BuildInfo(@Value("${git.build.version") String buildVersion,
|
||||
@Value("${git.branch") String branchName) {
|
||||
this.buildVersion = buildVersion;
|
||||
this.branchName = branchName;
|
||||
}
|
||||
public BuildInfo(@Value("${git.build.version") String buildVersion,
|
||||
@Value("${git.branch") String branchName) {
|
||||
this.buildVersion = buildVersion;
|
||||
this.branchName = branchName;
|
||||
}
|
||||
|
||||
}
|
@ -6,14 +6,11 @@ import org.ehcache.event.CacheEventListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class CustomCacheEventLogger
|
||||
implements CacheEventListener<Object, Object> {
|
||||
public class CustomCacheEventLogger implements CacheEventListener<Object, Object> {
|
||||
|
||||
@Override
|
||||
public void onEvent(
|
||||
CacheEvent<? extends Object, ? extends Object> cacheEvent) {
|
||||
log.debug("custom Caching event {} key = {} old {} new {} ",
|
||||
cacheEvent.getType(), cacheEvent.getKey(),
|
||||
public void onEvent(CacheEvent<? extends Object, ? extends Object> cacheEvent) {
|
||||
log.debug("custom Caching event {} key = {} old {} new {} ", cacheEvent.getType(), cacheEvent.getKey(),
|
||||
cacheEvent.getOldValue(), cacheEvent.getNewValue());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.ros.chatto.config;
|
||||
|
||||
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
@ -89,8 +89,8 @@ public class ChatMessageController {
|
||||
@PathVariable final String userName,
|
||||
@PathVariable final Instant lastMessageTime,
|
||||
final Principal principal) {
|
||||
final List<ChatMessageDTO> chatMessageDTOs = chatService
|
||||
.getNewMessages(principal.getName(), userName, lastMessageTime);
|
||||
final List<ChatMessageDTO> chatMessageDTOs = chatService.getNewMessages(
|
||||
principal.getName(), userName, lastMessageTime);
|
||||
return chatMessageDTOs;
|
||||
}
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
package org.ros.chatto.controller;
|
||||
|
||||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class CsrfController {
|
||||
|
||||
@RequestMapping("/csrf")
|
||||
public CsrfToken csrf(CsrfToken token) {
|
||||
return token;
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ public class Home {
|
||||
|| r.getAuthority().equals("ROLE_SUPER_USER"));
|
||||
log.trace("Is admin? " + isAdmin);
|
||||
// model.addAttribute("activeUsers",
|
||||
// userService.getOtherActiveUsers(principal.getName()));
|
||||
// userService.getOtherActiveUsers(principal.getName()));
|
||||
return "chat";
|
||||
}
|
||||
}
|
||||
|
@ -14,16 +14,16 @@ import lombok.RequiredArgsConstructor;
|
||||
@RequestMapping("/api/stats")
|
||||
@RequiredArgsConstructor
|
||||
public class StatisticsController {
|
||||
private final StatisticsService statisticsService;
|
||||
private final StatisticsService statisticsService;
|
||||
|
||||
@GetMapping
|
||||
public StatsDTO rootStats() {
|
||||
return StatsDTO.builder()
|
||||
.totalMessages(statisticsService.totalMessage())
|
||||
.totalOnlineUsers(statisticsService.totalUsersOnline())
|
||||
.numMessagesToday(
|
||||
statisticsService.messagesOnDay(Instant.now()))
|
||||
.totalUsers(statisticsService.totalUsers()).build();
|
||||
}
|
||||
@GetMapping
|
||||
public StatsDTO rootStats() {
|
||||
return StatsDTO.builder()
|
||||
.totalMessages(statisticsService.totalMessage())
|
||||
.totalOnlineUsers(statisticsService.totalUsersOnline())
|
||||
.numMessagesToday(
|
||||
statisticsService.messagesOnDay(Instant.now()))
|
||||
.totalUsers(statisticsService.totalUsers()).build();
|
||||
}
|
||||
|
||||
}
|
@ -6,9 +6,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@Controller
|
||||
@RequestMapping("/user")
|
||||
public class UserController {
|
||||
|
||||
@RequestMapping
|
||||
public String viewUserProfile() {
|
||||
return "user/home";
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
public String viewUserProfile() {
|
||||
return "user/home";
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package org.ros.chatto.dto;
|
||||
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
@ -11,10 +12,10 @@ import lombok.Data;
|
||||
public class ChatMessageDTO {
|
||||
@NotBlank(message = "Username should not be blank")
|
||||
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "Username must be alphanumeric")
|
||||
@Size(max = 15)
|
||||
@Size(max=15)
|
||||
private String toUser;
|
||||
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "Username must be alphanumeric")
|
||||
@Size(max = 15)
|
||||
@Size(max=15)
|
||||
private String fromUser;
|
||||
private MessageCipherDTO messageCipher;
|
||||
private Instant messageTime;
|
||||
|
@ -12,9 +12,7 @@ import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MessageCipherDTO {
|
||||
@Pattern(regexp = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$") // regex
|
||||
// for
|
||||
// base64
|
||||
@Pattern(regexp = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$") // regex for base64
|
||||
@NotBlank
|
||||
private String iv;
|
||||
@Max(1)
|
||||
|
@ -16,13 +16,11 @@ public class UserRegistrationDTO {
|
||||
@Transient
|
||||
@Size(min = 4, max = 75, message = "Password must be between 4 and 75 characters")
|
||||
@NotBlank(message = "Password should not be blank")
|
||||
// @Pattern(regexp =
|
||||
// "^.*(?=.{6,})(?=.*d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$", message
|
||||
// = "Invalid password format")
|
||||
// @Pattern(regexp = "^.*(?=.{6,})(?=.*d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$", message = "Invalid password format")
|
||||
private String password;
|
||||
|
||||
|
||||
private Long captchaID;
|
||||
private String captchaText;
|
||||
|
||||
|
||||
private String captchaInput;
|
||||
}
|
||||
|
@ -7,14 +7,15 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ErrorModel {
|
||||
public class ErrorModel{
|
||||
@JsonProperty("field_name")
|
||||
private String fieldName;
|
||||
private String fieldName;
|
||||
@JsonProperty("rejected_value")
|
||||
private Object rejectedValue;
|
||||
private Object rejectedValue;
|
||||
@JsonProperty("error_message")
|
||||
private String messageError;
|
||||
|
||||
private String messageError;
|
||||
|
||||
|
||||
}
|
@ -15,6 +15,6 @@ import lombok.NoArgsConstructor;
|
||||
@AllArgsConstructor
|
||||
public class ErrorResponse {
|
||||
@JsonProperty("errors")
|
||||
private List<ErrorModel> errorMessage;
|
||||
|
||||
private List<ErrorModel> errorMessage;
|
||||
|
||||
}
|
@ -4,6 +4,5 @@ import org.springframework.cache.annotation.CacheEvict;
|
||||
|
||||
public class TokenCacheUtil {
|
||||
@CacheEvict(value = "userTokenCache", key = "#cacheKey")
|
||||
public static void evictSingleTokenValue(String cacheKey) {
|
||||
}
|
||||
public static void evictSingleTokenValue(String cacheKey) {}
|
||||
}
|
||||
|
@ -26,8 +26,7 @@ public class UserLoggingSessionListener implements HttpSessionBindingListener {
|
||||
@Override
|
||||
public void valueBound(HttpSessionBindingEvent event) {
|
||||
|
||||
UserLoggingSessionListener user = (UserLoggingSessionListener) event
|
||||
.getValue();
|
||||
UserLoggingSessionListener user = (UserLoggingSessionListener) event.getValue();
|
||||
|
||||
log.debug("Incrementing session count for user {}", user.getUsername());
|
||||
|
||||
@ -39,8 +38,7 @@ public class UserLoggingSessionListener implements HttpSessionBindingListener {
|
||||
|
||||
@Override
|
||||
public void valueUnbound(HttpSessionBindingEvent event) {
|
||||
UserLoggingSessionListener user = (UserLoggingSessionListener) event
|
||||
.getValue();
|
||||
UserLoggingSessionListener user = (UserLoggingSessionListener) event.getValue();
|
||||
|
||||
log.debug("Decrementing session count for user {}", user.getUsername());
|
||||
|
||||
|
@ -11,19 +11,17 @@ import org.springframework.security.web.authentication.AuthenticationSuccessHand
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("myAuthenticationSuccessHandler")
|
||||
public class UserSessionLoggingLoginSuccessHandler
|
||||
implements AuthenticationSuccessHandler {
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request,
|
||||
HttpServletResponse response, Authentication authentication)
|
||||
throws IOException {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
UserLoggingSessionListener user = new UserLoggingSessionListener(
|
||||
authentication.getName());
|
||||
session.setAttribute("user", user);
|
||||
}
|
||||
response.sendRedirect("/chat");
|
||||
}
|
||||
public class UserSessionLoggingLoginSuccessHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request,
|
||||
HttpServletResponse response, Authentication authentication)
|
||||
throws IOException {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
UserLoggingSessionListener user = new UserLoggingSessionListener(authentication.getName());
|
||||
session.setAttribute("user", user);
|
||||
}
|
||||
response.sendRedirect("/chat");
|
||||
}
|
||||
}
|
@ -12,16 +12,15 @@ import org.springframework.security.web.authentication.logout.LogoutSuccessHandl
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("myLogoutSuccessHandler")
|
||||
public class UserSessionLoggingLogoutSuccessHandler
|
||||
implements LogoutSuccessHandler {
|
||||
@Override
|
||||
public void onLogoutSuccess(HttpServletRequest request,
|
||||
HttpServletResponse response, Authentication authentication)
|
||||
throws IOException, ServletException {
|
||||
HttpSession session = request.getSession();
|
||||
if (session != null) {
|
||||
session.removeAttribute("user");
|
||||
}
|
||||
response.sendRedirect("/login?logout");
|
||||
}
|
||||
public class UserSessionLoggingLogoutSuccessHandler implements LogoutSuccessHandler{
|
||||
@Override
|
||||
public void onLogoutSuccess(HttpServletRequest request,
|
||||
HttpServletResponse response, Authentication authentication)
|
||||
throws IOException, ServletException {
|
||||
HttpSession session = request.getSession();
|
||||
if (session != null){
|
||||
session.removeAttribute("user");
|
||||
}
|
||||
response.sendRedirect("/login?logout");
|
||||
}
|
||||
}
|
@ -24,18 +24,18 @@ public class ChatMessage {
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "m_id")
|
||||
private Long messageID;
|
||||
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "from_user")
|
||||
private ChatUser fromUser;
|
||||
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "to_user")
|
||||
private ChatUser toUser;
|
||||
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "message")
|
||||
private MessageCipher messageCipher;
|
||||
|
||||
private Instant messageTime;
|
||||
|
||||
private Instant messageTime;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ this is what the json will look like*/
|
||||
@Entity
|
||||
@Table(name = "message_ciphers")
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
// @JsonIgnoreProperties(value = { "id"}, allowGetters = false)
|
||||
//@JsonIgnoreProperties(value = { "id"}, allowGetters = false)
|
||||
public class MessageCipher {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
@ -16,6 +16,7 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
|
||||
@Entity
|
||||
@Table(name = "roles")
|
||||
@Data
|
||||
@ -23,14 +24,14 @@ public class Role {
|
||||
@Id
|
||||
@Column(name = "role_id")
|
||||
private int roleID;
|
||||
|
||||
|
||||
@Column(name = "role_name")
|
||||
private String name;
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
@OneToMany(mappedBy = "role", cascade = { CascadeType.PERSIST,
|
||||
CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
|
||||
|
||||
@OneToMany(mappedBy = "role", cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH,
|
||||
CascadeType.REFRESH })
|
||||
@JsonBackReference
|
||||
@ToString.Exclude
|
||||
@EqualsAndHashCode.Exclude
|
||||
|
@ -20,13 +20,13 @@ public class UserSession {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private int id;
|
||||
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id")
|
||||
private ChatUser user;
|
||||
|
||||
|
||||
private boolean online;
|
||||
|
||||
|
||||
private int numSessions;
|
||||
private Instant timeStamp;
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
// package org.ros.chatto.repository;
|
||||
//package org.ros.chatto.repository;
|
||||
//
|
||||
// import org.springframework.data.jpa.repository.JpaRepository;
|
||||
// import org.springframework.stereotype.Repository;
|
||||
//import org.springframework.data.jpa.repository.JpaRepository;
|
||||
//import org.springframework.stereotype.Repository;
|
||||
//
|
||||
// @Repository
|
||||
// public interface DBInitializerRepostory extends JpaRepository<Integer,
|
||||
// Integer>{
|
||||
//
|
||||
// }
|
||||
//@Repository
|
||||
//public interface DBInitializerRepostory extends JpaRepository<Integer, Integer>{
|
||||
//
|
||||
//}
|
||||
|
@ -5,7 +5,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface MessageCipherRepository
|
||||
extends JpaRepository<MessageCipher, Long> {
|
||||
public interface MessageCipherRepository extends JpaRepository<MessageCipher, Long>{
|
||||
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface RoleRepository extends JpaRepository<Role, Long> {
|
||||
public interface RoleRepository extends JpaRepository<Role, Long>{
|
||||
@Query("select r from Role r where r.name = ?1")
|
||||
public Role findByName(String roleName);
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ import java.util.List;
|
||||
|
||||
public interface UserRepositoryCustom {
|
||||
|
||||
// @Query("select s from Article s where s.author like ?1 and s.title = ?2")
|
||||
// List<Article> findByAuthorAndTitle(String author, String title);
|
||||
// @Query("select u from ChatUser u")
|
||||
// @Query("select s from Article s where s.author like ?1 and s.title = ?2")
|
||||
// List<Article> findByAuthorAndTitle(String author, String title);
|
||||
// @Query("select u from ChatUser u")
|
||||
public List<String> getAllUserNames(String s);
|
||||
}
|
||||
|
@ -14,34 +14,30 @@ import org.ros.chatto.model.ChatUser;
|
||||
import org.ros.chatto.repository.UserRepositoryCustom;
|
||||
|
||||
@Service
|
||||
class UserRepositoryCustomImpl implements UserRepositoryCustom {
|
||||
class UserRepositoryCustomImpl implements UserRepositoryCustom{
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Override
|
||||
public List<String> getAllUserNames(String userName) {
|
||||
List<String> userNamesList = null;
|
||||
// Session session = null;
|
||||
// Session session = null;
|
||||
try {
|
||||
CriteriaBuilder criteriaBuilder = entityManager
|
||||
.getCriteriaBuilder();
|
||||
CriteriaQuery<String> criteriaQuery = criteriaBuilder
|
||||
.createQuery(String.class);
|
||||
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<String> criteriaQuery = criteriaBuilder.createQuery(String.class);
|
||||
Root<ChatUser> root = criteriaQuery.from(ChatUser.class);
|
||||
criteriaQuery.select(root.get("userName"));
|
||||
criteriaQuery.where(
|
||||
criteriaBuilder.notEqual(root.get("userName"), userName));
|
||||
|
||||
userNamesList = entityManager.createQuery(criteriaQuery)
|
||||
.getResultList();
|
||||
// for(String un: userNamesList)
|
||||
// {
|
||||
// System.out.println(un);
|
||||
// }
|
||||
criteriaQuery.where(criteriaBuilder.notEqual(root.get("userName"), userName));
|
||||
|
||||
userNamesList = entityManager.createQuery(criteriaQuery).getResultList();
|
||||
// for(String un: userNamesList)
|
||||
// {
|
||||
// System.out.println(un);
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return userNamesList;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -16,29 +16,20 @@ import org.springframework.security.web.authentication.AuthenticationSuccessHand
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class AuthenticationSuccessHandlerImpl
|
||||
implements AuthenticationSuccessHandler {
|
||||
|
||||
private static final SimpleGrantedAuthority SUPER_USER_AUTHORITY = new SimpleGrantedAuthority(
|
||||
"ROLE_SUPER_USER");
|
||||
private static final SimpleGrantedAuthority ADMIN_AUTHORITY = new SimpleGrantedAuthority(
|
||||
"ROLE_ADMIN");
|
||||
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
||||
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
|
||||
|
||||
private static final SimpleGrantedAuthority SUPER_USER_AUTHORITY = new SimpleGrantedAuthority("ROLE_SUPER_USER");
|
||||
private static final SimpleGrantedAuthority ADMIN_AUTHORITY = new SimpleGrantedAuthority("ROLE_ADMIN");
|
||||
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
|
||||
HttpServletResponse httpServletResponse,
|
||||
Authentication authentication)
|
||||
throws IOException, ServletException {
|
||||
Collection<? extends GrantedAuthority> authorities = authentication
|
||||
.getAuthorities();
|
||||
if (authorities.contains(ADMIN_AUTHORITY)
|
||||
|| authorities.contains(SUPER_USER_AUTHORITY)) {
|
||||
redirectStrategy.sendRedirect(httpServletRequest,
|
||||
httpServletResponse, "/admin");
|
||||
} else {
|
||||
redirectStrategy.sendRedirect(httpServletRequest,
|
||||
httpServletResponse, "/user");
|
||||
}
|
||||
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
|
||||
Authentication authentication) throws IOException, ServletException {
|
||||
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||
if (authorities.contains(ADMIN_AUTHORITY) || authorities.contains(SUPER_USER_AUTHORITY)) {
|
||||
redirectStrategy.sendRedirect(httpServletRequest, httpServletResponse, "/admin");
|
||||
} else {
|
||||
redirectStrategy.sendRedirect(httpServletRequest, httpServletResponse, "/user");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,17 +9,18 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class CaptchaService {
|
||||
private final WebCaptcha webCaptcha;
|
||||
|
||||
|
||||
public CaptchaService() {
|
||||
webCaptcha = WebCaptcha.builder()
|
||||
.captchaBehaviour(new SimpleCaptchaBehavior()).build();
|
||||
webCaptcha = WebCaptcha.builder().captchaBehaviour(new SimpleCaptchaBehavior()).build();
|
||||
}
|
||||
|
||||
public BufferedImage createCaptchaImage(final String captchaText) {
|
||||
|
||||
public BufferedImage createCaptchaImage(final String captchaText)
|
||||
{
|
||||
return webCaptcha.generateCaptcha(captchaText);
|
||||
}
|
||||
|
||||
public String getRandomText() {
|
||||
|
||||
public String getRandomText()
|
||||
{
|
||||
return webCaptcha.getRandomChars();
|
||||
}
|
||||
}
|
||||
|
@ -46,15 +46,13 @@ public class DBInitializerService {
|
||||
});
|
||||
}
|
||||
|
||||
private void resetAllUserSessions(final Connection connection)
|
||||
throws SQLException {
|
||||
private void resetAllUserSessions(final Connection connection) throws SQLException {
|
||||
final PreparedStatement preparedStatement = connection
|
||||
.prepareStatement(dbInitializerConfig.getResetSessionsQuery());
|
||||
preparedStatement.executeUpdate();
|
||||
}
|
||||
|
||||
private void clearAllTokens(final Connection connection)
|
||||
throws SQLException {
|
||||
private void clearAllTokens(final Connection connection) throws SQLException {
|
||||
final PreparedStatement preparedStatement = connection
|
||||
.prepareStatement(dbInitializerConfig.getClearTokensQuery());
|
||||
preparedStatement.executeUpdate();
|
||||
|
12
src/main/java/org/ros/chatto/websocket/Message.java
Normal file
12
src/main/java/org/ros/chatto/websocket/Message.java
Normal file
@ -0,0 +1,12 @@
|
||||
package org.ros.chatto.websocket;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class Message {
|
||||
private String from;
|
||||
private String to;
|
||||
private String text;
|
||||
}
|
26
src/main/java/org/ros/chatto/websocket/WebSocketConfig.java
Normal file
26
src/main/java/org/ros/chatto/websocket/WebSocketConfig.java
Normal file
@ -0,0 +1,26 @@
|
||||
package org.ros.chatto.websocket;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry config) {
|
||||
config.enableSimpleBroker("/topic", "/queue");
|
||||
config.setApplicationDestinationPrefixes("/app");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
registry.addEndpoint("/ping");
|
||||
registry.addEndpoint("/ping").withSockJS();
|
||||
registry.addEndpoint("/chat");
|
||||
registry.addEndpoint("/chat").withSockJS();
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package org.ros.chatto.websocket;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.ros.chatto.dto.ChatMessageDTO;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class WebSocketController {
|
||||
|
||||
private final SimpMessagingTemplate smt;
|
||||
|
||||
@MessageMapping("/ping")
|
||||
public void send2(Principal principal) throws Exception {
|
||||
smt.convertAndSendToUser(principal.getName(), "/queue/ping", "pong");
|
||||
}
|
||||
|
||||
@MessageMapping("/chat")
|
||||
public void send(@Valid @Payload final ChatMessageDTO message)
|
||||
throws Exception {
|
||||
smt.convertAndSendToUser(message.getFromUser(), "/queue/reply",
|
||||
message);
|
||||
smt.convertAndSendToUser(message.getToUser(), "/queue/reply", message);
|
||||
}
|
||||
|
||||
@GetMapping("/ws")
|
||||
public String wsPage() {
|
||||
return "ws";
|
||||
}
|
||||
}
|
@ -57,6 +57,13 @@
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/loglevel/1.6.4/loglevel.min.js"
|
||||
integrity="sha256-ACTlnmNCkOooSKkPCKYbiex8WLE82aeiN+Z9ElZag5Q=" crossorigin="anonymous"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.5.0/sockjs.min.js"
|
||||
integrity="sha512-5yJ548VSnLflcRxWNqVWYeQZnby8D8fJTmYRLyvs445j1XmzR8cnWi85lcHx3CUEeAX+GrK3TqTfzOO6LKDpdw=="
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"
|
||||
integrity="sha512-iKDtgDyTHjAitUDdLljGhenhPwrbBfqTKWO1mkhSFH3A7blITC9MhYon6SjnMhp4o0rADGw9yAC6EW4t5a4K3g=="
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<link th:href="@{/css/master.css}" href="../../resources/static/css/master.css" rel="stylesheet">
|
||||
<link th:href="@{/css/colors.css}" href="../../resources/static/css/colors.css" rel="stylesheet">
|
||||
|
||||
|
83
src/main/resources/templates/ws.html
Normal file
83
src/main/resources/templates/ws.html
Normal file
@ -0,0 +1,83 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Chat WebSocket</title>
|
||||
<script type="text/javascript">
|
||||
var stompClient = null;
|
||||
|
||||
function setConnected(connected) {
|
||||
document.getElementById('connect').disabled = connected;
|
||||
document.getElementById('disconnect').disabled = !connected;
|
||||
document.getElementById('conversationDiv').style.visibility
|
||||
= connected ? 'visible' : 'hidden';
|
||||
document.getElementById('response').innerHTML = '';
|
||||
}
|
||||
|
||||
function connect() {
|
||||
var socket = new SockJS('/chat2');
|
||||
stompClient = Stomp.over(socket);
|
||||
stompClient.connect({}, function (frame) {
|
||||
setConnected(true);
|
||||
console.log('Connected: ' + frame);
|
||||
// stompClient.subscribe('/topic/messages', function (messageOutput) {
|
||||
// showMessageOutput(JSON.parse(messageOutput.body));
|
||||
// });
|
||||
stompClient.subscribe('/user/queue/reply', function (messageOutput) {
|
||||
showMessageOutput(JSON.parse(messageOutput.body));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (stompClient != null) {
|
||||
stompClient.disconnect();
|
||||
}
|
||||
setConnected(false);
|
||||
console.log("Disconnected");
|
||||
}
|
||||
|
||||
function sendMessage() {
|
||||
var from = document.getElementById('from').value;
|
||||
var to = document.getElementById('to').value;
|
||||
var text = document.getElementById('text').value;
|
||||
stompClient.send("/app/chat", {},
|
||||
JSON.stringify({ 'from': from, 'text': text, to: to }));
|
||||
}
|
||||
|
||||
function showMessageOutput(messageOutput) {
|
||||
var response = document.getElementById('response');
|
||||
var p = document.createElement('p');
|
||||
p.style.wordWrap = 'break-word';
|
||||
p.appendChild(document.createTextNode(messageOutput.from + ": "
|
||||
+ messageOutput.text + " (" + messageOutput.time + ")"));
|
||||
response.appendChild(p);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="disconnect()">
|
||||
<div>
|
||||
<div>
|
||||
<input type="text" id="from" placeholder="Choose a nickname" />
|
||||
</div>
|
||||
<div>
|
||||
<input type="text" id="to" placeholder="Send to User" />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button id="connect" onclick="connect();">Connect</button>
|
||||
<button id="disconnect" disabled="disabled" onclick="disconnect();">
|
||||
Disconnect
|
||||
</button>
|
||||
</div>
|
||||
<br />
|
||||
<div id="conversationDiv">
|
||||
<input type="text" id="text" placeholder="Write a message..." />
|
||||
<button id="sendMessage" onclick="sendMessage();">Send</button>
|
||||
<p id="response"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -15,68 +15,65 @@
|
||||
// @RunWith(SpringRunner.class)
|
||||
// @SpringBootTest
|
||||
// public class ChattoApplicationTests {
|
||||
|
||||
// // @Autowired
|
||||
// // ChatMessageRepository chatMessageRepository;
|
||||
|
||||
// // @Autowired
|
||||
// // ChatMessageRepository chatMessageRepository;
|
||||
// //
|
||||
// // @Mock
|
||||
// // ChatMessageRepository mockChatMessageRepository;
|
||||
// //
|
||||
// @Autowired
|
||||
// private UserRepository userRepository;
|
||||
|
||||
// @Autowired
|
||||
// private UserRoleRepository userRoleRepository;
|
||||
|
||||
// private final Logger logger = LoggerFactory.getLogger(ChattoApplicationTests.class);
|
||||
// //
|
||||
// // @Mock
|
||||
// // ChatMessageRepository mockChatMessageRepository;
|
||||
// @Test
|
||||
// public void contextLoads() {
|
||||
// }
|
||||
// //
|
||||
// @Autowired
|
||||
// private UserRepository userRepository;
|
||||
|
||||
// @Autowired
|
||||
// private UserRoleRepository userRoleRepository;
|
||||
|
||||
// private final Logger logger =
|
||||
// LoggerFactory.getLogger(ChattoApplicationTests.class);
|
||||
// //
|
||||
// @Test
|
||||
// public void contextLoads() {
|
||||
// }
|
||||
// //
|
||||
// // @Test
|
||||
// // public void testMessageRepo() {
|
||||
// // chatMessageRepository.findAll().toString();
|
||||
// // }
|
||||
|
||||
// @Test
|
||||
// public void testRoleRepo() {
|
||||
// List<String> list = userRoleRepository.getAllRegularUser();
|
||||
// logger.info("List = {} ", list);
|
||||
// }
|
||||
|
||||
// @Test
|
||||
// public void findAllOtherUsers() {
|
||||
// logger.info("Usernames =
|
||||
// {}",userRepository.findAllOtherUserNames("hmm").toString());
|
||||
// }
|
||||
|
||||
// // @Test
|
||||
// // public void testSave() {
|
||||
// // ChatUser fromUser = new ChatUser();
|
||||
// // fromUser = userRepository.findByUserName("hmm");
|
||||
// // ChatUser toUser = new ChatUser();
|
||||
// // toUser = userRepository.findByUserName("user2");
|
||||
// // ChatMessage chatMessage = new ChatMessage();
|
||||
// // chatMessage.setMessage("Hello!");
|
||||
// // chatMessage.setFromUser(fromUser);
|
||||
// // chatMessage.setToUser(toUser);
|
||||
// //
|
||||
// // chatMessageRepository.save(chatMessage);
|
||||
// // }
|
||||
|
||||
// /*
|
||||
// * @Test public void testSave() { ChatUser fromUser = new ChatUser(); fromUser
|
||||
// =
|
||||
// * userRepository.findByUserName("hmm"); ChatUser toUser = new ChatUser();
|
||||
// * toUser = userRepository.findByUserName("user2"); ChatMessage chatMessage =
|
||||
// * new ChatMessage(); chatMessage.setMessage("Hello!");
|
||||
// * chatMessage.setFromUser(fromUser); chatMessage.setToUser(toUser);
|
||||
// *
|
||||
// * // chatMessageRepository.save(chatMessage);
|
||||
// * when(mockChatMessageRepository.save(any(ChatMessage.class))).thenReturn(
|
||||
// * chatMessage); verify(mockChatMessageRepository,
|
||||
// * times(1)).save(Mockito.any(ChatMessage.class)); }
|
||||
// */
|
||||
// // @Test
|
||||
// // public void testMessageRepo() {
|
||||
// // chatMessageRepository.findAll().toString();
|
||||
// // }
|
||||
|
||||
// @Test
|
||||
// public void testRoleRepo() {
|
||||
// List<String> list = userRoleRepository.getAllRegularUser();
|
||||
// logger.info("List = {} ", list);
|
||||
// }
|
||||
|
||||
// @Test
|
||||
// public void findAllOtherUsers() {
|
||||
// logger.info("Usernames = {}",userRepository.findAllOtherUserNames("hmm").toString());
|
||||
// }
|
||||
|
||||
// // @Test
|
||||
// // public void testSave() {
|
||||
// // ChatUser fromUser = new ChatUser();
|
||||
// // fromUser = userRepository.findByUserName("hmm");
|
||||
// // ChatUser toUser = new ChatUser();
|
||||
// // toUser = userRepository.findByUserName("user2");
|
||||
// // ChatMessage chatMessage = new ChatMessage();
|
||||
// // chatMessage.setMessage("Hello!");
|
||||
// // chatMessage.setFromUser(fromUser);
|
||||
// // chatMessage.setToUser(toUser);
|
||||
// //
|
||||
// // chatMessageRepository.save(chatMessage);
|
||||
// // }
|
||||
|
||||
// /*
|
||||
// * @Test public void testSave() { ChatUser fromUser = new ChatUser(); fromUser =
|
||||
// * userRepository.findByUserName("hmm"); ChatUser toUser = new ChatUser();
|
||||
// * toUser = userRepository.findByUserName("user2"); ChatMessage chatMessage =
|
||||
// * new ChatMessage(); chatMessage.setMessage("Hello!");
|
||||
// * chatMessage.setFromUser(fromUser); chatMessage.setToUser(toUser);
|
||||
// *
|
||||
// * // chatMessageRepository.save(chatMessage);
|
||||
// * when(mockChatMessageRepository.save(any(ChatMessage.class))).thenReturn(
|
||||
// * chatMessage); verify(mockChatMessageRepository,
|
||||
// * times(1)).save(Mockito.any(ChatMessage.class)); }
|
||||
// */
|
||||
// }
|
||||
|
@ -32,62 +32,61 @@
|
||||
// @RunWith(MockitoJUnitRunner.class)
|
||||
// @Slf4j
|
||||
// public class UnitTest {
|
||||
// @InjectMocks
|
||||
// @InjectMocks
|
||||
|
||||
// // private RoleService roleService;
|
||||
// // private UserTokenService userTokenService;
|
||||
// // private RoleService roleService;
|
||||
// // private UserTokenService userTokenService;
|
||||
|
||||
// @Mock
|
||||
// private UserRoleRepository userRoleRepository;
|
||||
// @Mock
|
||||
// private UserRoleRepository userRoleRepository;
|
||||
|
||||
// @Mock
|
||||
// private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
// @Mock
|
||||
// private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
|
||||
// @Mock
|
||||
// private RoleRepository roleRepository;
|
||||
// @Mock
|
||||
// private RoleRepository roleRepository;
|
||||
|
||||
// @Mock
|
||||
// private UserRepositoryCustom userRepositoryCustom;
|
||||
// @Mock
|
||||
// private UserRepositoryCustom userRepositoryCustom;
|
||||
|
||||
// @Mock
|
||||
// private UserSessionRepository userSessionRepository;
|
||||
// @Mock
|
||||
// private UserSessionRepository userSessionRepository;
|
||||
|
||||
// @Mock
|
||||
// private UserRepository userRepository;
|
||||
// @Mock
|
||||
// private UserRepository userRepository;
|
||||
|
||||
// private UserService userService = new UserServiceImpl(userRepository,
|
||||
// userRoleRepository, passwordEncoder, roleRepository,
|
||||
// userSessionRepository);
|
||||
// // private ChatUser chatUser;
|
||||
// private UserService userService = new UserServiceImpl(userRepository, userRoleRepository, passwordEncoder, roleRepository,
|
||||
// userSessionRepository);
|
||||
// // private ChatUser chatUser;
|
||||
|
||||
// @Before
|
||||
// public void setupMock() {
|
||||
// // userRepository = mock(UserRepository.class);
|
||||
// // chatUser = mock(ChatUser.class);
|
||||
// }
|
||||
// @Before
|
||||
// public void setupMock() {
|
||||
// // userRepository = mock(UserRepository.class);
|
||||
// // chatUser = mock(ChatUser.class);
|
||||
// }
|
||||
|
||||
// @Test
|
||||
// public void userRegistrationTest() {
|
||||
// UserRegistrationDTO userRegistrationDTO = new UserRegistrationDTO();
|
||||
// userRegistrationDTO.setUserName("mickey");
|
||||
// userRegistrationDTO.setPassword("mouse");
|
||||
// @Test
|
||||
// public void userRegistrationTest() {
|
||||
// UserRegistrationDTO userRegistrationDTO = new UserRegistrationDTO();
|
||||
// userRegistrationDTO.setUserName("mickey");
|
||||
// userRegistrationDTO.setPassword("mouse");
|
||||
|
||||
// ChatUser chatUser = new ChatUser();
|
||||
// chatUser.setUserName("mickey");
|
||||
// // chatUser.setPassword("mouse");
|
||||
// ChatUser chatUser = new ChatUser();
|
||||
// chatUser.setUserName("mickey");
|
||||
// // chatUser.setPassword("mouse");
|
||||
|
||||
// Role role = new Role();
|
||||
// role.setRoleID(2);
|
||||
// role.setName("USER");
|
||||
// when(roleRepository.findByName("USER")).thenReturn(role);
|
||||
// when(userRepository.save(chatUser)).thenReturn(chatUser);
|
||||
// Role role = new Role();
|
||||
// role.setRoleID(2);
|
||||
// role.setName("USER");
|
||||
// when(roleRepository.findByName("USER")).thenReturn(role);
|
||||
// when(userRepository.save(chatUser)).thenReturn(chatUser);
|
||||
|
||||
// UserRole userRole = userService.registerUser(userRegistrationDTO);
|
||||
// assertArrayEquals(new Object[] { 2, "USER","mickey" },
|
||||
// new Object[] { userRole.getRole().getRoleID(),
|
||||
// userRole.getRole().getName(), userRole.getUser().getUserName() });
|
||||
// verify(userRepository, times(1)).save(chatUser);
|
||||
// verify(userRoleRepository,times(1)).save(userRole);
|
||||
// }
|
||||
// UserRole userRole = userService.registerUser(userRegistrationDTO);
|
||||
// assertArrayEquals(new Object[] { 2, "USER","mickey" },
|
||||
// new Object[] { userRole.getRole().getRoleID(),
|
||||
// userRole.getRole().getName(), userRole.getUser().getUserName() });
|
||||
// verify(userRepository, times(1)).save(chatUser);
|
||||
// verify(userRoleRepository,times(1)).save(userRole);
|
||||
// }
|
||||
|
||||
// }
|
||||
|
Loading…
Reference in New Issue
Block a user