Skip to content

Commit 386b6be

Browse files
committed
WIP for Policies
1 parent 5f250e6 commit 386b6be

18 files changed

+280
-26
lines changed

server/src/main/java/access/api/InviteController.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package access.api;
22

3-
import access.exception.NotFoundException;
3+
import access.exception.InvalidInputException;
44
import access.exception.UserRestrictionException;
55
import access.invite.InviteClient;
6-
import access.model.Authority;
7-
import access.model.Organization;
86
import access.model.User;
97
import access.repository.OrganizationRepository;
108
import access.repository.UserRepository;
@@ -60,6 +58,10 @@ public ResponseEntity<List<Map<String, Object>>> rolesPerOrganizationInviteAppli
6058
String.format("User %s is not authorized for organizationGUID %s",
6159
user.getEmail(), organizationGUID));
6260
}
61+
if (!applicationManageId.matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")) {
62+
throw new InvalidInputException(
63+
String.format("ApplicationManageId %s is not a valid UUID", applicationManageId));
64+
}
6365
List<Map<String, Object>> inviteRoles = this.inviteClient.rolesPerOrganizationApplicationId(organizationGUID, applicationManageId);
6466
return ResponseEntity.ok(inviteRoles);
6567
}

server/src/main/java/access/api/LoginController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public LoginController(Environment environment, SecurityContextRepository securi
3333
}
3434

