Skip to content

Commit 43db3a7

Browse files
committed
Always provide context for UserRestrictionException
1 parent 3775d2f commit 43db3a7

8 files changed

Lines changed: 50 additions & 65 deletions

server/src/main/java/invite/api/APITokenController.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@
1818
import org.springframework.transaction.annotation.Transactional;
1919
import org.springframework.util.StringUtils;
2020
import org.springframework.validation.annotation.Validated;
21-
import org.springframework.web.bind.annotation.*;
21+
import org.springframework.web.bind.annotation.DeleteMapping;
22+
import org.springframework.web.bind.annotation.GetMapping;
23+
import org.springframework.web.bind.annotation.PathVariable;
24+
import org.springframework.web.bind.annotation.PostMapping;
25+
import org.springframework.web.bind.annotation.RequestBody;
26+
import org.springframework.web.bind.annotation.RequestMapping;
27+
import org.springframework.web.bind.annotation.RestController;
2228

2329
import java.util.List;
2430
import java.util.Map;
@@ -73,10 +79,10 @@ public ResponseEntity<APIToken> create(@Validated @RequestBody APIToken apiToken
7379
UserPermissions.assertAuthority(user, Authority.INVITER);
7480
String token = (String) request.getSession().getAttribute(TOKEN_KEY);
7581
if (!StringUtils.hasText(token)) {
76-
throw new UserRestrictionException();
82+
throw new UserRestrictionException("Token is NULL");
7783
}
7884
if (user.isSuperUser() && !StringUtils.hasText(apiTokenRequest.getOrganizationGUID())) {
79-
throw new UserRestrictionException();
85+
throw new UserRestrictionException("Super user must specify API token OrganizationGUID");
8086
}
8187
APIToken apiToken;
8288
if (user.isSuperUser() || user.isInstitutionAdmin()) {
@@ -96,16 +102,17 @@ public ResponseEntity<APIToken> create(@Validated @RequestBody APIToken apiToken
96102
@DeleteMapping("/{id}")
97103
public ResponseEntity<Void> deleteToken(@PathVariable("id") Long id, @Parameter(hidden = true) User user) {
98104
LOG.debug(String.format("DELETE /tokens/deleteToken with id %s for user %s", id.toString(), user.getEduPersonPrincipalName()));
105+
99106
UserPermissions.assertAuthority(user, Authority.INVITER);
100107
APIToken apiToken = apiTokenRepository.findById(id).orElseThrow(() -> new NotFoundException("API token not found"));
101108
if (apiToken.isSuperUserToken() && !user.isSuperUser()) {
102-
throw new UserRestrictionException();
109+
throw new UserRestrictionException("Non super-user not allowed to delete super-user token: " + user.getEmail());
103110
}
104111
if (user.isInstitutionAdmin() && !apiToken.getOrganizationGUID().equals(user.getOrganizationGUID())) {
105-
throw new UserRestrictionException();
112+
throw new UserRestrictionException("User not allowed to delete token for different organization: " + user.getEmail());
106113
}
107114
if (!user.isSuperUser() && !user.isInstitutionAdmin() && !Objects.equals(user.getId(), apiToken.getOwner().getId())) {
108-
throw new UserRestrictionException();
115+
throw new UserRestrictionException("User not allowed to delete token for different owner: " + user.getEmail());
109116
}
110117
apiTokenRepository.delete(apiToken);
111118
return Results.deleteResult();

server/src/main/java/invite/api/RoleController.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public ResponseEntity<List<Role>> rolesPerApplicationId(@PathVariable("manageId"
179179
.collect(Collectors.toSet());
180180
applicationManageIdentifiers.addAll(roleManageIdentifiers);
181181
if (!applicationManageIdentifiers.contains(manageId)) {
182-
throw new UserRestrictionException();
182+
throw new UserRestrictionException(String.format("User %s has no access to manageID %s", user.getEmail(), manageId));
183183
}
184184
roles = roleRepository.findByOrganizationGUIDAndApplicationUsagesApplicationManageId(user.getOrganizationGUID(), manageId);
185185
}
@@ -194,7 +194,7 @@ public ResponseEntity<Role> newRole(@Validated @RequestBody RoleRequest roleRequ
194194
UserPermissions.assertAuthority(user, Authority.INSTITUTION_ADMIN);
195195
//For super_users we require an organization GUID from the input form
196196
if (user.isSuperUser() && !StringUtils.hasText(roleRequest.getOrganizationGUID())) {
197-
throw new UserRestrictionException();
197+
throw new UserRestrictionException("Super users must specify an organizationGUID");
198198
}
199199
Role role = new Role(roleRequest);
200200
role.setOrganizationGUID(user.isSuperUser() ? roleRequest.getOrganizationGUID() : user.getOrganizationGUID());
@@ -244,7 +244,8 @@ public ResponseEntity<Void> deleteRole(@PathVariable("id") Long id,
244244

245245
if (!user.isSuperUser() &&
246246
!Objects.equals(user.getOrganizationGUID(), role.getOrganizationGUID())) {
247-
throw new UserRestrictionException();
247+
throw new UserRestrictionException(String.format("Non super user %s not allowd to delete role %s",
248+
user.getEmail(), role.getName()));
248249
}
249250

250251
provisioningService.deleteGroupRequest(role);

server/src/main/java/invite/api/UserController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ public ResponseEntity<User> details(@PathVariable("id") Long id, @Parameter(hidd
131131
//We need to ensure the institution admin has access to at least one of the roles
132132
boolean allowedByManage = roles.stream().anyMatch(role -> user.getOrganizationGUID().equals(role.getOrganizationGUID()));
133133
if (!allowedByManage) {
134-
throw new UserRestrictionException();
134+
throw new UserRestrictionException(String.format("User %s has no access to role %s",
135+
user.getEmail(), roles.stream().map(Role::getName).collect(Collectors.joining(", "))));
135136
}
136137
}
137138
return ResponseEntity.ok(other);

server/src/main/java/invite/api/UserRoleAuditController.java

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,11 @@
33
import invite.config.Config;
44
import invite.exception.NotFoundException;
55
import invite.exception.UserRestrictionException;
6-
import invite.logging.AccessLogger;
7-
import invite.logging.Event;
8-
import invite.manage.EntityType;
9-
import invite.model.Application;
10-
import invite.model.ApplicationUsage;
11-
import invite.model.Authority;
126
import invite.model.Role;
13-
import invite.model.RoleRequest;
147
import invite.model.User;
15-
import invite.model.UserRole;
168
import invite.model.UserRoleAudit;
17-
import invite.provision.scim.GroupURN;
189
import invite.repository.RoleRepository;
1910
import invite.repository.UserRoleAuditRepository;
20-
import invite.security.UserPermissions;
2111
import io.swagger.v3.oas.annotations.Parameter;
2212
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
2313
import org.apache.commons.logging.Log;
@@ -29,38 +19,19 @@
2919
import org.springframework.data.domain.Sort;
3020
import org.springframework.http.MediaType;
3121
import org.springframework.http.ResponseEntity;
32-
import org.springframework.retry.annotation.Backoff;
33-
import org.springframework.retry.annotation.Retryable;
3422
import org.springframework.transaction.annotation.Transactional;
3523
import org.springframework.util.StringUtils;
36-
import org.springframework.validation.annotation.Validated;
37-
import org.springframework.web.bind.annotation.DeleteMapping;
3824
import org.springframework.web.bind.annotation.GetMapping;
39-
import org.springframework.web.bind.annotation.PathVariable;
40-
import org.springframework.web.bind.annotation.PostMapping;
41-
import org.springframework.web.bind.annotation.PutMapping;
42-
import org.springframework.web.bind.annotation.RequestBody;
4325
import org.springframework.web.bind.annotation.RequestMapping;
4426
import org.springframework.web.bind.annotation.RequestParam;
4527
import org.springframework.web.bind.annotation.RestController;
4628

47-
import java.net.URLDecoder;
48-
import java.nio.charset.Charset;
49-
import java.sql.SQLTransactionRollbackException;
50-
import java.util.ArrayList;
51-
import java.util.Collection;
5229
import java.util.List;
5330
import java.util.Map;
54-
import java.util.Objects;
55-
import java.util.Optional;
56-
import java.util.Set;
57-
import java.util.UUID;
58-
import java.util.stream.Collectors;
5931

6032
import static invite.SwaggerOpenIdConfig.API_TOKENS_SCHEME_NAME;
6133
import static invite.SwaggerOpenIdConfig.OPEN_ID_SCHEME_NAME;
6234
import static invite.security.InstitutionAdmin.isInstitutionAdmin;
63-
import static invite.security.UserPermissions.assertAuthority;
6435
import static invite.security.UserPermissions.assertInstitutionAdmin;
6536

6637
@RestController
@@ -107,7 +78,8 @@ public ResponseEntity<Page<UserRoleAudit>> search(
10778
Role role = roleRepository.findById(roleId)
10879
.orElseThrow(() -> new NotFoundException("Role not found: " + roleId));
10980
if (!user.getOrganizationGUID().equals(role.getOrganizationGUID())) {
110-
throw new UserRestrictionException();
81+
throw new UserRestrictionException(String.format("User %s has no access to role %s",
82+
user.getEmail(), role.getName()));
11183
}
11284
}
11385

server/src/main/java/invite/exception/UserRestrictionException.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@
77
@ResponseStatus(HttpStatus.FORBIDDEN)
88
public class UserRestrictionException extends AuthenticationException {
99

10-
public UserRestrictionException() {
11-
super("Forbidden");
12-
}
13-
1410
public UserRestrictionException(String message) {
1511
super(message);
1612
}

server/src/main/java/invite/security/RemoteUserPermissions.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
import invite.model.Role;
66
import org.apache.commons.lang3.stream.Streams;
77

8+
import java.util.Arrays;
89
import java.util.Collection;
910
import java.util.List;
11+
import java.util.stream.Collectors;
1012

1113
public class RemoteUserPermissions {
1214

@@ -15,13 +17,14 @@ private RemoteUserPermissions() {
1517

1618
public static void assertScopeAccess(RemoteUser remoteUser, Scope... scopes) {
1719
if (remoteUser == null) {
18-
throw new UserRestrictionException();
20+
throw new UserRestrictionException("Remote user is NULL");
1921
}
2022
if (scopes == null || scopes.length == 0) {
2123
return;
2224
}
2325
if (!Streams.of(scopes).allMatch(scope -> remoteUser.getScopes().contains(scope))) {
24-
throw new UserRestrictionException();
26+
throw new UserRestrictionException(String.format("Scope %s not allowd for remoteUsser %s",
27+
Arrays.toString(scopes), remoteUser.getName()));
2528
}
2629
}
2730

@@ -31,7 +34,7 @@ public static void assertApplicationAccess(RemoteUser remoteUser, Role role) {
3134

3235
public static void assertApplicationAccess(RemoteUser remoteUser, List<Role> roles) {
3336
if (remoteUser == null) {
34-
throw new UserRestrictionException();
37+
throw new UserRestrictionException("Remote user is NULL");
3538
}
3639
if (remoteUser.isLocalDevMode()) {
3740
return;
@@ -43,7 +46,8 @@ public static void assertApplicationAccess(RemoteUser remoteUser, List<Role> rol
4346
.anyMatch(remoteUserApplication -> remoteUserApplication.getManageId().equals(application.getManageId())
4447
&& remoteUserApplication.getManageType().equals(application.getManageType())));
4548
if (!hasApplicationAccess) {
46-
throw new UserRestrictionException();
49+
throw new UserRestrictionException(String.format("RemoteUser %s has no access to roles %s",
50+
remoteUser.getName(), roles.stream().map(role -> role.getName()).collect(Collectors.joining(", "))));
4751
}
4852
}
4953

server/src/main/java/invite/security/UserHandlerMethodArgumentResolver.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public User resolveArgument(MethodParameter methodParameter,
6969
//The user has an API token (from her institution admin) and there is no state
7070
String hashedToken = HashGenerator.hashToken(apiTokenHeader);
7171
APIToken apiToken = apiTokenRepository.findByHashedValue(hashedToken)
72-
.orElseThrow(UserRestrictionException::new);
72+
.orElseThrow(() -> new UserRestrictionException("No API token found for: " + hashedToken));
7373
List<User> apiUsers = new ArrayList<>();
7474
User owner = apiToken.getOwner();
7575
String organizationGUID = apiToken.getOrganizationGUID();
@@ -85,7 +85,7 @@ public User resolveArgument(MethodParameter methodParameter,
8585
}
8686
if (apiUsers.isEmpty()) {
8787
//we don't want to return null as this is not part of the happy-path
88-
throw new UserRestrictionException();
88+
throw new UserRestrictionException("No API user found for token: " + hashedToken);
8989
}
9090
//For old superuser and institution admin tokens it does not make any difference security-wise which user we return
9191
//For inviters and managers (and all new tokens after 0.0.36 release) the user is linked to the API token
@@ -124,7 +124,7 @@ public User resolveArgument(MethodParameter methodParameter,
124124
if (StringUtils.hasText(impersonateId) && user.isSuperUser()) {
125125
validImpersonation.set(true);
126126
return userRepository.findById(Long.valueOf(impersonateId))
127-
.orElseThrow(UserRestrictionException::new);
127+
.orElseThrow(() -> new UserRestrictionException("No user found in impersonation: " + impersonateId));
128128
}
129129
return user;
130130
});
@@ -144,7 +144,7 @@ public User resolveArgument(MethodParameter methodParameter,
144144
}
145145
}
146146
return user;
147-
}).orElseThrow(UserRestrictionException::new);
147+
}).orElseThrow(() -> new UserRestrictionException("No user found sub:" + sub));
148148

149149
}
150150

server/src/main/java/invite/security/UserPermissions.java

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,38 @@
1111

1212
import java.util.List;
1313
import java.util.Set;
14+
import java.util.stream.Collectors;
1415

1516
public class UserPermissions {
1617
private static final Log LOG = LogFactory.getLog(UserPermissions.class);
18+
1719
private UserPermissions() {
1820
}
1921

2022
public static void assertSuperUser(User user) {
2123
if (user == null) {
22-
throw new UserRestrictionException();
24+
throw new UserRestrictionException("User is NULL");
2325
}
2426

2527
if (!user.isSuperUser()) {
26-
throw new UserRestrictionException();
28+
throw new UserRestrictionException("User is no super user: " + user.getEmail());
2729
}
2830
}
2931

3032
public static void assertInstitutionAdmin(User user) {
3133
if (user == null) {
32-
throw new UserRestrictionException();
34+
throw new UserRestrictionException("User is NULL");
3335
}
3436

3537
if (user.isSuperUser() || (user.isInstitutionAdmin() && StringUtils.hasText(user.getOrganizationGUID()))) {
3638
return;
3739
}
38-
throw new UserRestrictionException();
40+
throw new UserRestrictionException("User is no institution admin: " + user.getEmail());
3941
}
4042

4143
public static void assertAuthority(User user, Authority authority) {
4244
if (user == null) {
43-
throw new UserRestrictionException();
45+
throw new UserRestrictionException("User is NULL");
4446
}
4547
LOG.debug(String.format("assertAuthority for user %s", user.getEduPersonPrincipalName()));
4648

@@ -54,21 +56,21 @@ public static void assertAuthority(User user, Authority authority) {
5456
return;
5557
}
5658
if (user.getUserRoles().stream()
57-
.noneMatch(userRole -> userRole.getAuthority().hasEqualOrHigherRights(authority)))
58-
throw new UserRestrictionException();
59+
.noneMatch(userRole -> userRole.getAuthority().hasEqualOrHigherRights(authority)))
60+
throw new UserRestrictionException(String.format("User %s is not an Authority %s", user.getEmail(), authority));
5961
}
6062

6163
public static void assertValidInvitation(User user, Authority intendedAuthority, List<Role> roles) {
6264
if (user == null) {
63-
throw new UserRestrictionException();
65+
throw new UserRestrictionException("User is NULL");
6466
}
6567
LOG.debug(String.format("assertValidInvitation for user %s", user.getEduPersonPrincipalName()));
6668

6769
if (user.isSuperUser()) {
6870
return;
6971
}
7072
if (intendedAuthority.equals(Authority.SUPER_USER)) {
71-
throw new UserRestrictionException();
73+
throw new UserRestrictionException("Invalid invitation for super-user by " + user.getEmail());
7274
}
7375
Set<UserRole> userRoles = user.getUserRoles();
7476
//Institution admin needs to own all roles or be a member of the role for at least the authority of invitationo
@@ -85,21 +87,22 @@ public static void assertValidInvitation(User user, Authority intendedAuthority,
8587
return mayInviteByInstitutionAdmin || mayInviteByApplication || mayInviteByAuthority;
8688
});
8789
if (!allowed) {
88-
throw new UserRestrictionException();
90+
throw new UserRestrictionException(String.format("Invalid invation by %s for roles %s",
91+
user.getEmail(), roles.stream().map(role -> role.getName()).collect(Collectors.joining(", "))));
8992
}
9093
}
9194

9295
public static void assertRoleAccess(User user, Role accessRole, Authority authority) {
9396
if (user == null) {
94-
throw new UserRestrictionException();
97+
throw new UserRestrictionException("USer is NULL");
9598
}
9699
LOG.debug(String.format("assertRoleAccess for user %s", user.getEduPersonPrincipalName()));
97100

98101
if (user.isSuperUser()) {
99102
return;
100103
}
101104
if (accessRole == null) {
102-
throw new UserRestrictionException();
105+
throw new UserRestrictionException("Role is NULL");
103106
}
104107
if (user.isInstitutionAdmin() && user.getOrganizationGUID().equals(accessRole.getOrganizationGUID())) {
105108
return;
@@ -110,7 +113,8 @@ public static void assertRoleAccess(User user, Role accessRole, Authority author
110113
(userRole.hasAccessToApplication(accessRole) &&
111114
userRole.getAuthority().hasEqualOrHigherRights(Authority.INSTITUTION_ADMIN)))
112115
.findFirst()
113-
.orElseThrow(UserRestrictionException::new);
116+
.orElseThrow(() -> new UserRestrictionException(String.format("User %s has no access to role %s",
117+
user.getEmail(), accessRole.getName())));
114118
}
115119

116120
//Does one of the userRoles has Authority.MANAGE and has the same application as the role

0 commit comments

Comments
 (0)