diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6241a4d1d..0aa30791e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 }} @@ -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: diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml index f5bfc91d4..d2a842516 100644 --- a/.github/workflows/review.yml +++ b/.github/workflows/review.yml @@ -5,7 +5,7 @@ jobs: license-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 @@ -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' diff --git a/pom.xml b/pom.xml index e802cd001..4c38b40de 100644 --- a/pom.xml +++ b/pom.xml @@ -36,16 +36,16 @@ UTF-8 3.19.18 - 11.4.0 + 11.5.0 4.1.1 6.2.0 - 6.4.3 + 6.4.4 9.2.0 - 12.0.17 + 12.0.18 1.18.36 2.18.3 2.0.17 - 4.6 + 5.0.0 jdbc:mysql://${db.ip}:${db.port}/${db.schema}?useSSL=true&serverTimezone=UTC @@ -582,7 +582,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.4.2 + 5.5 jakarta.websocket @@ -726,7 +726,7 @@ com.zaxxer HikariCP - 6.2.1 + 7.0.1 org.jooq diff --git a/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java index 5ec7b5db5..b4e35234a 100644 --- a/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/config/SecurityConfiguration.java @@ -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( @@ -77,9 +80,9 @@ 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") @@ -87,21 +90,21 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .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 diff --git a/src/main/java/de/rwth/idsg/steve/repository/WebUserRepository.java b/src/main/java/de/rwth/idsg/steve/repository/WebUserRepository.java index b323f7216..b871cc191 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/WebUserRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/WebUserRepository.java @@ -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); diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/WebUserRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/WebUserRepositoryImpl.java index 7fce360db..ca8b0232a 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/WebUserRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/WebUserRepositoryImpl.java @@ -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 @@ -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(); @@ -180,7 +182,7 @@ public Result> 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 roles = Arrays.stream(split).map(String::strip).toList(); selectQuery.addConditions(conditionsForAuthorities(roles)); } @@ -190,8 +192,14 @@ public Result> getOverview(WebUserQueryF private static List conditionsForAuthorities(List 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 jsonQuote(String element) { + return DSL.field("JSON_QUOTE({0})", SQLDataType.JSON, DSL.val(element)); + } } diff --git a/src/main/java/de/rwth/idsg/steve/service/WebUserService.java b/src/main/java/de/rwth/idsg/steve/service/WebUserService.java index 07f8181bc..55474560f 100644 --- a/src/main/java/de/rwth/idsg/steve/service/WebUserService.java +++ b/src/main/java/de/rwth/idsg/steve/service/WebUserService.java @@ -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); @@ -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)); @@ -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); @@ -231,15 +230,13 @@ public List 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); @@ -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(); @@ -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 @@ -364,7 +360,7 @@ private static void validateAuthorities(Collection 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()); diff --git a/src/main/java/de/rwth/idsg/steve/service/dto/WebUserOverview.java b/src/main/java/de/rwth/idsg/steve/service/dto/WebUserOverview.java index be8c4db94..7d2ba115d 100644 --- a/src/main/java/de/rwth/idsg/steve/service/dto/WebUserOverview.java +++ b/src/main/java/de/rwth/idsg/steve/service/dto/WebUserOverview.java @@ -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 diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/NoAccessController.java b/src/main/java/de/rwth/idsg/steve/web/controller/NoAccessController.java index 3cecc0906..3fa4481bb 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/NoAccessController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/NoAccessController.java @@ -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 @@ -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 */ @@ -37,7 +34,7 @@ public class NoAccessController { // HTTP methods // ------------------------------------------------------------------------- - @RequestMapping() + @GetMapping() public String accessDenied() { return "noAccess"; } diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/WebUsersController.java b/src/main/java/de/rwth/idsg/steve/web/controller/WebUsersController.java index 17a8c1314..7ac10e435 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/WebUsersController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/WebUsersController.java @@ -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 @@ -18,29 +18,29 @@ */ package de.rwth.idsg.steve.web.controller; - import de.rwth.idsg.steve.service.WebUserService; import de.rwth.idsg.steve.web.dto.WebUserAuthority; import de.rwth.idsg.steve.web.dto.WebUserBaseForm; import de.rwth.idsg.steve.web.dto.WebUserForm; import de.rwth.idsg.steve.web.dto.WebUserQueryForm; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import jakarta.validation.Valid; - +@RequiredArgsConstructor @Controller @RequestMapping(value = "/manager/webusers") public class WebUsersController { - @Autowired private WebUserService webUserService; + private final WebUserService webUserService; private static final String PARAMS = "params"; @@ -61,13 +61,13 @@ public class WebUsersController { // HTTP methods // ------------------------------------------------------------------------- - @RequestMapping(method = RequestMethod.GET) + @GetMapping public String getOverview(Model model) { initList(model, new WebUserQueryForm()); return "data-man/webusers"; } - @RequestMapping(value = QUERY_PATH, method = RequestMethod.GET) + @GetMapping(value = QUERY_PATH) public String getQuery(@ModelAttribute(PARAMS) WebUserQueryForm params, Model model) { initList(model, params); return "data-man/webusers"; @@ -78,26 +78,29 @@ private void initList(Model model, WebUserQueryForm params) { model.addAttribute("webuserList", webUserService.getOverview(params)); } - @RequestMapping(value = DETAILS_PATH, method = RequestMethod.GET) + @GetMapping(value = DETAILS_PATH) public String getDetails(@PathVariable("webUserPk") Integer webUserPk, Model model) { WebUserBaseForm form = webUserService.getDetails(webUserPk); model.addAttribute("webuserForm", form); + model.addAttribute("availableAuthorities", WebUserAuthority.values()); return "data-man/webuserDetails"; } - @RequestMapping(value = ADD_PATH, method = RequestMethod.GET) + @GetMapping(value = ADD_PATH) public String addGet(Model model) { WebUserForm webUserForm = new WebUserForm(); webUserForm.setAuthorities(WebUserAuthority.USER); model.addAttribute("webuserForm", webUserForm); + model.addAttribute("availableAuthorities", WebUserAuthority.values()); return "data-man/webuserAdd"; } - @RequestMapping(params = "add", value = ADD_PATH, method = RequestMethod.POST) + @PostMapping(params = "add", value = ADD_PATH) public String addPost(@Valid @ModelAttribute("webuserForm") WebUserForm webuserForm, BindingResult result, Model model) { if (result.hasErrors()) { + model.addAttribute("availableAuthorities", WebUserAuthority.values()); return "data-man/webuserAdd"; } @@ -105,10 +108,11 @@ public String addPost(@Valid @ModelAttribute("webuserForm") WebUserForm webuserF return toOverview(); } - @RequestMapping(params = "update", value = UPDATE_PATH, method = RequestMethod.POST) + @PostMapping(params = "update", value = UPDATE_PATH) public String update(@Valid @ModelAttribute("webuserForm") WebUserBaseForm webuserBaseForm, BindingResult result, Model model) { if (result.hasErrors()) { + model.addAttribute("availableAuthorities", WebUserAuthority.values()); return "data-man/webuserDetails"; } @@ -116,22 +120,26 @@ public String update(@Valid @ModelAttribute("webuserForm") WebUserBaseForm webus return toOverview(); } - @RequestMapping(value = PASSWORD_PATH, method = RequestMethod.GET) + @GetMapping(value = PASSWORD_PATH) public String passwordChangeGet(@PathVariable("webUserName") String webUserName, Model model) { + WebUserBaseForm base = webUserService.getDetails(webUserName); + WebUserForm webUserForm = fromBase(base); + model.addAttribute("webuserForm", webUserForm); + return "data-man/webuserPassword"; + } + + private static WebUserForm fromBase(WebUserBaseForm webUserBaseForm) { WebUserForm webUserForm = new WebUserForm(); - WebUserBaseForm webUserBaseForm = webUserService.getDetails(webUserName); webUserForm.setWebUserPk(webUserBaseForm.getWebUserPk()); webUserForm.setWebUsername(webUserBaseForm.getWebUsername()); webUserForm.setAuthorities(webUserBaseForm.getAuthorities()); webUserForm.setEnabled(webUserBaseForm.getEnabled()); - - model.addAttribute("webuserForm", webUserForm); - return "data-man/webuserPassword"; + return webUserForm; } - @RequestMapping(params = "change", value = PASSWORD_PATH, method = RequestMethod.POST) + @PostMapping(params = "change", value = PASSWORD_PATH) public String passwordChange(@Valid @ModelAttribute("webuserForm") WebUserForm webuserForm, - BindingResult result, Model model) { + BindingResult result) { if (result.hasErrors()) { return "data-man/webuserPassword"; } @@ -140,22 +148,17 @@ public String passwordChange(@Valid @ModelAttribute("webuserForm") WebUserForm w return toDetails(webuserForm.getWebUserPk()); } - @RequestMapping(value = API_PASSWORD_PATH, method = RequestMethod.GET) + @GetMapping(value = API_PASSWORD_PATH) public String apiPasswordChangeGet(@PathVariable("webUserName") String webUserName, Model model) { - WebUserForm webUserForm = new WebUserForm(); - WebUserBaseForm webUserBaseForm = webUserService.getDetails(webUserName); - webUserForm.setWebUserPk(webUserBaseForm.getWebUserPk()); - webUserForm.setWebUsername(webUserBaseForm.getWebUsername()); - webUserForm.setAuthorities(webUserBaseForm.getAuthorities()); - webUserForm.setEnabled(webUserBaseForm.getEnabled()); - + WebUserBaseForm base = webUserService.getDetails(webUserName); + WebUserForm webUserForm = fromBase(base); model.addAttribute("webuserForm", webUserForm); return "data-man/webuserApiPassword"; } - @RequestMapping(params = "change", value = API_PASSWORD_PATH, method = RequestMethod.POST) + @PostMapping(params = "change", value = API_PASSWORD_PATH) public String apiPasswordChange(@Valid @ModelAttribute("webuserForm") WebUserForm webuserForm, - BindingResult result, Model model) { + BindingResult result) { if (result.hasErrors()) { return "data-man/webuserApiPassword"; } @@ -164,7 +167,7 @@ public String apiPasswordChange(@Valid @ModelAttribute("webuserForm") WebUserFor return toDetails(webuserForm.getWebUserPk()); } - @RequestMapping(value = DELETE_PATH, method = RequestMethod.POST) + @PostMapping(value = DELETE_PATH) public String delete(@PathVariable("webUserPk") Integer webUserPk) { webUserService.deleteUser(webUserPk); return toOverview(); @@ -174,34 +177,31 @@ public String delete(@PathVariable("webUserPk") Integer webUserPk) { // Back to Overview // ------------------------------------------------------------------------- - @RequestMapping(params = "backToOverview", value = PASSWORD_PATH, method = RequestMethod.POST) - public String passwordBackToOverview(@Valid @ModelAttribute("webuserForm") WebUserForm webuserForm, - BindingResult result, Model model) { + @PostMapping(params = "backToOverview", value = PASSWORD_PATH) + public String passwordBackToOverview(@Valid @ModelAttribute("webuserForm") WebUserForm webuserForm) { return toDetails(webuserForm.getWebUserPk()); } - @RequestMapping(params = "backToOverview", value = API_PASSWORD_PATH, method = RequestMethod.POST) - public String apiPasswordBackToOverview(@Valid @ModelAttribute("webuserForm") WebUserForm webuserForm, - BindingResult result, Model model) { + @PostMapping(params = "backToOverview", value = API_PASSWORD_PATH) + public String apiPasswordBackToOverview(@Valid @ModelAttribute("webuserForm") WebUserForm webuserForm) { return toDetails(webuserForm.getWebUserPk()); } - @RequestMapping(params = "backToOverview", value = ADD_PATH, method = RequestMethod.POST) + @PostMapping(params = "backToOverview", value = ADD_PATH) public String addBackToOverview() { return toOverview(); } - @RequestMapping(params = "backToOverview", value = UPDATE_PATH, method = RequestMethod.POST) + @PostMapping(params = "backToOverview", value = UPDATE_PATH) public String updateBackToOverview() { return toOverview(); } - private String toOverview() { + private static String toOverview() { return "redirect:/manager/webusers"; } - private String toDetails(Integer userPk) { - String redirect_str = String.format("redirect:/manager/webusers/details/%s", userPk); - return redirect_str; + private static String toDetails(Integer userPk) { + return String.format("redirect:/manager/webusers/details/%s", userPk); } } diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/WebUserAuthority.java b/src/main/java/de/rwth/idsg/steve/web/dto/WebUserAuthority.java index 0682a4c6b..ff99a84a0 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/WebUserAuthority.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/WebUserAuthority.java @@ -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 @@ -18,35 +18,54 @@ */ package de.rwth.idsg.steve.web.dto; -import org.jooq.JSON; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Getter; -import lombok.RequiredArgsConstructor; +import org.jooq.JSON; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; -@RequiredArgsConstructor public enum WebUserAuthority { - USER(JSON.json("[\"USER\"]"), "USER"), - ADMIN(JSON.json("[\"ADMIN\"]"), "ADMIN"), - USER_ADMIN(JSON.json("[\"ADMIN\",\"USER\"]"), "USER, ADMIN"); + USER("USER"), + ADMIN("ADMIN"), + USER_ADMIN("USER", "ADMIN"); - @Getter private final JSON jsonValue; - @Getter private final String value; + private static final ObjectMapper MAPPER = new ObjectMapper(); - public static WebUserAuthority fromJsonValue(JSON v) { - for (WebUserAuthority c: WebUserAuthority.values()) { - if (c.jsonValue.equals(v)) { - return c; - } + private final Set values; + @Getter + private final JSON jsonValue; + + WebUserAuthority(String... values) { + if (values == null || values.length == 0) { + throw new IllegalArgumentException("JSON values must not be null or empty"); } - throw new IllegalArgumentException(v.toString()); + this.values = new HashSet<>(Arrays.asList(values)); + this.jsonValue = this.values.stream().map(v -> "\"" + v + "\"") + .reduce((a, b) -> a + ", " + b) + .map(s -> JSON.json("[" + s + "]")) + .orElseThrow(() -> new IllegalArgumentException("Failed to create JSON value")); } - public static WebUserAuthority fromValue(String v) { - for (WebUserAuthority c: WebUserAuthority.values()) { - if (c.value.equals(v)) { - return c; - } + // For jsp + public String getValue() { + return String.join(", ", values); + } + + public static WebUserAuthority fromJsonValue(JSON v) { + try { + List values = Arrays.asList(MAPPER.readValue(v.data(), String[].class)); + for (WebUserAuthority c: WebUserAuthority.values()) { + if (c.values.containsAll(values)) { + return c; + } + } + throw new IllegalArgumentException(v.toString()); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(v.toString()); } - throw new IllegalArgumentException(v); } } diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/WebUserBaseForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/WebUserBaseForm.java index e1330cec8..7fbaa8bb8 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/WebUserBaseForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/WebUserBaseForm.java @@ -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 diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/WebUserForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/WebUserForm.java index d438a7f5e..3a7653712 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/WebUserForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/WebUserForm.java @@ -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 @@ -22,6 +22,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Getter; +import lombok.Setter; /** @@ -31,6 +32,7 @@ @Getter public class WebUserForm extends WebUserBaseForm { + @Setter @NotNull(message = "Password is required") @Size(min = 8, message = "Password requires 8 or more characters") private String password = ""; @@ -42,21 +44,15 @@ public class WebUserForm extends WebUserBaseForm { @AssertFalse(message = "The repeated password did not match!") private Boolean pwError; + @Setter private String apiPassword = ""; - public void setPassword(String password) { - this.password = password; - } - public void setPasswordComparison(String passwordComparison) { - this.pwError = true; this.passwordComparison = passwordComparison; - if (passwordComparison != null) { + if (passwordComparison == null) { + this.pwError = true; + } else { this.pwError = !passwordComparison.equals(this.password); } } - - public void setApiPassword(String apiPassword) { - this.apiPassword = apiPassword; - } } diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/WebUserQueryForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/WebUserQueryForm.java index 201af530b..2f1ba5548 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/WebUserQueryForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/WebUserQueryForm.java @@ -1,9 +1,6 @@ /* * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2023 SteVe Community Team -/* - * 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 @@ -40,19 +37,14 @@ public class WebUserQueryForm { private String apiToken; public boolean isSetWebUsername() { - return webUsername != null; + return webUsername != null && !webUsername.isBlank(); } public boolean isSetRoles() { - return roles != null; + return roles != null && !roles.isBlank(); } public boolean isSetEnabled() { return enabled != null; } - - public boolean isSetApiKey() { - return apiToken != null; - } - } diff --git a/src/main/resources/webapp/WEB-INF/views/data-man/webuserAdd.jsp b/src/main/resources/webapp/WEB-INF/views/data-man/webuserAdd.jsp index 63f4b1c5b..5fe857a48 100644 --- a/src/main/resources/webapp/WEB-INF/views/data-man/webuserAdd.jsp +++ b/src/main/resources/webapp/WEB-INF/views/data-man/webuserAdd.jsp @@ -1,7 +1,7 @@ <%-- 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 @@ -19,17 +19,12 @@ --%> <%@ include file="../00-header.jsp" %> -
Error while trying to add a webuser:
    -
  • ${error.defaultMessage}
  • +
