Skip to content

Commit 8a429c9

Browse files
committed
WIP for policies
1 parent c8371ae commit 8a429c9

9 files changed

Lines changed: 71 additions & 47 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,5 @@ jira.yml
4848
application-local.yml
4949
jira.yml
5050
spieldata
51-
PlayGroundTest.java
51+
PlayGroundTest.java
52+
LOCAL_USERS.md

client/src/App.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ const App = () => {
120120
<Flash/>
121121
{impersonator && <Impersonating/>}
122122
<div className="container">
123-
<SharedMenu/>
123+
<SharedMenu currentLocation={currentLocation}/>
124124
<div className="pages">
125125
<AuthorizedHeader setIsAuthenticated={setIsAuthenticated}/>
126126
<Routes>

client/src/components/SharedMenu.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {ORGANIZATION_STATUSES} from "../utils/Manage.js";
1111
import {allMenuGroups} from "../utils/MenuItems.js";
1212
import {isEmpty} from "../utils/Utils.js";
1313
import {useShallow} from "zustand/react/shallow";
14+
import {useLocation} from "react-router-dom";
1415

1516
export const SharedMenu = () => {
1617

@@ -21,7 +22,8 @@ export const SharedMenu = () => {
2122
})));
2223

2324
const navigate = useNavigate();
24-
25+
const currentLocation = useLocation();
26+
console.log(currentLocation.href)
2527
const filteredMenuGroups = useMemo(() => {
2628
return allMenuGroups
2729
.map(menuGroup => ({

client/src/pages/Profile.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const Profile = ({setIsAuthenticated}) => {
2424
useEffect(() => {
2525
useAppStore.setState({
2626
breadcrumbPaths: [
27-
{path: "/home", value: I18n.t("profile.title"), menuItemName: mainMenuItems.home},
27+
{path: "/home", value: I18n.t("breadCrumb.home"), menuItemName: mainMenuItems.home},
2828
{value: I18n.t("breadCrumb.profile")}
2929
]
3030
});

client/src/pages/UserHome.jsx

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import "./UserHome.scss";
2-
import React, {useEffect} from "react";
2+
import React from "react";
33
import {useAppStore} from "../stores/AppStore";
4+
import {Button, ButtonType} from "@surfnet/sds";
45
import I18n from "../locale/I18n";
56
import {isEmpty} from "../utils/Utils.js";
6-
import {useNavigate} from "react-router-dom";
7+
import {Navigate, useNavigate} from "react-router-dom";
78
import {mainMenuItems} from "../utils/MenuItems.js";
8-
import {Button, ButtonType} from "@surfnet/sds";
99
import DOMPurify from "dompurify";
1010
import {InfoBlock} from "../components/InfoBlock.jsx";
1111

@@ -15,25 +15,21 @@ const UserHome = () => {
1515
const currentOrganization = useAppStore(state => state.currentOrganization);
1616

1717
const navigate = useNavigate();
18-
19-
useEffect(() => {
20-
let newLocation = null;
21-
if (isEmpty(user.joinRequests) && isEmpty(currentOrganization?.id)) {
22-
newLocation = "/landing"
23-
24-
} else if (!isEmpty(user.joinRequests) && isEmpty(currentOrganization?.id)) {
25-
newLocation = "/relax"
26-
}
27-
if (newLocation !== null) {
28-
navigate(newLocation, {replace: true});
29-
} else {
30-
useAppStore.setState({
31-
breadcrumbPaths: [
32-
{path: "/home", value: I18n.t("breadCrumb.home"), menuItemName: mainMenuItems.home}
33-
]
34-
});
35-
}
36-
}, [user.joinRequests, currentOrganization?.id, navigate]);
18+
let newLocation = null;
19+
if (isEmpty(user.joinRequests) && isEmpty(currentOrganization?.id)) {
20+
newLocation = "/landing"
21+
} else if (!isEmpty(user.joinRequests) && isEmpty(currentOrganization?.id)) {
22+
newLocation = "/relax"
23+
}
24+
if (newLocation !== null) {
25+
return <Navigate to={newLocation} replace/>;
26+
} else {
27+
useAppStore.setState({
28+
breadcrumbPaths: [
29+
{path: "/home", value: I18n.t("breadCrumb.home"), menuItemName: mainMenuItems.home}
30+
]
31+
});
32+
}
3733

3834
const navigateInner = (menuItem, path) => {
3935
navigate(path);

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

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
import access.manage.*;
66
import access.model.EntityType;
77
import access.model.Environment;
8-
import access.model.Institution;
98
import access.model.User;
10-
import access.security.InstitutionAdmin;
119
import com.fasterxml.jackson.core.type.TypeReference;
1210
import com.fasterxml.jackson.databind.ObjectMapper;
1311
import lombok.SneakyThrows;
@@ -20,8 +18,6 @@
2018
import org.springframework.core.io.UrlResource;
2119
import org.springframework.http.MediaType;
2220
import org.springframework.http.ResponseEntity;
23-
import org.springframework.security.core.Authentication;
24-
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
2521
import org.springframework.web.bind.annotation.*;
2622

2723
import java.net.URI;
@@ -104,14 +100,13 @@ public ResponseEntity<List<Map<String, Object>>> identityProviders(@PathVariable
104100
@GetMapping("/policies")
105101
@SuppressWarnings("unchecked")
106102
public ResponseEntity<List<Map<String, Object>>> policies(User user,
107-
Authentication authentication,
108103
@RequestParam("entityId") String entityId) {
109104
LOG.debug("/policies for " + entityId + " for " + user.getEmail());
110105

111106
confirmInstitutionAdmin(user);
112107
//we need to ensure the application is connected to the IdP of the user - realtime
113108
if (!user.isSuperUser()) {
114-
Map<String, Object> data = getIdentityProvider(authentication);
109+
Map<String, Object> data = getIdentityProvider(user);
115110
boolean noneMatch = ((List<Map<String, String>>) data.getOrDefault("allowedEntities", List.of()))
116111
.stream()
117112
.noneMatch(allowedEntity -> allowedEntity.get("name").equals(entityId));
@@ -131,7 +126,7 @@ public ResponseEntity<List<Map<String, Object>>> policies(User user,
131126
public ResponseEntity<Map<String, Object>> createPolicy(User user, @RequestBody Map<String, Object> policy) {
132127
LOG.debug("/createPolicy for " + policy + " for " + user.getEmail());
133128

134-
policyAccessAllowed(user, policy);
129+
policyAccessAllowed(user, policy, true);
135130
return ResponseEntity.ok(manage.createPolicy(policy));
136131
}
137132

@@ -140,7 +135,7 @@ public ResponseEntity<Map<String, Object>> createPolicy(User user, @RequestBody
140135
public ResponseEntity<Map<String, Object>> updatePolicy(User user, @RequestBody Map<String, Object> policy) {
141136
LOG.debug("/updatePolicy for " + policy + " for " + user.getEmail());
142137

143-
policyAccessAllowed(user, policy);
138+
policyAccessAllowed(user, policy, true);
144139
return ResponseEntity.ok(manage.updatePolicy(policy));
145140
}
146141

@@ -151,7 +146,7 @@ public ResponseEntity<Void> deletePolicy(User user, @PathVariable String policyI
151146

152147
LOG.debug("/deletePolicy for " + policy + " for " + user.getEmail());
153148

154-
policyAccessAllowed(user, policy);
149+
policyAccessAllowed(user, policy, true);
155150
manage.deletePolicy(policy);
156151
return ResponseEntity.noContent().build();
157152
}
@@ -194,21 +189,17 @@ public ResponseEntity<Map<String, Object>> rejectChangeRequest(User user, @Reque
194189
return Results.okResult();
195190
}
196191

197-
private void policyAccessAllowed(User user, Map<String, Object> policy) {
192+
private void policyAccessAllowed(User user, Map<String, Object> policy, boolean throwException) {
198193
confirmInstitutionAdmin(user);
199194
//We don't want to use PolicyDefinition as @RequestBody, because the template from Manage is leading
200195
PolicyDefinition policyDefinition = this.objectMapper.convertValue(policy.get("data"), PolicyDefinition.class);
201196
confirmPolicyAccess(user, policyDefinition, manage);
202197
}
203198

204-
private Map<String, Object> getIdentityProvider(Authentication authentication) {
205-
OidcUser oidcUser = (OidcUser) authentication.getPrincipal();
206-
Map<String, Object> claims = oidcUser.getUserInfo().getClaims();
207-
Institution institution = (Institution) claims.get(InstitutionAdmin.INSTITUTION);
199+
private Map<String, Object> getIdentityProvider(User user) {
208200
//We can't use any cache as this method is called right after automatic connection allowed
209-
Map<String, Object> identityProvider = manage.providerById(EntityType.saml20_idp, institution.getManageIdentifier(), Environment.PROD);
210-
Map<String, Object> data = getData(identityProvider);
211-
return data;
201+
Map<String, Object> identityProvider = manage.identityProviderByEntityID(user.getAuthenticatingAuthority());
202+
return getData(identityProvider);
212203
}
213204

214205

server/src/main/java/access/mail/MailBox.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ public void sendJoinRequestMail(JoinRequest joinRequest) {
140140
if (!environment.equalsIgnoreCase("prod")) {
141141
variables.put("environment", environment);
142142
}
143-
variables.put("url", String.format("%s/organization/%s/joins", clientUrl, organization.getId()));
143+
144+
variables.put("url", String.format("%s/users/%s/joins", clientUrl, organization.getId()));
144145
List<String> emails = organization.getOrganizationMemberships().stream()
145146
.filter(organizationMembership -> organizationMembership.getAuthority().equals(Authority.ADMIN))
146147
.map(organizationMembership -> organizationMembership.getUser().getEmail())
Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,49 @@
11
package access.manage;
22

3+
import access.exception.UserRestrictionException;
34
import access.model.User;
45

6+
import java.util.List;
57
import java.util.Map;
68

9+
import static access.manage.ManageData.getData;
10+
711
public interface PolicyAccessRights {
812

9-
default void confirmPolicyAccess(User user, PolicyDefinition policyDefinition, Manage manage) {
13+
@SuppressWarnings("unchecked")
14+
default void confirmPolicyAccess(User user,
15+
PolicyDefinition policyDefinition,
16+
Manage manage) {
1017
if (user.isSuperUser()) {
1118
return;
1219
}
13-
//TODO implement
20+
List<String> authenticatingAuthority = List.of(user.getAuthenticatingAuthority());
1421
//Is the IdP of the Policy the same as the IdP of the User?
22+
if (!policyDefinition.getIdentityProviderIds().stream()
23+
.map(policyProvider -> policyProvider.getName())
24+
.toList()
25+
.equals(authenticatingAuthority)) {
26+
throwUserRestrictionException(user, policyDefinition);
27+
}
28+
//All SP's must be linked to the IdP of the user (=organizationGUID of the instituitonAdmin)
29+
List<String> serviceProviderIdentifiers = policyDefinition.getServiceProviderIds().stream()
30+
.map(policyProvider -> policyProvider.getName())
31+
.toList();
32+
Map<String, Object> identityProvider = manage.identityProviderByEntityID(user.getAuthenticatingAuthority());
33+
Map<String, Object> data = getData(identityProvider);
34+
List<String> allowedEntities = ((List<Map<String, String>>) data.getOrDefault("allowedEntities", List.of()))
35+
.stream()
36+
.map(allowedEntry -> allowedEntry.get("name"))
37+
.toList();
38+
if (!allowedEntities.containsAll(serviceProviderIdentifiers)) {
39+
throwUserRestrictionException(user, policyDefinition);
40+
}
41+
}
1542

16-
//No IdP, all SP's must be owned by the IdP of the user (=organizationGUID of the instituitonAdmin)
43+
default void throwUserRestrictionException(User user,
44+
PolicyDefinition policyDefinition) {
45+
throw new UserRestrictionException(
46+
String.format("User %s from %s is not allowed access to policy %s",
47+
user.getEmail(), user.getAuthenticatingAuthority(), policyDefinition.getIdentityProviderIds()));
1748
}
1849
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
import lombok.Getter;
66
import lombok.NoArgsConstructor;
77
import lombok.Setter;
8+
import lombok.ToString;
89

910
@NoArgsConstructor
1011
@Getter
1112
@Setter
1213
@AllArgsConstructor
14+
@ToString
1315
public class PolicyProvider {
1416

1517
private String name;

0 commit comments

Comments
 (0)