Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c8f7de4
Bump jetty.version from 12.0.17 to 12.0.18
dependabot[bot] Mar 22, 2025
1c3c3bb
Bump spring.security.version from 6.4.3 to 6.4.4
dependabot[bot] Mar 22, 2025
be202c5
Bump flyway.version from 11.4.0 to 11.5.0
dependabot[bot] Mar 29, 2025
1ae5668
Bump plugin.license-maven.version from 4.6 to 5.0.0
dependabot[bot] Mar 30, 2025
b695755
Merge pull request #1724 from steve-community/dependabot/maven/jetty.…
goekay Aug 13, 2025
65ff23e
Merge pull request #1725 from steve-community/dependabot/maven/spring…
goekay Aug 13, 2025
57347c4
Merge pull request #1727 from steve-community/dependabot/maven/plugin…
goekay Aug 13, 2025
5ee952e
Merge pull request #1737 from steve-community/dependabot/maven/flyway…
goekay Aug 13, 2025
8ebde00
retire ubuntu 20, use ubuntu 24
goekay Aug 13, 2025
adbc90b
Merge pull request #1765 from steve-community/update-github-runners
goekay Aug 13, 2025
2a2d368
Bump actions/checkout from 4 to 5
dependabot[bot] Aug 13, 2025
d79e651
Bump org.apache.httpcomponents.client5:httpclient5 from 5.4.2 to 5.5
dependabot[bot] Aug 13, 2025
718204c
Bump com.zaxxer:HikariCP from 6.2.1 to 7.0.1
dependabot[bot] Aug 13, 2025
4c2f2fa
Merge pull request #1754 from steve-community/dependabot/maven/org.ap…
goekay Aug 13, 2025
7118b15
Merge pull request #1764 from steve-community/dependabot/github_actio…
goekay Aug 13, 2025
28b627a
Merge pull request #1763 from steve-community/dependabot/maven/com.za…
goekay Aug 13, 2025
c89df4b
Merge remote-tracking branch 'fnkbsi/MultiUser_4' into upstream
juherr Aug 15, 2025
498caf3
fix: update after CodeRabbit review
juherr Aug 18, 2025
2edfbee
fix: update licence
juherr Aug 18, 2025
3a7b344
fix: json array elements may have different sort
juherr Aug 18, 2025
a845ce9
fix: update after Copilot review
juherr Aug 18, 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
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-20.04, ubuntu-22.04 ]
os: [ ubuntu-22.04, ubuntu-24.04 ]
java: [ '17', '21' ]
db: [ 'mysql:8.0', 'mariadb:10.3', 'mariadb:10.4.30', 'mariadb:10.5.21', 'mariadb:10.6.14' ]
runs-on: ${{ matrix.os }}
Expand All @@ -21,7 +21,7 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Java ${{ matrix.Java }}
uses: actions/setup-java@v4
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jobs:
license-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0

Expand All @@ -21,7 +21,7 @@ jobs:
checkstyle:
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
Expand Down
12 changes: 6 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<jooq.version>3.19.18</jooq.version>
<flyway.version>11.4.0</flyway.version>
<flyway.version>11.5.0</flyway.version>
<cxf.version>4.1.1</cxf.version>
<spring.version>6.2.0</spring.version>
<spring.security.version>6.4.3</spring.security.version>
<spring.security.version>6.4.4</spring.security.version>
<mysql.jdbc.version>9.2.0</mysql.jdbc.version>
<jetty.version>12.0.17</jetty.version>
<jetty.version>12.0.18</jetty.version>
<lombok.version>1.18.36</lombok.version>
<jackson.version>2.18.3</jackson.version>
<slf4j.version>2.0.17</slf4j.version>
<plugin.license-maven.version>4.6</plugin.license-maven.version>
<plugin.license-maven.version>5.0.0</plugin.license-maven.version>