@@ -45,19 +40,16 @@ Password confirmation: Roles: -
-
+ + + + + Enabled:true - - @@ -66,4 +58,4 @@ -<%@ include file="../00-footer.jsp" %> \ No newline at end of file +<%@ include file="../00-footer.jsp" %> diff --git a/src/main/resources/webapp/WEB-INF/views/data-man/webuserApiPassword.jsp b/src/main/resources/webapp/WEB-INF/views/data-man/webuserApiPassword.jsp index 676ee612e..957e84a61 100644 --- a/src/main/resources/webapp/WEB-INF/views/data-man/webuserApiPassword.jsp +++ b/src/main/resources/webapp/WEB-INF/views/data-man/webuserApiPassword.jsp @@ -1,7 +1,7 @@ <%-- 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 @@ -31,7 +31,7 @@
Webuser change password
- + @@ -52,4 +52,4 @@
Webuser
-<%@ include file="../00-footer.jsp" %> \ No newline at end of file +<%@ include file="../00-footer.jsp" %> diff --git a/src/main/resources/webapp/WEB-INF/views/data-man/webuserDetails.jsp b/src/main/resources/webapp/WEB-INF/views/data-man/webuserDetails.jsp index 45e1aa811..03bf4ad11 100644 --- a/src/main/resources/webapp/WEB-INF/views/data-man/webuserDetails.jsp +++ b/src/main/resources/webapp/WEB-INF/views/data-man/webuserDetails.jsp @@ -1,7 +1,7 @@ <%-- 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 @@ -21,23 +21,23 @@ <%@ include file="../00-header.jsp" %>
- Error while trying to update a charge point: + Error while trying to update a webuser:
    -
  • ${error.defaultMessage}
  • +
