Skip to content

Commit f274f16

Browse files
committed
Change login page, Save Keycloak configuration in resources, refactor Robot page
1 parent 2c1755b commit f274f16

File tree

14 files changed

+746
-95
lines changed

14 files changed

+746
-95
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* Cerberus Copyright (C) 2013 - 2025 cerberustesting
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This file is part of Cerberus.
6+
*
7+
* Cerberus is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* Cerberus is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with Cerberus. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
package org.cerberus.core.apiprivate;
21+
22+
import com.google.gson.Gson;
23+
import io.swagger.v3.oas.annotations.Operation;
24+
import org.apache.logging.log4j.LogManager;
25+
import org.apache.logging.log4j.Logger;
26+
import org.cerberus.core.api.dto.ai.LogAIUsageMonthlyStatsDTOV001;
27+
import org.cerberus.core.api.dto.ai.LogAIUsageStatsDTOV001;
28+
import org.cerberus.core.crud.entity.Robot;
29+
import org.cerberus.core.crud.entity.UserPrompt;
30+
import org.cerberus.core.crud.entity.stats.UserPromptStats;
31+
import org.cerberus.core.crud.service.impl.RobotService;
32+
import org.cerberus.core.crud.service.impl.UserPromptService;
33+
import org.cerberus.core.engine.entity.MessageEvent;
34+
import org.cerberus.core.enums.MessageEventEnum;
35+
import org.cerberus.core.util.answer.AnswerItem;
36+
import org.cerberus.core.util.answer.AnswerList;
37+
import org.cerberus.core.util.datatable.DataTableInformation;
38+
import org.cerberus.core.util.servlet.ServletUtil;
39+
import org.json.JSONArray;
40+
import org.json.JSONException;
41+
import org.json.JSONObject;
42+
import org.owasp.html.PolicyFactory;
43+
import org.owasp.html.Sanitizers;
44+
import org.springframework.beans.factory.annotation.Autowired;
45+
import org.springframework.format.annotation.DateTimeFormat;
46+
import org.springframework.http.HttpStatus;
47+
import org.springframework.http.ResponseEntity;
48+
import org.springframework.web.bind.annotation.*;
49+
50+
import javax.servlet.http.HttpServletRequest;
51+
import java.sql.Timestamp;
52+
import java.time.LocalDate;
53+
import java.time.LocalDateTime;
54+
import java.util.List;
55+
56+
/**
57+
* @author bcivel
58+
*/
59+
@RestController
60+
@RequestMapping("/robots")
61+
public class RobotPrivateController {
62+
63+
private static final Logger LOG = LogManager.getLogger(RobotPrivateController.class);
64+
private final PolicyFactory policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
65+
66+
@Autowired
67+
RobotService robotService;
68+
69+
@Operation(hidden=true)
70+
@PostMapping("/read")
71+
public String read(HttpServletRequest request) {
72+
73+
// Calling Servlet Transversal Util.
74+
ServletUtil.servletStart(request);
75+
boolean userHasPermissions = request.isUserInRole("Integrator");
76+
77+
JSONObject object = new JSONObject();
78+
try {
79+
80+
AnswerItem<JSONObject> answer = new AnswerItem<>(new MessageEvent(MessageEventEnum.DATA_OPERATION_ERROR_UNEXPECTED));
81+
AnswerList<Robot> robotList = new AnswerList<>();
82+
83+
DataTableInformation dti = new DataTableInformation(request, "robot,type,platform,browser,version,isActive,userAgent,screenSize,robotDecli,lbexemethod,description");
84+
robotList = robotService.readByCriteria(true, true, dti.getStartPosition(), dti.getLength(), dti.getColumnName(), dti.getSort(), dti.getSearchParameter(), dti.getIndividualSearch());
85+
86+
JSONArray jsonArray = new JSONArray();
87+
if (robotList.isCodeEquals(MessageEventEnum.DATA_OPERATION_OK.getCode())) {//the service was able to perform the query, then we should get all values
88+
for (Robot robot : robotList.getDataList()) {
89+
Gson gson = new Gson();
90+
jsonArray.put(new JSONObject(gson.toJson(robot)).put("hasPermissions", userHasPermissions));
91+
}
92+
}
93+
94+
object.put("contentTable", jsonArray);
95+
object.put("hasPermissions", userHasPermissions);
96+
object.put("iTotalRecords", robotList.getTotalRows());
97+
object.put("iTotalDisplayRecords", robotList.getTotalRows());
98+
object.put("messageType", "OK");
99+
100+
} catch (JSONException ex) {
101+
LOG.warn(ex);
102+
}
103+
return object.toString();
104+
}
105+
106+
107+
/**
108+
* Read Distinct Value Of Column
109+
*
110+
* @param request
111+
* @return
112+
*/
113+
@Operation(hidden=true)
114+
@GetMapping("readDistinctValueOfColumn")
115+
public String readDistinctValueOfColumn(HttpServletRequest request,
116+
@RequestParam("columnName") String columnName) {
117+
118+
boolean userHasPermissions = request.isUserInRole("Integrator");
119+
120+
JSONObject object = new JSONObject();
121+
try {
122+
DataTableInformation dti = new DataTableInformation(request, "robot,type,platform,browser,version,isActive,userAgent,screenSize,robotDecli,lbexemethod,description");
123+
AnswerList robotList = robotService.readDistinctValuesByCriteria(dti.getSearchParameter(), dti.getIndividualSearch(), columnName);
124+
object.put("distinctValues", robotList.getDataList());
125+
126+
} catch (JSONException ex) {
127+
LOG.warn(ex);
128+
}
129+
return object.toString();
130+
}
131+
132+
133+
}