<!-- In Mysql: schema == database (http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_schema) -->
<jdbcUrl>jdbc:mysql://${db.ip}:${db.port}/${db.schema}?useSSL=true&amp;serverTimezone=UTC</jdbcUrl>
Expand Down Expand Up @@ -582,7 +582,7 @@
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.4.2</version>
<version>5.5</version>
</dependency>
<dependency>
<groupId>jakarta.websocket</groupId>
Expand Down Expand Up @@ -726,7 +726,7 @@
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>6.2.1</version>
<version>7.0.1</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
Expand Down
23 changes: 13 additions & 10 deletions src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ public PasswordEncoder passwordEncoder() {
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
final String prefix = CONFIG.getSpringManagerMapping();

RequestMatcher toOverview = (request) -> request.getParameter("backToOverview") != null;
RequestMatcher toOverview = request -> {
String param = request.getParameter("backToOverview");
return param != null && !param.isEmpty();
};

return http
.authorizeHttpRequests(
Expand All @@ -77,31 +80,31 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.requestMatchers(prefix + "/home").hasAnyAuthority("USER", "ADMIN")
// webuser
//only allowed to change the own password
.requestMatchers(prefix + "/webusers" + "/password/{name}")
.requestMatchers(prefix + "/webusers/password/{name}")
.access(new WebExpressionAuthorizationManager("#name == authentication.name"))
.requestMatchers(prefix + "/webusers" + "/apipassword/{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")
.requestMatchers(prefix + "/users/details/**").hasAnyAuthority("USER", "ADMIN")
//ocppTags
.requestMatchers(prefix + "/ocppTags").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/ocppTags" + "/details/**").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/ocppTags/details/**").hasAnyAuthority("USER", "ADMIN")
// chargepoints
.requestMatchers(prefix + "/chargepoints").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/chargepoints" + "/details/**").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 + "/transactions/details/**").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/reservations").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/reservations" + "/**").hasAnyAuthority("ADMIN")
.requestMatchers(prefix + "/reservations/**").hasAnyAuthority("ADMIN")
// singout and noAccess
.requestMatchers(prefix + "/signout/" + "**").hasAnyAuthority("USER", "ADMIN")
.requestMatchers(prefix + "/noAccess/" + "**").hasAnyAuthority("USER", "ADMIN")
.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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ public interface WebUserRepository {
void changePassword(String username, String newPassword);

void changePassword(Integer userPk, String newPassword);

void changeApiPassword(Integer userPk, String newPassword);

boolean userExists(String username);

WebUserRecord loadUserByUsePk(Integer webUserPk);
WebUserRecord loadUserByUserPk(Integer webUserPk);

WebUserRecord loadUserByUsername(String username);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,22 @@
import lombok.extern.slf4j.Slf4j;
import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.Field;
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 org.jooq.impl.DSL;
import org.jooq.impl.SQLDataType;
import org.springframework.stereotype.Repository;

import static jooq.steve.db.Tables.WEB_USER;
import static org.jooq.impl.DSL.count;

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;
import java.util.Objects;

/**
* @author Sevket Goekay <sevketgokay@gmail.com>
Expand Down Expand Up @@ -154,7 +156,7 @@ public WebUserRecord loadUserByUsername(String username) {
}

@Override
public WebUserRecord loadUserByUsePk(Integer webUserPk) {
public WebUserRecord loadUserByUserPk(Integer webUserPk) {
return ctx.selectFrom(WEB_USER)
.where(WEB_USER.WEB_USER_PK.eq(webUserPk))
.fetchOne();
Expand All @@ -180,7 +182,7 @@ public Result<Record4<Integer, String, Boolean, JSON>> getOverview(WebUserQueryF
}

if (form.isSetRoles()) {
String[] split = form.getRoles().split(","); //Semicolon seperated String to StringArray
String[] split = form.getRoles().split(","); // Comma seperated String to StringArray
List<String> roles = Arrays.stream(split).map(String::strip).toList();
selectQuery.addConditions(conditionsForAuthorities(roles));
}
Expand All @@ -190,8 +192,14 @@ public Result<Record4<Integer, String, Boolean, JSON>> getOverview(WebUserQueryF

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))
.filter(Objects::nonNull)
.filter(it -> !it.trim().isEmpty())
.map(WebUserRepositoryImpl::jsonQuote)
.map(WEB_USER.AUTHORITIES::contains)
.toList();
}

private static Field<JSON> jsonQuote(String element) {
return DSL.field("JSON_QUOTE({0})", SQLDataType.JSON, DSL.val(element));
}
}
20 changes: 8 additions & 12 deletions src/main/java/de/rwth/idsg/steve/service/WebUserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ public void changePassword(String oldPassword, String newPassword) {
}

String username = currentUser.getName();
webUserRepository.changePassword(username, newPassword);
webUserRepository.changePassword(username, encoder.encode(newPassword));

Authentication authentication = createNewAuthentication(currentUser, newPassword);
Authentication authentication = createNewAuthentication(currentUser);
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
context.setAuthentication(authentication);
this.securityContextHolderStrategy.setContext(context);
Expand Down Expand Up @@ -197,7 +197,6 @@ public boolean hasUserWithAuthority(String authority) {
return count != null && count > 0;
}


// Methods for the website
public void add(WebUserForm form) {
createUser(toUserDetails(form));
Expand All @@ -216,10 +215,10 @@ public void update(WebUserBaseForm form) {
public void updatePassword(WebUserForm form) {
webUserRepository.changePassword(form.getWebUserPk(), encoder.encode(form.getPassword()));
}

public void updateApiPassword(WebUserForm form) {
String newPassword = null;
if (form.getApiPassword() != null) {
if (form.getApiPassword() != null && !form.getApiPassword().isEmpty()) {
newPassword = encoder.encode(form.getApiPassword());
}
webUserRepository.changeApiPassword(form.getWebUserPk(), newPassword);
Expand All @@ -231,15 +230,13 @@ public List<WebUserOverview> getOverview(WebUserQueryForm form) {
.webUserPk(r.value1())
.webUsername(r.value2())
.enabled(r.value3())
//.authorities(fromJsonToString(r.value4()))
.authorities(WebUserAuthority.fromJsonValue(r.value4()))
.build()
);
}


public WebUserBaseForm getDetails(Integer webUserPk) {
WebUserRecord ur = webUserRepository.loadUserByUsePk(webUserPk);
WebUserRecord ur = webUserRepository.loadUserByUserPk(webUserPk);

if (ur == null) {
throw new SteveException("There is no user with id '%d'", webUserPk);
Expand All @@ -252,12 +249,12 @@ public WebUserBaseForm getDetails(Integer webUserPk) {
form.setAuthorities(WebUserAuthority.fromJsonValue(ur.getAuthorities()));
return form;
}

public WebUserBaseForm getDetails(String webUserName) {
WebUserRecord ur = webUserRepository.loadUserByUsername(webUserName);

if (ur == null) {
throw new SteveException("There is no user with id '%s'", webUserName);
throw new SteveException("There is no user with username '%s'", webUserName);
}

WebUserBaseForm form = new WebUserBaseForm();
Expand Down Expand Up @@ -309,7 +306,6 @@ private UserDetails toUserDetailsBaseForm(WebUserBaseForm form) {
private UserDetails toUserDetails(WebUserForm form) {
String encPw = "";
if (form.getPassword() != null) {
//encPw = form.getPassword();
encPw = encoder.encode(form.getPassword());
}
return User
Expand Down Expand Up @@ -364,7 +360,7 @@ private static void validateAuthorities(Collection<? extends GrantedAuthority> a
/**
* Lifted from {@link JdbcUserDetailsManager#createNewAuthentication(Authentication, String)}
*/
private Authentication createNewAuthentication(Authentication currentAuth, String newPassword) {
private Authentication createNewAuthentication(Authentication currentAuth) {
var user = this.loadUserByUsername(currentAuth.getName());
var newAuthentication = authenticated(user, null, user.getAuthorities());
newAuthentication.setDetails(currentAuth.getDetails());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve
* Copyright (C) 2013-2024 SteVe Community Team
* Copyright (C) 2013-2025 SteVe Community Team
* All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve
* Copyright (C) 2013-2024 SteVe Community Team
* Copyright (C) 2013-2025 SteVe Community Team
* All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
Expand All @@ -18,14 +18,11 @@
*/
package de.rwth.idsg.steve.web.controller;



import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;



/**
/**
* @author fnkbsi
* @since 01.04.2022
*/
Expand All @@ -37,7 +34,7 @@ public class NoAccessController {
// HTTP methods
// -------------------------------------------------------------------------

@RequestMapping()
@GetMapping()
public String accessDenied() {
return "noAccess";
}
Expand Down
Loading