Webuser Details
- + - - - + + + diff --git a/src/main/resources/webapp/WEB-INF/views/data-man/webuserPassword.jsp b/src/main/resources/webapp/WEB-INF/views/data-man/webuserPassword.jsp index 33a082a73..75d5e31af 100644 --- a/src/main/resources/webapp/WEB-INF/views/data-man/webuserPassword.jsp +++ b/src/main/resources/webapp/WEB-INF/views/data-man/webuserPassword.jsp @@ -1,7 +1,7 @@ <%-- 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 @@ -31,9 +31,9 @@
Webuser change password
- +
Webuser
Username:${webuserForm.webUsername} - - +
Username: + +
@@ -51,27 +51,27 @@
Roles: - +
Enabled: - + + + +
- - + +
- +
Webuser
Webuser
Webusername:${webuserForm.webUsername} @@ -51,4 +51,4 @@
-<%@ include file="../00-footer.jsp" %> \ No newline at end of file +<%@ include file="../00-footer.jsp" %> diff --git a/src/main/resources/webapp/WEB-INF/views/data-man/webusers.jsp b/src/main/resources/webapp/WEB-INF/views/data-man/webusers.jsp index 7cb03ce34..be4c04910 100644 --- a/src/main/resources/webapp/WEB-INF/views/data-man/webusers.jsp +++ b/src/main/resources/webapp/WEB-INF/views/data-man/webusers.jsp @@ -1,7 +1,7 @@ <%-- 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 @@ -57,8 +57,8 @@ - ${cr.webUsername} - "${cr.authorities.value}" + + ${cr.authorities.value} ${cr.enabled} @@ -70,4 +70,4 @@ -<%@ include file="../00-footer.jsp" %> \ No newline at end of file +<%@ include file="../00-footer.jsp" %> diff --git a/src/main/resources/webapp/WEB-INF/views/noAccess.jsp b/src/main/resources/webapp/WEB-INF/views/noAccess.jsp index 397bcbbac..aa35b582e 100644 --- a/src/main/resources/webapp/WEB-INF/views/noAccess.jsp +++ b/src/main/resources/webapp/WEB-INF/views/noAccess.jsp @@ -1,7 +1,7 @@ <%-- 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 @@ -18,7 +18,6 @@ along with this program. If not, see . --%> - <%@ include file="00-header.jsp" %> <%@ include file="00-context.jsp" %> @@ -28,14 +27,12 @@ Access denied for the requested page! For further information ask your administrator. info_image - Some information and configuration pages are only accessable for administrators. + Some information and configuration pages are only accessible for administrators. -
- -
+ Home -<%@ include file="00-footer.jsp" %> \ No newline at end of file +<%@ include file="00-footer.jsp" %>