source/src/main/java/org/cerberus/core/crud/service/impl/RobotService.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,16 @@ public HashMap<String, Robot> readToHashMapByRobotList(List<String> robotList) t
118118
@Override
119119
public AnswerList<Robot> readByCriteria(boolean withCapabilities, boolean withExecutors, int startPosition, int length, String columnName, String sort,
120120
String searchParameter, Map<String, List<String>> individualSearch) {
121+
AnswerList<Robot> resultRobotList;
122+
resultRobotList = robotDao.readByCriteria(startPosition, length, columnName, sort, searchParameter, individualSearch);
121123
if (withCapabilities) {
122-
return fillCapabilities(robotDao.readByCriteria(startPosition, length, columnName, sort, searchParameter, individualSearch));
123-
} else {
124-
return robotDao.readByCriteria(startPosition, length, columnName, sort, searchParameter, individualSearch);
124+
resultRobotList = fillCapabilities(resultRobotList);
125125
}
126+
if (withExecutors) {
127+
resultRobotList = fillExecutors(resultRobotList);
128+
}
129+
130+
return resultRobotList;
126131
}
127132

128133
@Override
@@ -243,6 +248,20 @@ private Robot fillExecutors(Robot robotItem) throws CerberusException {
243248
return robotItem;
244249
}
245250