3535
@PutMapping("/login")
36-
public ResponseEntity<Void> login(@RequestBody Map<String, String> body,
36+
public ResponseEntity<Void> login(@RequestBody Map<String, Object> body,
3737
HttpServletRequest servletRequest,
3838
HttpServletResponse servletResponse) {
3939
if (!environment.acceptsProfiles(Profiles.of("test"))) {

server/src/main/java/access/api/ManageController.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package access.api;
22

33
import access.exception.InvalidInputException;
4+
import access.exception.UserRestrictionException;
45
import access.manage.*;
56
import access.model.*;
7+
import access.security.InstitutionAdmin;
68
import com.fasterxml.jackson.core.type.TypeReference;
79
import com.fasterxml.jackson.databind.ObjectMapper;
810
import lombok.SneakyThrows;
@@ -27,7 +29,7 @@
2729

2830
@RestController
2931
@RequestMapping(value = {"/api/v1/manage"}, produces = MediaType.APPLICATION_JSON_VALUE)
30-
public class ManageController {
32+
public class ManageController implements UserAccessRights{
3133

3234
private static final Log LOG = LogFactory.getLog(ManageController.class);
3335

@@ -86,17 +88,21 @@ public ResponseEntity<List<Map<String, Object>>> identityProviders(@PathVariable
8688
}
8789

8890
@SneakyThrows
89-
@GetMapping("/policies/{entityId}")
90-
public ResponseEntity<List<Map<String, Object>>> policies(Authentication authentication,
91-
@PathVariable String entityId) {
91+
@GetMapping("/policies")
92+
public ResponseEntity<List<Map<String, Object>>> policies(User user,
93+
Authentication authentication,
94+
@RequestParam("entityId") String entityId) {
95+
confirmInstitutionAdmin(user);
9296
//we need to ensure the application is connected to the IdP of the user
9397
OidcUser oidcUser = (OidcUser) authentication.getPrincipal();
9498
Map<String, Object> claims = oidcUser.getUserInfo().getClaims();
95-
String authenticatingAuthority = (String) claims.get("authenticating_authority");
96-
Map<String, Object> identityProvider = manage.identityProviderByEntityID(authenticatingAuthority);
97-
Map<String, Object> data = ManageData.getData(identityProvider);
98-
List<Map<String, Object>> providers = manage.providers(environment, EntityType.policy);
99-
return ResponseEntity.ok(providers);
99+
Institution institution = (Institution) claims.get(InstitutionAdmin.INSTITUTION);
100+
if (!institution.getAllowedEntities().contains(entityId)) {
101+
throw new UserRestrictionException(String.format("User %s is not allowed to request policies for %s",
102+
user.getEmail(), entityId));
103+
}
104+
List<Map<String, Object>> policies = this.manage.policiesByServiceProvider(institution.getEntityID(), entityId);
105+
return ResponseEntity.ok(policies);
100106
}
101107

102108
@SneakyThrows

server/src/main/java/access/api/UserAccessRights.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,12 @@ default void confirmSuperUser(User user) {
9292
}
9393
}
9494

95+
default void confirmInstitutionAdmin(User user) {
96+
if (!user.isSuperUser() && !user.isInstitutionAdmin()) {
97+
throw new UserRestrictionException(String.format("User %s, %s is no super user or institution admin",
98+
user.getEmail(), user.getAuthenticatingAuthority()));
99+
}
100+
101+
}
102+
95103
}

server/src/main/java/access/manage/LocalManage.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ private List<Map<String, Object>> initialize(EntityType entityType, String stati
4444
List<Map<String, Object>> providers = objectMapper.readValue(resource.getInputStream(), new TypeReference<>() {
4545
});
4646
//Need mutability
47-
4847
return providers.stream().map(provider -> sanitizeProvider(provider))
4948
.collect(Collectors.toCollection(ArrayList::new));
5049
}
@@ -245,6 +244,24 @@ public List<Map<String, Object>> identityProvidersByAllowedConnections(List<Conn
245244
.toList();
246245
}
247246

247+
@Override
248+
public List<Map<String, Object>> policiesByServiceProvider(String identityProviderEntityId, String serviceProviderEntityId) {
249+
return this.allProviders.get(EntityType.policy).stream()
250+
.filter(policy -> {
251+
Map<String, Object> data = getData(policy);
252+
List<Map<String, String>> serviceProviderIds = (List<Map<String, String>>)
253+
data.getOrDefault("serviceProviderIds", List.of());
254+
List<Map<String, String>> identityProviderIds = (List<Map<String, String>>)
255+
data.getOrDefault("identityProviderIds", List.of());
256+
return serviceProviderIds.stream()
257+
.anyMatch(m -> m.get("name").equals(serviceProviderEntityId))
258+
&& (identityProviderIds.isEmpty() ||
259+
identityProviderIds.stream()
260+
.anyMatch(m -> m.get("name").equals(identityProviderEntityId)));
261+
})
262+
.toList();
263+
}
264+
248265
@Override
249266
public void connectWithoutInteraction(Map<String, Object> identityProvider, Map<String, Object> serviceProvider, User currentUser) {
250267
//nope

server/src/main/java/access/manage/Manage.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public interface Manage {
4646

4747
List<Map<String, Object>> identityProvidersByAllowedConnections(List<Connection> connections);
4848

49+
List<Map<String, Object>> policiesByServiceProvider(String identityProviderEntityId,
50+
String serviceProviderEntityId);
51+
4952
void connectWithoutInteraction(Map<String, Object> identityProvider, Map<String, Object> serviceProvider, User currentUser);
5053

5154
default Map<String, Object> sanitizeProvider(Map<String, Object> provider) {

server/src/main/java/access/manage/RemoteManage.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
import org.apache.commons.logging.LogFactory;
1111
import org.springframework.core.ParameterizedTypeReference;
1212
import org.springframework.core.io.ClassPathResource;
13-
import org.springframework.http.*;
13+
import org.springframework.http.HttpEntity;
14+
import org.springframework.http.HttpMethod;
15+
import org.springframework.http.HttpStatus;
16+
import org.springframework.http.ResponseEntity;
1417
import org.springframework.util.StringUtils;
1518
import org.springframework.web.client.ResponseErrorHandler;
1619
import org.springframework.web.client.RestTemplate;
@@ -257,7 +260,7 @@ public List<Map<String, Object>> serviceProvidersByEntityID(List<String> entityI
257260
public List<Map<String, Object>> identityProvidersByInstitutionalGUID(Environment environment, String organisationGUID) {
258261
LOG.debug("identityProviderByInstitutionalGUID for : " + organisationGUID);
259262

260-
Map<String, Object> baseQuery = getBaseQuery(false);
263+
Map<String, Object> baseQuery = getBaseQuery(true);
261264
baseQuery.put("metaDataFields.coin:institution_guid", organisationGUID);
262265

263266
String url = String.format("%s/manage/api/internal/search/%s",
@@ -341,6 +344,25 @@ public List<Map<String, Object>> identityProvidersByAllowedConnections(List<Conn
341344
return restTemplate.postForEntity(URI.create(url), body, List.class).getBody();
342345
}
343346

347+
@Override
348+
public List<Map<String, Object>> policiesByServiceProvider(String identityProviderEntityId,
349+
String serviceProviderEntityId) {
350+
String query = """
351+
{
352+
"data.serviceProviderIds.name": "%s",
353+
$or: [
354+
{ "data.identityProviderIds": { $size: 0 } },
355+
{ "data.identityProviderIds": { $exists: false } },
356+
{ "data.identityProviderIds.name": "%s" }
357+
]
358+
})
359+
""".formatted(serviceProviderEntityId, identityProviderEntityId);
360+
RestTemplate restTemplate = environmentRestTemplate(Environment.PROD);
361+
String url = String.format("%s/manage/api/internal/rawSearch/%s",
362+
environmentUrl(Environment.PROD), EntityType.policy);
363+
return restTemplate.postForEntity(url, query, List.class).getBody();
364+
}
365+
344366
@Override
345367
public void connectWithoutInteraction(Map<String, Object> identityProvider, Map<String, Object> serviceProvider, User user) {
346368
RestTemplate restTemplate = environmentRestTemplate(Environment.PROD);

server/src/main/java/access/model/Institution.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import lombok.Setter;
66

77
import java.io.Serializable;
8+
import java.util.List;
89
import java.util.Map;
910

1011
import static access.manage.ManageData.getData;
@@ -19,10 +20,17 @@ public class Institution implements Serializable {
1920
private String entityID;
2021
private String name;
2122
private String organizationName;
23+
private boolean allowedall;
24+
private List<String> allowedEntities;
2225

2326
public Institution(Map<String, Object> provider) {
2427
Map<String, Object> data = getData(provider);
25-
Map<String, Object> metaDataFields = getMetaDataFields (data);
28+
this.allowedall = (boolean) data.getOrDefault("allowedall", false);
29+
this.allowedEntities = ((List<Map<String, String>>) data.getOrDefault("allowedEntities", Map.of()))
30+
.stream()
31+
.map(allowedEntity -> allowedEntity.get("name"))
32+
.toList();
33+
Map<String, Object> metaDataFields = getMetaDataFields(data);
2634
this.entityID = (String) data.get("entityid");
2735
this.name = (String) metaDataFields.get("name:en");
2836
this.organizationName = (String) metaDataFields.get("OrganizationName:en");

server/src/main/java/access/model/User.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ public class User implements Serializable, NameHolder {
6161
@Column(name = "schac_home_organization")
6262
private String schacHomeOrganization;
6363

64+
@Column(name = "authenticating_authority")
65+
private String authenticatingAuthority;
66+
6467
@Column
6568
private String email;
6669

@@ -107,6 +110,7 @@ public User(boolean superUser, Map<String, Object> attributes) {
107110
this.sub = (String) attributes.get("sub");
108111
this.eduPersonPrincipalName = (String) attributes.get("eduperson_principal_name");
109112
this.schacHomeOrganization = (String) attributes.get("schac_home_organization");
113+
this.authenticatingAuthority = (String) attributes.get("authenticating_authority");
110114
this.email = (String) attributes.get("email");
111115
this.givenName = (String) attributes.get("given_name");
112116
this.familyName = (String) attributes.get("family_name");
@@ -117,7 +121,7 @@ public User(boolean superUser, Map<String, Object> attributes) {
117121
this.lastActivity = this.createdAt;
118122
this.institutionAdmin = (boolean) attributes.getOrDefault(INSTITUTION_ADMIN, false);
119123
this.organizationGUID = (String) attributes.get(ORGANIZATION_GUID);
120-
this.institution = (Institution) attributes.getOrDefault(INSTITUTION, null);
124+
this.institution = (Institution) attributes.get(INSTITUTION);
121125

122126
//Defensive mode, EPPN is not a required attribute for access RP
123127
if (!StringUtils.hasText(this.eduPersonPrincipalName)) {

server/src/main/java/access/security/CustomOidcUserService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2Authenticatio
6969
if (institutionAdmin && StringUtils.hasText(organizationGuid)) {
7070
String authenticatingAuthority = (String) claims.get("authenticating_authority");
7171
List<Map<String, Object>> identityProviders = manage.identityProvidersByInstitutionalGUID(Environment.PROD, organizationGuid);
72+
//If there are multiple identityProviders with the same organizationGuid, we pick the one that was used to login
7273
Optional<Map<String, Object>> optionalIdentityProvider = identityProviders.isEmpty() ? Optional.empty() :
7374
Optional.of(identityProviders.stream()
7475
.filter(idp -> entityID(idp).equals(authenticatingAuthority))

0 commit comments

Comments
 (0)