Skip to content

Commit

Permalink
Roles to be expired soon
Browse files Browse the repository at this point in the history
  • Loading branch information
oharsta committed Jan 12, 2024
1 parent 4948b49 commit 043e08f
Show file tree
Hide file tree
Showing 12 changed files with 228 additions and 16 deletions.
8 changes: 6 additions & 2 deletions client/src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,12 @@ export function validate(type, value) {
}

//System
export function cron() {
return fetchJson("/api/v1/system/cron")
export function cronCleanup() {
return fetchJson("/api/v1/system/cron/cleanup")
}

export function expiryUserRoles() {
return fetchJson("/api/v1/system/expiry-user-roles")
}

export function rolesUnknownInManage() {
Expand Down
8 changes: 7 additions & 1 deletion client/src/locale/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ const en = {
cron: "Cron",
invite: "Invite",
tokens: "API tokens",
unknownRoles: "Missing applications"
unknownRoles: "Missing applications",
expiredUserRoles: "User role expirations"
},
home: {
access: "SURFconext Invite",
Expand Down Expand Up @@ -434,6 +435,11 @@ const en = {
title: "Roles linked to applications unknown in Manage",
searchPlaceHolder: "Search...",
noRoles: "Yeah, no unknown manage applications"
},
expiredUserRoles: {
title: "Roles to be expired the next month",
searchPlaceHolder: "Zoek...",
noResults: "Yeah, no user-roles to be expired within one month"
}
}

Expand Down
8 changes: 7 additions & 1 deletion client/src/locale/nl.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ const nl = {
cron: "Cron",
invite: "Uitnodiging",
tokens: "API-tokens",
unknownRoles: "Missing applications"
unknownRoles: "Missing applications",
expiredUserRoles: "User role expirations"
},
home: {
access: "SURFconext Invite",
Expand Down Expand Up @@ -434,6 +435,11 @@ const nl = {
title: "Rollen gekoppeld aan applicaties die verwijderd zijn in Manage",
searchPlaceHolder: "Zoek...",
noRoles: "Yeah, no unknown manage applications"
},
expiredUserRoles: {
title: "Roles to be expired the next month",
searchPlaceHolder: "Zoek...",
noRoles: "Yeah, no user-roles to be expired within one month"
}
}

Expand Down
7 changes: 7 additions & 0 deletions client/src/pages/System.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {Cron} from "../tabs/Cron";
import {RolesUnknownInManage} from "../tabs/RolesUnknownInManage";
import {Invitations} from "../tabs/Invitations";
import {ReactComponent as InvitationLogo} from "@surfnet/sds/icons/functional-icons/id-1.svg";
import {ExpiredUserRoles} from "../tabs/ExpiredUserRoles";


export const System = () => {
Expand Down Expand Up @@ -47,6 +48,12 @@ export const System = () => {
label={I18n.t("tabs.unknownRoles")}
Icon={RoleLogo}>
<RolesUnknownInManage/>
</Page>,
<Page key="expiredUserRoles"
name="expiredUserRoles"
label={I18n.t("tabs.expiredUserRoles")}
Icon={RoleLogo}>
<ExpiredUserRoles/>
</Page>

];
Expand Down
4 changes: 2 additions & 2 deletions client/src/tabs/Cron.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import I18n from "../locale/I18n";
import "./Cron.scss";
import {Button} from "@surfnet/sds";
import "./Users.scss";
import {cron} from "../api";
import {cronCleanup} from "../api";
import {isEmpty} from "../utils/Utils";
import {allExpanded, defaultStyles, JsonView} from 'react-json-view-lite';
import 'react-json-view-lite/dist/index.css';
Expand All @@ -16,7 +16,7 @@ export const Cron = () => {
<div className="mod-cron">
<div className="actions">
<span>{I18n.t("system.cronInfo")}</span>
{isEmpty(results) && <Button onClick={() => cron().then(res => setResults(res))}
{isEmpty(results) && <Button onClick={() => cronCleanup().then(res => setResults(res))}
txt={I18n.t("system.trigger")}/>}
{!isEmpty(results) && <Button onClick={() => setResults({})}
txt={I18n.t("system.clear")}/>}
Expand Down
94 changes: 94 additions & 0 deletions client/src/tabs/ExpiredUserRoles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React, {useEffect, useState} from "react";
import I18n from "../locale/I18n";
import "./ExpiredUserRoles.scss";
import {Chip, Loader} from "@surfnet/sds";
import {Entities} from "../components/Entities";
import {dateFromEpoch, shortDateFromEpoch} from "../utils/Date";
import {useNavigate} from "react-router-dom";
import {chipTypeForUserRole} from "../utils/Authority";
import {expiryUserRoles} from "../api";
import {stopEvent} from "../utils/Utils";


export const ExpiredUserRoles = () => {
const navigate = useNavigate();
const [userRoles, setUserRoles] = useState({});
const [loading, setLoading] = useState(true);

useEffect(() => {
expiryUserRoles().then(res => {
setUserRoles(res);
setLoading(false);
});

},
[])

if (loading) {
return <Loader/>
}

const openRole = (e, userRole) => {
const path = `/roles/${userRole.role.id}`
if (e.metaKey || e.ctrlKey) {
window.open(path, '_blank');
} else {
stopEvent(e);
navigate(path);
}
};

const displayEndDate = userRole => {
return dateFromEpoch(userRole.endDate)
}

const columns = [
{
key: "name",
header: I18n.t("users.name_email"),
mapper: userRole => (
<div className="user-name-email">
<span className="name">{userRole.userInfo.name}</span>
<span className="email">{userRole.userInfo.email}</span>
</div>)
},
{
key: "schacHomeOrganisation",
header: I18n.t("users.schacHomeOrganization"),
mapper: userRole => <span>{userRole.userInfo.schacHomeOrganization}</span>
},
{
key: "authority",
header: I18n.t("roles.authority"),
mapper: userRole => <Chip type={chipTypeForUserRole(userRole.authority)}
label={I18n.t(`access.${userRole.authority}`)}/>
},
{
key: "createdAt",
header: I18n.t("userRoles.createdAt"),
mapper: userRole => shortDateFromEpoch(userRole.createdAt)
},
{
key: "endDate",
header: I18n.t("roles.endDate"),
toolTip: I18n.t("tooltips.roleExpiryDateTooltip"),
mapper: userRole => displayEndDate(userRole)
}];

return (
<div className="mod-expired-user-roles">
<Entities entities={userRoles}
modelName="userRoles"
defaultSort="name"
columns={columns}
showNew={false}
customNoEntities={I18n.t(`expiredUserRoles.noResults`)}
loading={false}
rowLinkMapper={openRole}
title={I18n.t("expiredUserRoles.title")}
searchAttributes={["name", "email", "schacHomeOrganization"]}
inputFocus={true}>
</Entities>
</div>)

}
63 changes: 63 additions & 0 deletions client/src/tabs/ExpiredUserRoles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
.mod-expired-user-roles {
background: white;

.date-field-container {
display: flex;
position: relative;

.date-field {
margin-left: auto;
}

.no-end-date {
position: absolute;
z-index: 99;
top: 10px;
right: 130px;
}
}

table.userRoles {

thead {
th {
&.name {
width: 18%;
}

&.schacHomeOrganisation {
width: 12%;
}

&.authority {
width: 18%;
text-align: center;
}

&.createdAt {
width: 12%;
}

&.endDate {
width: 30%;
text-align: right;
}
}
}

tbody {
td {
&.authority {
text-align: center;
}

&.endDate {
text-align: right;
}
}


}
}

}
2 changes: 1 addition & 1 deletion client/src/tabs/RolesUnknownInManage.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@import "../styles/vars.scss";

div.mod-unknown-roles {

background: white;
table.unknown-roles {

thead {
Expand Down
27 changes: 23 additions & 4 deletions server/src/main/java/access/api/SystemController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import access.manage.Manage;
import access.model.Role;
import access.model.User;
import access.model.UserRole;
import access.repository.RoleRepository;
import access.repository.UserRoleRepository;
import access.security.UserPermissions;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
Expand All @@ -18,6 +20,8 @@
import org.springframework.web.bind.annotation.*;

import java.io.Serializable;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;

Expand All @@ -34,22 +38,37 @@ public class SystemController {

private final ResourceCleaner resourceCleaner;
private final RoleRepository roleRepository;
private final UserRoleRepository userRoleRepository;
private final Manage manage;

public SystemController(ResourceCleaner resourceCleaner, RoleRepository roleRepository, Manage manage) {
public SystemController(ResourceCleaner resourceCleaner,
RoleRepository roleRepository,
UserRoleRepository userRoleRepository,
Manage manage) {
this.resourceCleaner = resourceCleaner;
this.roleRepository = roleRepository;
this.userRoleRepository = userRoleRepository;
this.manage = manage;
}

@GetMapping("/cron")
public ResponseEntity<Map<String, List<? extends Serializable>>> cron(@Parameter(hidden = true) User user) {
LOG.debug("/cron");
@GetMapping("/cron/cleanup")
public ResponseEntity<Map<String, List<? extends Serializable>>> cronCleanup(@Parameter(hidden = true) User user) {
LOG.debug("/cron/cleanup");
UserPermissions.assertSuperUser(user);
Map<String, List<? extends Serializable>> body = resourceCleaner.doClean();
return ResponseEntity.ok(body);
}

@GetMapping("/expiry-user-roles")
public ResponseEntity<List<UserRole>> expiryUserRoles(@Parameter(hidden = true) User user) {
LOG.debug("/cron/notifications");
UserPermissions.assertSuperUser(user);
Instant instant = Instant.now().plus(30, ChronoUnit.DAYS);
List<UserRole> userRoles = userRoleRepository.findByEndDateBefore(instant);
userRoles.forEach(userRole -> userRole.setUserInfo(userRole.getUser().asMap()));
return ResponseEntity.ok(userRoles);
}

@GetMapping("/unknown-roles")
public ResponseEntity<List<Role>> unknownRoles(@Parameter(hidden = true) User user) {
LOG.debug("/unknown-roles");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,5 @@ public void sweep() {

LOG.info("Send expiration notification mail to " + userRole.getUser().getEmail());
});

}
}
4 changes: 2 additions & 2 deletions server/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ config:
server-url: "http://localhost:8888"
server-welcome-url: "http://localhost:8888"
eduid-entity-id: "https://login.test2.eduid.nl"
role-search-required: false
past-date-allowed: false
role-search-required: False
past-date-allowed: True
eduid-idp-schac-home-organization: "test.eduid.nl"

voot:
Expand Down
18 changes: 16 additions & 2 deletions server/src/test/java/access/api/SystemControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,31 @@
class SystemControllerTest extends AbstractTest {

@Test
void cron() throws Exception {
void cronCleanup() throws Exception {
AccessCookieFilter accessCookieFilter = openIDConnectFlow("/api/v1/users/login", SUPER_SUB);
given()
.when()
.filter(accessCookieFilter.cookieFilter())
.accept(ContentType.JSON)
.header(accessCookieFilter.csrfToken().getHeaderName(), accessCookieFilter.csrfToken().getToken())
.contentType(ContentType.JSON)
.get("/api/v1/system/cron")
.get("/api/v1/system/cron/cleanup")
.then()
.statusCode(200);
}

@Test
void expiryUserRoles() throws Exception {
AccessCookieFilter accessCookieFilter = openIDConnectFlow("/api/v1/users/login", SUPER_SUB);
given()
.when()
.filter(accessCookieFilter.cookieFilter())
.accept(ContentType.JSON)
.header(accessCookieFilter.csrfToken().getHeaderName(), accessCookieFilter.csrfToken().getToken())
.contentType(ContentType.JSON)
.get("/api/v1/system/expiry-user-roles")
.then()
.statusCode(200);
}

}

0 comments on commit 043e08f

Please sign in to comment.