251+
private AnswerList<Robot> fillExecutors(AnswerList<Robot> robotList) {
252+
try {
253+
List<Robot> robots = convert(robotList);
254+
if (robots != null) {
255+
for (Robot robot : robots) {
256+
robot.setExecutors(robotExecutorService.convert(robotExecutorService.readByRobot(robot.getRobot())));
257+
}
258+
}
259+
} catch (CerberusException e) {
260+
LOGGER.warn("Unable to fill robot executors due to " + e.getMessage());
261+
}
262+
return robotList;
263+
}
264+
246265
private AnswerItem<Robot> fillCapabilities(AnswerItem<Robot> robotItem) {
247266
try {
248267
Robot robot = convert(robotItem);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Structure on keycloak > theme
2+
3+
cerberus/
4+
└─ login/
5+
├─ theme.properties
6+
├─ login.ftl
7+
└─ resources/
8+
├─ css/ > Put crb_style.css
9+
├─ js/ > Put tsparticles.min.js
10+
└─ img/ > Put your Logo
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<!DOCTYPE html>
2+
<html class="dark" lang="${realm.locale!'en'}">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>${msg("loginTitle", realm.name)}</title>
6+
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8+
9+
<!-- Keycloak required -->
10+
<link rel="icon" href="${url.resourcesPath}/img/favicon.ico">
11+
12+
<!-- Your custom CSS -->
13+
<link rel="stylesheet" href="${url.resourcesPath}/css/styles.css">
14+
15+
<!-- tsParticles -->
16+
<script src="${url.resourcesPath}/js/tsparticles.min.js"></script>
17+
</head>
18+
<body class="crb_body relative min-h-screen">
19+
20+
<!-- tsParticles container -->
21+
<div id="tsparticles" class="absolute inset-0 z-0 pointer-events-none"></div>
22+
23+
<!-- Login card -->
24+
<div class="relative z-10 flex items-center justify-center w-full min-h-screen crb_main">
25+
<div class="crb_card w-full md:w-1/2 p-6 flex flex-col md:flex-row items-center gap-8">
26+
27+
<!-- Logo -->
28+
<div class="md:w-1/2 flex justify-center items-center">
29+
<img src="${url.resourcesPath}/img/logo.png"
30+
class="logo-login"
31+
alt="Cerberus / InValue">
32+
</div>
33+
34+
<!-- Login form -->
35+
<div class="md:w-1/2">
36+
37+
<form id="kc-form-login"
38+
action="${url.loginAction}"
39+
method="post"
40+
class="space-y-4">
41+
42+
<!-- Title -->
43+
<h2 class="text-2xl font-bold mb-8">${msg("doLogIn")}</h2>
44+
45+
<!-- Error messages -->
46+
<#if message?has_content>
47+
<div class="alert-error">
48+
${message.summary}
49+
</div>
50+
</#if>
51+
52+
<!-- Username -->
53+
<div class="mb-4">
54+
<label class="block mb-1">${msg("username")}:</label>
55+
<div class="flex items-center border rounded">
56+
<span class="px-2 text-gray-500 flex items-center justify-center">
57+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
58+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
59+
d="M16 14c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zM12 14v2a4 4 0 004 4h4a4 4 0 004-4v-2"/>
60+
</svg>
61+
</span>
62+
<input id="username" name="username" class="flex-1 p-2 outline-none" placeholder="${msg("username")}" autofocus>
63+
</div>
64+
</div>
65+
66+
<!-- Password -->
67+
<div class="mb-4">
68+
<label class="block mb-1">${msg("password")}:</label>
69+
<div class="flex items-center border rounded">
70+
<span class="px-2 text-gray-500 flex items-center justify-center">
71+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
72+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
73+
d="M12 11c1.657 0 3 1.343 3 3v3H9v-3c0-1.657 1.343-3 3-3zM7 11V8a5 5 0 0110 0v3"/>
74+
<rect x="7" y="14" width="10" height="6" rx="2" ry="2" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
75+
</svg>
76+
</span>
77+
<input id="password" name="password" type="password" class="flex-1 p-2 outline-none" placeholder="${msg("password")}" autocomplete="current-password">
78+
</div>
79+
</div>
80+
81+
<!-- Remember me -->
82+
<#if realm.rememberMe>
83+
<div class="flex items-center gap-2">
84+
<input type="checkbox"
85+
id="rememberMe"
86+
name="rememberMe"
87+
<#if login.rememberMe??>checked
88+
</#if>
89+
/>
90+
<label for="rememberMe">
91+
${msg("rememberMe")}
92+
</label>
93+
</div>
94+
</#if>
95+
96+
<!-- Submit button -->
97+
<div class="flex justify-end">
98+
<button type="submit" class="border rounded-lg bg-blue-600 hover:bg-blue-500 text-white px-4 py-2">${msg("doLogIn")}</button>
99+
</div>
100+
101+
<!-- Forgot password -->
102+
<#if realm.resetPasswordAllowed>
103+
<div class="flex justify-end mt-2">
104+
<a href="${url.loginResetCredentialsUrl}" class="text-blue-500 hover:underline">${msg("doForgotPassword")}</a>
105+
</div>
106+
</#if>
107+
</form>
108+
</div>
109+
</div>
110+
</div>
111+
<script>
112+
document.addEventListener("DOMContentLoaded", function () {
113+
tsParticles.load("tsparticles", {
114+
fullScreen: { enable: false },
115+
background: { color: { value: "transparent" } },
116+
particles: {
117+
number: { value: 80, density: { enable: true, area: 900 } },
118+
color: { value: "#3b82f6" },
119+
links: { enable: true, distance: 140, color: "#3b82f6", opacity: 0.35, width: 1 },
120+
move: { enable: true, speed: 0.4, outModes: { default: "bounce" } },
121+
size: { value: 2 },
122+
opacity: { value: 0.8 }
123+
},
124+
detectRetina: true
125+
});
126+
});
127+
</script>
128+
</body>
129+
</html>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
parent=keycloak
2+
import=common/keycloak
3+
4+
styles=css/styles.css
5+
kcLogoLink=http://www.cerberus-testing.org

0 commit comments

Comments
 (0)