Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
10 changes: 10 additions & 0 deletions application-engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,16 @@
<artifactId>jackson-module-jsonSchema</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.netgrif.application.engine.auth.service;

import com.netgrif.application.engine.auth.service.interfaces.IAuthorizationService;

import java.util.Arrays;

import com.netgrif.application.engine.objects.auth.domain.LoggedUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
Expand All @@ -18,4 +21,12 @@ public boolean hasAuthority(String authority) {
LoggedUser loggedUser = userService.getLoggedUserFromContext();
return loggedUser.getAuthoritySet().stream().anyMatch(it -> it.getAuthority().equals(authority));
}

@Override
public boolean hasAnyAuthority(String... authority) {
// TODO: impersonation
// LoggedUser loggedUser = userService.getLoggedUserFromContext().getSelfOrImpersonated();
LoggedUser loggedUser = userService.getLoggedUserFromContext();
return loggedUser.getAuthoritySet().stream().anyMatch(it -> Arrays.stream(authority).anyMatch(a -> it.getAuthority().equals(a)));
}
Comment on lines +25 to +31
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider optimizing authority lookup with a Set.

The current implementation recreates Arrays.stream(authority) for each authority in the user's set, resulting in O(n×m) complexity. Converting the varargs to a Set first would improve this to O(n) with O(1) lookups.

Also, consider renaming the parameter to authorities (plural) for clarity with varargs.

♻️ Proposed optimization
 `@Override`
 public boolean hasAnyAuthority(String... authority) {
     // TODO: impersonation
 //        LoggedUser loggedUser = userService.getLoggedUserFromContext().getSelfOrImpersonated();
     LoggedUser loggedUser = userService.getLoggedUserFromContext();
-    return loggedUser.getAuthoritySet().stream().anyMatch(it -> Arrays.stream(authority).anyMatch(a -> it.getAuthority().equals(a)));
+    Set<String> authoritySet = Set.of(authority);
+    return loggedUser.getAuthoritySet().stream().anyMatch(it -> authoritySet.contains(it.getAuthority()));
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public boolean hasAnyAuthority(String... authority) {
// TODO: impersonation
// LoggedUser loggedUser = userService.getLoggedUserFromContext().getSelfOrImpersonated();
LoggedUser loggedUser = userService.getLoggedUserFromContext();
return loggedUser.getAuthoritySet().stream().anyMatch(it -> Arrays.stream(authority).anyMatch(a -> it.getAuthority().equals(a)));
}
`@Override`
public boolean hasAnyAuthority(String... authority) {
// TODO: impersonation
// LoggedUser loggedUser = userService.getLoggedUserFromContext().getSelfOrImpersonated();
LoggedUser loggedUser = userService.getLoggedUserFromContext();
Set<String> authoritySet = Set.of(authority);
return loggedUser.getAuthoritySet().stream().anyMatch(it -> authoritySet.contains(it.getAuthority()));
}
🤖 Prompt for AI Agents
In
`@application-engine/src/main/java/com/netgrif/application/engine/auth/service/AuthorizationService.java`
around lines 25 - 31, In AuthorizationService.hasAnyAuthority, convert the
incoming varargs (rename parameter from authority to authorities) into a
Set<String> once, then check membership against the logged user's authorities
using a contains lookup to avoid the nested Arrays.stream per-item scan;
specifically, build the Set from the authorities parameter and replace the
Arrays.stream(...).anyMatch(...) logic with a single pass that tests
loggedUser.getAuthoritySet() entries' getAuthority() against the Set (use
getLoggedUserFromContext() as currently used or restore impersonation call if
needed).

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

