Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.netgrif.application.engine.auth.service;

import com.netgrif.application.engine.objects.auth.domain.AbstractUser;
import com.netgrif.application.engine.objects.auth.domain.LoggedUser;
import com.netgrif.application.engine.auth.service.interfaces.IUserResourceHelperService;
import com.netgrif.application.engine.auth.web.responsebodies.User;
import com.netgrif.application.engine.auth.web.responsebodies.UserResource;
import com.netgrif.application.engine.impersonation.service.interfaces.IImpersonationService;
import com.netgrif.application.engine.objects.auth.domain.AbstractUser;
import com.netgrif.application.engine.objects.auth.domain.LoggedUser;
import com.netgrif.application.engine.objects.dto.response.user.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
Expand All @@ -32,20 +32,17 @@ public UserResource getResource(LoggedUser loggedUser, Locale locale, boolean sm
// User result = loggedUser.isImpersonating() ?
// getLocalisedUser(user, getImpersonated(loggedUser, small), locale) :
// getLocalisedUser(user, locale);
User result = getLocalisedUser(user, locale);
UserDto result = getLocalisedUser(user, locale);
return new UserResource(result, "profile");
}

@Override
public User getLocalisedUser(AbstractUser user, AbstractUser impersonated, Locale locale) {
User localisedUser = getLocalisedUser(user, locale);
User impersonatedUser = userFactory.getUser(impersonated, locale);
localisedUser.setImpersonated(impersonatedUser);
return localisedUser;
public UserDto getLocalisedUser(AbstractUser user, AbstractUser impersonated, Locale locale) {
return userFactory.getUserWithImpersonation(user, impersonated, locale);
}

@Override
public User getLocalisedUser(AbstractUser user, Locale locale) {
public UserDto getLocalisedUser(AbstractUser user, Locale locale) {
return userFactory.getUser(user, locale);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.netgrif.application.engine.auth.service.interfaces;

import com.netgrif.application.engine.auth.web.responsebodies.User;
import com.netgrif.application.engine.objects.dto.response.user.UserDto;
import com.netgrif.application.engine.auth.web.responsebodies.UserResource;
import com.netgrif.application.engine.objects.auth.domain.AbstractUser;
import com.netgrif.application.engine.objects.auth.domain.LoggedUser;
Expand All @@ -10,7 +10,7 @@
public interface IUserResourceHelperService {
UserResource getResource(LoggedUser loggedUser, Locale locale, boolean small);

User getLocalisedUser(AbstractUser user, AbstractUser impersonated, Locale locale);
UserDto getLocalisedUser(AbstractUser user, AbstractUser impersonated, Locale locale);

User getLocalisedUser(AbstractUser user, Locale locale);
UserDto getLocalisedUser(AbstractUser user, Locale locale);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import com.netgrif.application.engine.auth.service.PreferencesService;
import com.netgrif.application.engine.auth.service.UserService;
import com.netgrif.application.engine.auth.web.requestbodies.PreferencesRequest;
import com.netgrif.application.engine.auth.web.requestbodies.UserSearchRequestBody;
import com.netgrif.application.engine.auth.web.responsebodies.PreferencesResource;
import com.netgrif.application.engine.auth.web.responsebodies.User;
import com.netgrif.application.engine.objects.auth.domain.AbstractUser;
import com.netgrif.application.engine.objects.auth.domain.LoggedUser;
import com.netgrif.application.engine.objects.dto.PreferencesDto;
import com.netgrif.application.engine.objects.dto.request.user.UserSearchRequestBody;
import com.netgrif.application.engine.objects.dto.response.user.UserDto;
import com.netgrif.application.engine.objects.preferences.Preferences;
import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -28,6 +28,7 @@
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Locale;

@Slf4j
@RestController
Expand All @@ -53,7 +54,7 @@ public class PublicUserController {
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@GetMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getLoggedUser(Authentication auth) {
public ResponseEntity<UserDto> getLoggedUser(Authentication auth) {
LoggedUser loggedUser = (LoggedUser) auth.getPrincipal();
AbstractUser user;
try {
Expand All @@ -67,7 +68,7 @@ public ResponseEntity<User> getLoggedUser(Authentication auth) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}

return ResponseEntity.ok(User.createUser(user));
return ResponseEntity.ok(UserDto.fromAbstractUser(user));
}

@ApiResponses(value = {
Expand All @@ -77,12 +78,10 @@ public ResponseEntity<User> getLoggedUser(Authentication auth) {
})
@Operation(summary = "Generic user search", security = {@SecurityRequirement(name = "X-Auth-Token")})
@PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Page<User>> search(@RequestBody UserSearchRequestBody query, Pageable pageable, Authentication auth) {
List<ProcessResourceId> roles = query.getRoles() == null ? null : query.getRoles().stream().map(ProcessResourceId::new).toList();
List<ProcessResourceId> negativeRoles = query.getNegativeRoles() == null ? null : query.getNegativeRoles().stream().map(ProcessResourceId::new).toList();
Page<AbstractUser> users = userService.searchAllCoMembers(query.getFulltext(),
public ResponseEntity<Page<UserDto>> search(@RequestBody UserSearchRequestBody query, Pageable pageable, Authentication auth, Locale locale) {
List<ProcessResourceId> roles = query.roles() == null ? null : query.roles().stream().map(ProcessResourceId::new).toList();
Page<AbstractUser> users = userService.searchAllCoMembers(query.fulltext(),
roles,
negativeRoles,
(LoggedUser) auth.getPrincipal(), pageable);
return ResponseEntity.ok(changeToResponse(users, pageable));
}
Expand All @@ -101,7 +100,7 @@ public ResponseEntity<PreferencesResource> preferences(Authentication auth) {
if (preferences == null) {
preferences = new com.netgrif.application.engine.adapter.spring.preferences.Preferences(userId);
}
PreferencesResource preferencesResource = PreferencesResource.withPreferences(preferences);
PreferencesResource preferencesResource = PreferencesResource.withPreferences(PreferencesDto.fromPreferences(preferences));

return ResponseEntity.ok(preferencesResource);
}
Expand All @@ -115,24 +114,24 @@ public ResponseEntity<PreferencesResource> preferences(Authentication auth) {
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@PostMapping(value = "/preferences", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> savePreferences(@RequestBody PreferencesRequest preferences, Authentication auth) {
public ResponseEntity<String> savePreferences(@RequestBody PreferencesDto preferences, Authentication auth) {
try {
String userId = ((LoggedUser) auth.getPrincipal()).getStringId();
preferences.setUserId(userId);
preferencesService.save(preferences.toPreferences());
Preferences domainPreferences = com.netgrif.application.engine.adapter.spring.preferences.Preferences.fromDto(preferences, userId);
preferencesService.save(domainPreferences);
return ResponseEntity.ok("User preferences saved");
} catch (Exception e) {
log.error("Saving user preferences failed", e);
return ResponseEntity.badRequest().body("Saving user preferences failed");
}
}

private Page<User> changeToResponse(Page<AbstractUser> users, Pageable pageable) {
private Page<UserDto> changeToResponse(Page<AbstractUser> users, Pageable pageable) {
return new PageImpl<>(changeType(users.getContent()), pageable, users.getTotalElements());
}

public List<User> changeType(List<AbstractUser> users) {
return users.stream().map(User::createUser).toList();
public List<UserDto> changeType(List<AbstractUser> users) {
return users.stream().map(UserDto::fromAbstractUser).toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import com.netgrif.application.engine.adapter.spring.common.web.responsebodies.ResponseMessage;
import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService;
import com.netgrif.application.engine.auth.service.*;
import com.netgrif.application.engine.auth.web.requestbodies.PreferencesRequest;
import com.netgrif.application.engine.auth.web.requestbodies.UserCreateRequest;
import com.netgrif.application.engine.auth.web.requestbodies.UserSearchRequestBody;
import com.netgrif.application.engine.auth.web.responsebodies.PreferencesResource;
import com.netgrif.application.engine.auth.web.responsebodies.User;
import com.netgrif.application.engine.objects.auth.domain.AbstractUser;
import com.netgrif.application.engine.objects.auth.domain.Authority;
import com.netgrif.application.engine.objects.auth.domain.LoggedUser;
import com.netgrif.application.engine.objects.auth.domain.Realm;
import com.netgrif.application.engine.objects.dto.PreferencesDto;
import com.netgrif.application.engine.objects.dto.request.user.UserCreateRequestDto;
import com.netgrif.application.engine.objects.dto.request.user.UserSearchRequestBody;
import com.netgrif.application.engine.objects.dto.response.authority.AuthorityDto;
import com.netgrif.application.engine.objects.dto.response.user.UserDto;
import com.netgrif.application.engine.objects.preferences.Preferences;
import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId;
import io.swagger.v3.oas.annotations.Operation;
Expand Down Expand Up @@ -62,25 +62,25 @@ public class UserController {
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@PostMapping("/{realmId}")
public ResponseEntity<User> createUser(@PathVariable String realmId, @RequestBody UserCreateRequest request, Locale locale) {
public ResponseEntity<UserDto> createUser(@PathVariable String realmId, @RequestBody UserCreateRequestDto request, Locale locale) {
try {
if (!realmExists(realmId)) {
log.error("Realm with id [{}] not found", realmId);
return ResponseEntity.badRequest().build();
}
if (userService.findUserByUsername(request.getUsername(), realmId).isPresent()) {
log.error("User with username [{}] already exists in realm [{}]", request.getUsername(), realmId);
if (userService.findUserByUsername(request.username(), realmId).isPresent()) {
log.error("User with username [{}] already exists in realm [{}]", request.username(), realmId);
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
AbstractUser user = userService.createUser(
request.getUsername(),
request.getEmail(),
request.getFirstName(),
request.getLastName(),
request.getPassword(),
request.username(),
request.email(),
request.firstName(),
request.lastName(),
request.password(),
realmId
);
log.info("New user with username [{}] has been created in realm [{}]", request.getUsername(), realmId);
log.info("New user with username [{}] has been created in realm [{}]", request.username(), realmId);
return ResponseEntity.status(HttpStatus.CREATED).body(userFactory.getUser(user, locale));
} catch (Exception e) {
log.error("Failed to create user", e);
Expand All @@ -95,7 +95,7 @@ public ResponseEntity<User> createUser(@PathVariable String realmId, @RequestBod
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@GetMapping("/{realmId}/all")
public ResponseEntity<Page<User>> getAllUsers(@PathVariable String realmId, Pageable pageable, Locale locale) {
public ResponseEntity<Page<UserDto>> getAllUsers(@PathVariable String realmId, Pageable pageable, Locale locale) {
if (!realmExists(realmId)) {
log.error("Realm with id [{}] not found", realmId);
return ResponseEntity.badRequest().build();
Expand All @@ -111,7 +111,7 @@ public ResponseEntity<Page<User>> getAllUsers(@PathVariable String realmId, Page
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@GetMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getLoggedUser(Authentication auth, Locale locale) {
public ResponseEntity<UserDto> getLoggedUser(Authentication auth, Locale locale) {
LoggedUser loggedUser = (LoggedUser) auth.getPrincipal();
AbstractUser user;
try {
Expand All @@ -135,12 +135,10 @@ public ResponseEntity<User> getLoggedUser(Authentication auth, Locale locale) {
})
@Operation(summary = "Generic user search", security = {@SecurityRequirement(name = "X-Auth-Token")})
@PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Page<User>> search(@RequestBody UserSearchRequestBody query, Pageable pageable, Authentication auth, Locale locale) {
List<ProcessResourceId> roles = query.getRoles() == null ? null : query.getRoles().stream().map(ProcessResourceId::new).toList();
List<ProcessResourceId> negativeRoles = query.getNegativeRoles() == null ? null : query.getNegativeRoles().stream().map(ProcessResourceId::new).toList();
Page<AbstractUser> users = userService.searchAllCoMembers(query.getFulltext(),
public ResponseEntity<Page<UserDto>> search(@RequestBody UserSearchRequestBody query, Pageable pageable, Authentication auth, Locale locale) {
List<ProcessResourceId> roles = query.roles() == null ? null : query.roles().stream().map(ProcessResourceId::new).toList();
Page<AbstractUser> users = userService.searchAllCoMembers(query.fulltext(),
roles,
negativeRoles,
(LoggedUser) auth.getPrincipal(), pageable);
return ResponseEntity.ok(changeToResponse(users, pageable, locale));
}
Expand All @@ -153,7 +151,7 @@ public ResponseEntity<Page<User>> search(@RequestBody UserSearchRequestBody quer
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@GetMapping(value = "/{realmId}/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getUser(@PathVariable("realmId") String realmId, @PathVariable("id") String userId, Locale locale) {
public ResponseEntity<UserDto> getUser(@PathVariable("realmId") String realmId, @PathVariable("id") String userId, Locale locale) {
LoggedUser actualUser = userService.getLoggedUserFromContext();
// TODO: impersonation
// LoggedUser loggedUser = actualUser.getSelfOrImpersonated();
Expand Down Expand Up @@ -261,8 +259,8 @@ public ResponseEntity<ResponseMessage> assignRolesToUser(@PathVariable("realmId"
@ApiResponse(responseCode = "403", description = "Caller doesn't fulfill the authorisation requirements"),
@ApiResponse(responseCode = "500", description = "Internal server error")
})
public ResponseEntity<List<Authority>> getAllAuthorities() {
return ResponseEntity.ok(authorityService.findAll(Pageable.unpaged()).stream().toList());
public ResponseEntity<List<AuthorityDto>> getAllAuthorities() {
return ResponseEntity.ok(authorityService.findAll(Pageable.unpaged()).stream().map(AuthorityDto::fromAuthority).toList());
}

@PreAuthorize("@authorizationService.hasAuthority('ADMIN')")
Expand Down Expand Up @@ -300,7 +298,7 @@ public ResponseEntity<PreferencesResource> preferences(Authentication auth) {
if (preferences == null) {
preferences = new com.netgrif.application.engine.adapter.spring.preferences.Preferences(userId);
}
PreferencesResource preferencesResource = PreferencesResource.withPreferences(preferences);
PreferencesResource preferencesResource = PreferencesResource.withPreferences(PreferencesDto.fromPreferences(preferences));

return ResponseEntity.ok(preferencesResource);
}
Expand All @@ -313,23 +311,23 @@ public ResponseEntity<PreferencesResource> preferences(Authentication auth) {
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@PostMapping(value = "/preferences", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResponseMessage> savePreferences(@RequestBody PreferencesRequest preferences, Authentication auth) {
public ResponseEntity<ResponseMessage> savePreferences(@RequestBody PreferencesDto preferences, Authentication auth) {
try {
String userId = ((LoggedUser) auth.getPrincipal()).getStringId();
preferences.setUserId(userId);
preferencesService.save(preferences.toPreferences());
Preferences domainPreferences = com.netgrif.application.engine.adapter.spring.preferences.Preferences.fromDto(preferences, userId);
preferencesService.save(domainPreferences);
return ResponseEntity.ok(ResponseMessage.createSuccessMessage("User preferences saved"));
} catch (Exception e) {
log.error("Saving user preferences failed", e);
return ResponseEntity.badRequest().body(ResponseMessage.createErrorMessage("Saving user preferences failed"));
}
Comment on lines +314 to 323
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate PreferencesDto before persisting

PreferencesDto now reaches the service layer directly, but without @Valid on the controller parameter any field-level constraints on the DTO (e.g., @NotBlank, size limits) are skipped. That allows malformed preference data to be saved or to blow up deeper in Preferences.fromDto. Add @Valid to keep the bean validation contract intact.

-    public ResponseEntity<ResponseMessage> savePreferences(@RequestBody PreferencesDto preferences, Authentication auth) {
+    public ResponseEntity<ResponseMessage> savePreferences(@Valid @RequestBody PreferencesDto preferences, Authentication auth) {

(Requires jakarta.validation.Valid import if not already added.)

🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java
around lines 314 to 323, the controller method parameter for PreferencesDto is
not annotated with @Valid so bean validation constraints on the DTO are skipped;
update the method signature to annotate the preferences parameter with
jakarta.validation.Valid and add the required import (jakarta.validation.Valid)
so Spring triggers validation before calling Preferences.fromDto, optionally
handle MethodArgumentNotValidException globally or add a BindingResult parameter
if you want to return field-level errors.

}

private Page<User> changeToResponse(Page<AbstractUser> users, Pageable pageable, Locale locale) {
private Page<UserDto> changeToResponse(Page<AbstractUser> users, Pageable pageable, Locale locale) {
return new PageImpl<>(changeType(users.getContent(), locale), pageable, users.getTotalElements());
}

public List<User> changeType(List<AbstractUser> users, Locale locale) {
public List<UserDto> changeType(List<AbstractUser> users, Locale locale) {
return users.stream().map(u -> userFactory.getUser(u, locale)).toList();
}

Expand Down
Loading
Loading