Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cb5af53
Added website stuff for Web-User administarion and additional request…
fnkbsi Aug 16, 2024
4344489
delete Statistics.java.bak
Aug 17, 2024
30d2e78
WebUserForm, WebUserQueryForm use camel case
Aug 17, 2024
2fa85d7
WebUserOverview moved to service/dto, use camel case and correct typo
Aug 17, 2024
2bfa311
WebUserForm use camel case at webUsername
Aug 17, 2024
e2f7b0f
webuser*.jsp: adapt to the using of camel case in the dto's
Aug 17, 2024
a605705
moved the mapping part of List<WebUserOverview> getOverview(WebUserQu…
fnkbsi Aug 17, 2024
88d2880
WebUserController: using camel case
fnkbsi Aug 17, 2024
b9b43be
WebUserServive don't expose pw and apitoken to ui
fnkbsi Aug 17, 2024
d00c287
refactor
mrge-sevket-goekay Aug 17, 2024
56015ba
WebUserOverview: changed String[] authorities to String
Aug 18, 2024
8aa37f1
Adding Enum WebUserAuthority and adapt the code using it
Aug 18, 2024
7f5ab25
BugFix: Set the ".requestMatchers(prefix + "/**").hasAuthority("ADMIN…
fnkbsi Aug 28, 2024
3936817
WebUserController removed password comparison; webuserAdd.jsp, webuse…
fnkbsi Aug 28, 2024
e13f60a
Merge branch 'master' into MultiUser_4
fnkbsi Aug 28, 2024
d7a2db8
GenericRepositoryImpl: re-add 'import org.jooq.impl.DSL;' (wrong deci…
fnkbsi Aug 28, 2024
3ee9c65
resolve style checks
fnkbsi Aug 28, 2024
6e78297
Merge branch 'steve-community:master' into MultiUser_4
fnkbsi Oct 9, 2024
fd0d461
WebUserForm.java: pwError changed from AssertTrue to AssertFalse
fnkbsi Oct 9, 2024
bfbe877
Improvements on webUser password change. Only own password can be cha…
fnkbsi Oct 9, 2024
d77afa3
add website to set and change WebUser API password
fnkbsi Nov 7, 2024
3d4eaa4
Merge branch 'steve-community:master' into MultiUser_4
fnkbsi Dec 24, 2024
9b2aee4
Merge branch 'steve-community:master' into MultiUser_4
fnkbsi Feb 25, 2025
661500b
Merge branch 'steve-community:master' into MultiUser_4
fnkbsi Apr 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import static de.rwth.idsg.steve.SteveConfiguration.CONFIG;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
import org.springframework.security.web.util.matcher.RequestMatcher;

/**
* @author Sevket Goekay <[email protected]>
Expand Down Expand Up @@ -60,6 +63,8 @@ public PasswordEncoder passwordEncoder() {
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
final String prefix = CONFIG.getSpringManagerMapping();

RequestMatcher toOverview = (request) -> request.getParameter("backToOverview") != null;

return http
.authorizeHttpRequests(
req -> req
Expand All @@ -69,6 +74,34 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
WebSocketConfiguration.PATH_INFIX + "**",
"/WEB-INF/views/**" // https://github.com/spring-projects/spring-security/issues/13285#issuecomment-1579097065
).permitAll()
.requestMatchers(prefix + "/home").hasAnyAuthority("USER", "ADMIN")
// webuser
//only allowed to change the own password
.requestMatchers(prefix + "/webusers" + "/password/{name}")
.access(new WebExpressionAuthorizationManager("#name == authentication.name"))
.requestMatchers(prefix + "/webusers" + "/apipassword/{name}")
.access(new WebExpressionAuthorizationManager("#name == authentication.name"))
// otherwise denies access on backToOverview!
.requestMatchers(toOverview).hasAnyAuthority("USER", "ADMIN")
.requestMatchers(HttpMethod.GET, prefix + "/webusers/**").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(HttpMethod.POST, prefix + "/webusers/**").hasAuthority("ADMIN")
// users
.requestMatchers(prefix + "/users").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/users" + "/details/**").hasAnyAuthority("USER", "ADMIN")
//ocppTags
.requestMatchers(prefix + "/ocppTags").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/ocppTags" + "/details/**").hasAnyAuthority("USER", "ADMIN")
// chargepoints
.requestMatchers(prefix + "/chargepoints").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/chargepoints" + "/details/**").hasAnyAuthority("USER", "ADMIN")
// transactions and reservations
.requestMatchers(prefix + "/transactions").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/transactions" + "/details/**").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/reservations").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/reservations" + "/**").hasAnyAuthority("ADMIN")
// singout and noAccess
.requestMatchers(prefix + "/signout/" + "**").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/noAccess/" + "**").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/**").hasAuthority("ADMIN")
)
// SOAP stations are making POST calls for communication. even though the following path is permitted for
Expand All @@ -84,6 +117,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.logout(
req -> req.logoutUrl(prefix + "/signout")
)
.exceptionHandling(
req -> req.accessDeniedPage(prefix + "/noAccess")
)
.build();
}

Expand Down
14 changes: 14 additions & 0 deletions src/main/java/de/rwth/idsg/steve/repository/WebUserRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,20 @@
*/
package de.rwth.idsg.steve.repository;

import de.rwth.idsg.steve.web.dto.WebUserQueryForm;
import jooq.steve.db.tables.records.WebUserRecord;
import org.jooq.JSON;
import org.jooq.Record4;
import org.jooq.Result;

public interface WebUserRepository {

void createUser(WebUserRecord user);

void updateUser(WebUserRecord user);

void updateUserByPk(WebUserRecord user);

void deleteUser(String username);

void deleteUser(int webUserPk);
Expand All @@ -36,7 +42,15 @@

void changePassword(String username, String newPassword);

void changePassword(Integer userPk, String newPassword);

Check failure on line 46 in src/main/java/de/rwth/idsg/steve/repository/WebUserRepository.java

View workflow job for this annotation

GitHub Actions / checkstyle

[checkstyle] reported by reviewdog 🐶 Line has trailing spaces. Raw Output: /github/workspace/./src/main/java/de/rwth/idsg/steve/repository/WebUserRepository.java:46:0: error: Line has trailing spaces. (com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineCheck)
void changeApiPassword(Integer userPk, String newPassword);

boolean userExists(String username);

WebUserRecord loadUserByUsePk(Integer webUserPk);

WebUserRecord loadUserByUsername(String username);

Result<Record4<Integer, String, Boolean, JSON>> getOverview(WebUserQueryForm form);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import org.jooq.DatePart;
import org.jooq.Field;
import org.jooq.Record2;
import org.jooq.Record8;
import org.jooq.Record9;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.ContextRefreshedEvent;
Expand All @@ -46,6 +46,7 @@
import static jooq.steve.db.tables.OcppTag.OCPP_TAG;
import static jooq.steve.db.tables.SchemaVersion.SCHEMA_VERSION;
import static jooq.steve.db.tables.User.USER;
import static jooq.steve.db.tables.WebUser.WEB_USER;
import static org.jooq.impl.DSL.max;
import static org.jooq.impl.DSL.select;

Expand Down Expand Up @@ -129,7 +130,12 @@ public Statistics getStats() {
.where(date(CHARGE_BOX.LAST_HEARTBEAT_TIMESTAMP).lessThan(date(yesterdaysNow)))
.asField("heartbeats_earlier");

Record8<Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer> gs =
Field<Integer> numWebUsers =
ctx.selectCount()
.from(WEB_USER)
.asField("num_webusers");

Record9<Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer> gs =
ctx.select(
numChargeBoxes,
numOcppTags,
Expand All @@ -138,7 +144,8 @@ public Statistics getStats() {
numTransactions,
heartbeatsToday,
heartbeatsYesterday,
heartbeatsEarlier
heartbeatsEarlier,
numWebUsers
).fetchOne();

return Statistics.builder()
Expand All @@ -150,6 +157,7 @@ public Statistics getStats() {
.heartbeatToday(gs.value6())
.heartbeatYesterday(gs.value7())
.heartbeatEarlier(gs.value8())
.numWebUsers(gs.value9())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,24 @@
package de.rwth.idsg.steve.repository.impl;

import de.rwth.idsg.steve.repository.WebUserRepository;
import de.rwth.idsg.steve.web.dto.WebUserQueryForm;
import jooq.steve.db.tables.records.WebUserRecord;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.JSON;
import org.springframework.stereotype.Repository;

import static jooq.steve.db.Tables.WEB_USER;
import org.jooq.Record4;
import org.jooq.Result;
import org.jooq.SelectQuery;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.jooq.impl.DSL.condition;
import static org.jooq.impl.DSL.count;

Expand Down Expand Up @@ -54,15 +64,25 @@ public void createUser(WebUserRecord user) {

@Override
public void updateUser(WebUserRecord user) {
// To change the password use one of the changePassword methods
ctx.update(WEB_USER)
.set(WEB_USER.PASSWORD, user.getPassword())
.set(WEB_USER.API_PASSWORD, user.getApiPassword())
.set(WEB_USER.ENABLED, user.getEnabled())
.set(WEB_USER.AUTHORITIES, user.getAuthorities())
.where(WEB_USER.USERNAME.eq(user.getUsername()))
.execute();
}

@Override
public void updateUserByPk(WebUserRecord user) {
// To change the password use one of the changePassword methods
ctx.update(WEB_USER)
.set(WEB_USER.USERNAME, user.getUsername())
.set(WEB_USER.ENABLED, user.getEnabled())
.set(WEB_USER.AUTHORITIES, user.getAuthorities())
.where(WEB_USER.WEB_USER_PK.eq(user.getWebUserPk()))
.execute();
}

@Override
public void deleteUser(String username) {
ctx.delete(WEB_USER)
Expand All @@ -87,10 +107,9 @@ public void changeStatusOfUser(String username, boolean enabled) {

@Override
public Integer getUserCountWithAuthority(String authority) {
JSON authValue = JSON.json("\"" + authority + "\"");
return ctx.selectCount()
.from(WEB_USER)
.where(condition("json_contains({0}, {1})", WEB_USER.AUTHORITIES, authValue))
.where(conditionsForAuthorities(Collections.singletonList(authority)))
.fetchOne(count());
}

Expand All @@ -102,6 +121,22 @@ public void changePassword(String username, String newPassword) {
.execute();
}

@Override
public void changePassword(Integer userPk, String newPassword) {
ctx.update(WEB_USER)
.set(WEB_USER.PASSWORD, newPassword)
.where(WEB_USER.WEB_USER_PK.eq(userPk))
.execute();
}

@Override
public void changeApiPassword(Integer userPk, String newPassword) {
ctx.update(WEB_USER)
.set(WEB_USER.API_PASSWORD, newPassword)
.where(WEB_USER.WEB_USER_PK.eq(userPk))
.execute();
}

@Override
public boolean userExists(String username) {
return ctx.selectOne()
Expand All @@ -117,4 +152,46 @@ public WebUserRecord loadUserByUsername(String username) {
.where(WEB_USER.USERNAME.eq(username))
.fetchOne();
}

@Override
public WebUserRecord loadUserByUsePk(Integer webUserPk) {
return ctx.selectFrom(WEB_USER)
.where(WEB_USER.WEB_USER_PK.eq(webUserPk))
.fetchOne();
}

@Override
public Result<Record4<Integer, String, Boolean, JSON>> getOverview(WebUserQueryForm form) {
SelectQuery selectQuery = ctx.selectQuery();
selectQuery.addFrom(WEB_USER);
selectQuery.addSelect(
WEB_USER.WEB_USER_PK,
WEB_USER.USERNAME,
WEB_USER.ENABLED,
WEB_USER.AUTHORITIES
);

if (form.isSetWebUsername()) {
selectQuery.addConditions(WEB_USER.USERNAME.eq(form.getWebUsername()));
}

if (form.isSetEnabled()) {
selectQuery.addConditions(WEB_USER.ENABLED.eq(form.getEnabled()));
}

if (form.isSetRoles()) {
String[] split = form.getRoles().split(","); //Semicolon seperated String to StringArray
List<String> roles = Arrays.stream(split).map(String::strip).toList();
selectQuery.addConditions(conditionsForAuthorities(roles));
}

return selectQuery.fetch();
}

private static List<Condition> conditionsForAuthorities(List<String> authorities) {
return authorities.stream()
.map(it -> JSON.json("\"" + it + "\""))
.map(it -> condition("json_contains({0}, {1})", WEB_USER.AUTHORITIES, it))
.toList();
}
}
Loading