public interface IAuthorizationService {
boolean hasAuthority(String authority);

boolean hasAnyAuthority(String... authority);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.netgrif.application.engine.objects.auth.domain.AbstractUser;
import com.netgrif.application.engine.configuration.properties.SecurityConfigurationProperties;
import com.netgrif.application.engine.objects.auth.domain.ActorTransformer;
import com.netgrif.application.engine.workflow.web.responsebodies.MessageResource;
import com.netgrif.application.engine.objects.auth.domain.LoggedUser;
import com.netgrif.application.engine.auth.service.InvalidUserTokenException;
Expand Down Expand Up @@ -131,8 +132,8 @@ public MessageResource verifyToken(@RequestBody String token) {

@Operation(summary = "Verify validity of an authentication token")
@GetMapping(value = "/verify", produces = MediaTypes.HAL_JSON_VALUE)
public MessageResource verifyAuthToken(Authentication auth) {
LoggedUser loggedUser = (LoggedUser) auth.getPrincipal();
public MessageResource verifyAuthToken() {
LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedUser());
return MessageResource.successMessage("Auth Token successfully verified, for user [" + loggedUser.getId() + "] " + loggedUser.getName());
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.netgrif.application.engine.auth.web;
package com.netgrif.application.engine.auth.web;

import com.netgrif.application.engine.adapter.spring.common.web.responsebodies.ResponseMessage;
import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService;
Expand All @@ -8,10 +8,7 @@
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.auth.domain.*;
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 @@ -30,6 +27,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.util.*;
Expand Down Expand Up @@ -112,10 +110,10 @@ public ResponseEntity<Page<User>> getAllUsers(@PathVariable String realmId, Page
})
@GetMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getLoggedUser(Authentication auth, Locale locale) {
LoggedUser loggedUser = (LoggedUser) auth.getPrincipal();
LoggedUser loggedUser = resolveAuthenticationToken(auth);
AbstractUser user;
try {
user = userService.findById(loggedUser.getStringId(), loggedUser.getRealmId());
user = resolveLoggedUser(loggedUser);
if (user == null) {
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED).build();
Expand Down Expand Up @@ -294,11 +292,14 @@ public ResponseEntity<ResponseMessage> assignAuthorityToUser(@PathVariable("real
})
@GetMapping(value = "/preferences", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PreferencesResource> preferences(Authentication auth) {
String userId = ((LoggedUser) auth.getPrincipal()).getStringId();
Preferences preferences = preferencesService.get(userId);
LoggedUser loggedUser = resolveAuthenticationToken(auth);
if (loggedUser == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
Preferences preferences = preferencesService.get(loggedUser.getStringId());

if (preferences == null) {
preferences = new com.netgrif.application.engine.adapter.spring.preferences.Preferences(userId);
preferences = new com.netgrif.application.engine.adapter.spring.preferences.Preferences(loggedUser.getStringId());
}
PreferencesResource preferencesResource = PreferencesResource.withPreferences(preferences);

Expand Down Expand Up @@ -337,4 +338,25 @@ private boolean realmExists(String realmId) {
Optional<Realm> realm = realmService.getRealmById(realmId);
return realm.isPresent();
}

private LoggedUser resolveAuthenticationToken(Authentication auth) {
if (auth != null) {
return (LoggedUser) auth.getPrincipal();
}
auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
return (LoggedUser) auth.getPrincipal();
}
return null;
}
Comment on lines +342 to +351
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check all calls to resolveAuthenticationToken and their context
rg -n "resolveAuthenticationToken" --type java -B 5 -A 5

Repository: netgrif/application-engine

Length of output: 4910


Remove redundant fallback to SecurityContextHolder.

Both usages of resolveAuthenticationToken() are in controller methods (getLoggedUser at line 113 and preferences at line 295) where Authentication is auto-injected by Spring. Based on established patterns in this codebase, the fallback to SecurityContextHolder on lines 346-349 is unnecessary—the injected Authentication parameter will always be populated, and auth.getPrincipal() always returns a valid LoggedUser instance. Simplify the method to remove the defensive fallback logic.

🤖 Prompt for AI Agents
In
`@application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java`
around lines 342 - 351, The method resolveAuthenticationToken currently falls
back to SecurityContextHolder; remove that redundant fallback and simplify it to
just return the principal from the provided Authentication parameter (cast to
LoggedUser) or null if the injected auth is null. Update
resolveAuthenticationToken(Authentication auth) to only check auth and return
(LoggedUser) auth.getPrincipal() when non-null; remove any references to
SecurityContextHolder and its getContext() usage. This keeps callers like
getLoggedUser and preferences relying on the injected Authentication and removes
unnecessary defensive logic.


private AbstractUser resolveLoggedUser(LoggedUser loggedUser) {
if (loggedUser == null) {
return null;
}
if (loggedUser.isAnonymous()) {
return ActorTransformer.toUser(loggedUser);
}
return userService.findById(loggedUser.getStringId(), loggedUser.getRealmId());
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package com.netgrif.application.engine.configuration;

import com.netgrif.application.engine.auth.service.DefaultLoggedUserFactory;
import com.netgrif.application.engine.objects.auth.domain.ActorTransformer;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class LoggedUserConfiguration {

private final ActorTransformer.LoggedUserFactory loggedUserFactory;
private final ActorTransformer.UserFactory userFactory;

@PostConstruct
public void initializeLoggedUserFactory() {
ActorTransformer.setLoggedUserFactory(new DefaultLoggedUserFactory());
ActorTransformer.setLoggedUserFactory(loggedUserFactory);
ActorTransformer.setUserFactory(userFactory);
}
}
Loading
Loading