Skip to content
This repository was archived by the owner on Sep 28, 2022. It is now read-only.

Commit 7d5dc7a

Browse files
author
Dominik František Bučík
authored
Merge pull request #208 from dBucik/ice
feat: IsEligible authproc filter and claim source
2 parents bfb1433 + 2e0aaa7 commit 7d5dc7a

File tree

10 files changed

+459
-42
lines changed

10 files changed

+459
-42
lines changed

perun-oidc-server-webapp/src/main/resources/localization/messages_cs.properties

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,6 @@ contact_p=V p\u0159\u00EDpad\u011B nejasnost\u00ED n\u00E1s kontaktujte na
7575
403_informationPage=Pro v\u00EDce informac\u00ED o slu\u017Eb\u011B nav\u0161tivte
7676
403_contactSupport=Pokud si mysl\u00EDte \u017Ee m\u00E1te m\u00EDt p\u0159\u00EDstup, kontaktujte administr\u00E1tora:
7777
403_subject=Probl\u00E9m s p\u0159ihl\u00E1\u0161en\u00EDm do slu\u017Eby
78-
403_isCesnetEligible_notSet_hdr=P\u0159\u00EDstup zam\u00EDtnut
79-
403_isCesnetEligible_notSet_msg=P\u0159\u00EDstup ke slu\u017Eb\u011B zam\u00EDtnut, proto\u017Ee V\u00E1\u0161 \u00FA\u010Det nen\u00ED z \u010Desk\u00E9 akademick\u00E9 instituce. P\u0159ihlaste se, pros\u00EDm, pomoc\u00ED sv\u00E9ho \u00FA\u010Dtu u akademick\u00E9 instituce.<br/><a class="mt-2 cw btn btn-primary btn-lg btn-block" href="%%TARGET%%">Znovu p\u0159ihl\u00E1sit</a>
80-
403_isCesnetEligible_expired_hdr=P\u0159\u00EDstup zam\u00EDtnut
81-
403_isCesnetEligible_expired_msg=P\u0159\u00EDstup ke slu\u017Eb\u011B zam\u00EDtnut, proto\u017Ee plynula doba 12 m\u011Bs\u00EDc\u016F od Va\u0161eho posledn\u00EDho p\u0159ihl\u00E1\u0161en\u00ED \u00FA\u010Dtem z \u010Desk\u00E9 akademick\u00E9 instituce. P\u0159ihlaste se, pros\u00EDm, pomoc\u00ED sv\u00E9ho \u00FA\u010Dtu u akademick\u00E9 instituce.<br/><a class="mt-2 cw btn btn-lg btn-primary btn-block" href="%%TARGET%%">Znovu p\u0159ihl\u00E1sit</a>
8278
403_ensure_vo_hdr=P\u0159\u00EDstup zam\u00EDtnut
8379
403_ensure_vo_msg=Nem\u00E1te dostate\u010Dn\u00E1 pr\u00E1va pro p\u0159\u00EDstup ke slu\u017Eb\u011B
8480
403_authorization_hdr=P\u0159\u00EDstup zam\u00EDtnut
@@ -92,6 +88,11 @@ contact_p=V p\u0159\u00EDpad\u011B nejasnost\u00ED n\u00E1s kontaktujte na
9288
403_not_logged_in_hdr=P\u0159\u00EDstup zam\u00EDtnut
9389
403_not_logged_in_msg=Zd\u00E1 se, \u017Ee p\u0159ihl\u00E1\u0161en\u00ED selhalo. Zkuste, pros\u00EDm, zav\u0159\u00EDt V\u00E1\u0161 prohl\u00ED\u017Ee\u010D a p\u0159ihl\u00E1sit se znovu.
9490

91+
403_is_eligible_default_header_text=P\u0159\u00EDstup zam\u00EDtnut
92+
403_is_eligible_default_text=P\u0159\u00EDstup ke slu\u017Eb\u011B byl zam\u00EDtnut, proto\u017Ee V\u00E1\u0161 \u00FA\u010Det nespl\u0148uje pomd\u00EDnky p\u0159\u00EDstupu. P\u0159ihlaste se, pros\u00EDme, pomoc\u00ED jin\u00E9ho \u00FA\u010Dtu.
93+
403_is_eligible_default_button_text=Pokra\u010Dovat
94+
403_is_eligible_default_contact_text=Pokud si mysl\u00EDte, \u017Ee pou\u017E\u00EDv\u00E1te spr\u00E1vn\u00FD \u00FA\u010Det a p\u0159\u00EDstup je V\u00E1m odm\u00EDtnut nepr\u00E1vem, pros\u00EDme kontakujte n\u00E1s na
95+
9596
#GO TO REGISTRATION
9697
go_to_registration_title=Je vy\u017Eadov\u00E1na Va\u0161e aktivita
9798
go_to_registration_header1=Pro p\u0159\u00EDstup ke slu\u017Eb\u011B

perun-oidc-server-webapp/src/main/resources/localization/messages_en.properties

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,6 @@ contact_p=In case of any questions, do not hesitate to contact us at
7474
403_informationPage=For more information about this service please visit this
7575
403_contactSupport=If you think you should have an access contact service operator at
7676
403_subject=Problem with login to service:
77-
403_isCesnetEligible_notSet_hdr=Access denied
78-
403_isCesnetEligible_notSet_msg=Your account is not from Czech academic institution. Please log in with your account from academic institution.<a class="mt-2 cw btn btn-primary btn-lg btn-block" href="%%TARGET%%">Log in again</a>
79-
403_isCesnetEligible_expired_hdr=Access denied
80-
403_isCesnetEligible_expired_msg=Your last login, from Czech academic institution, has been registered 12 months ago. Please sign in with your account from academic institution.<a class="mt-2 cw btn btn-primary btn-lg btn-block" href="%%TARGET%%">Log in again</a>
8177
403_ensure_vo_hdr=Access denied
8278
403_ensure_vo_msg=You don't meet the prerequisites to access the service.
8379
403_authorization_hdr=Access denied
@@ -91,6 +87,11 @@ contact_p=In case of any questions, do not hesitate to contact us at
9187
403_not_logged_in_hdr=Access denied
9288
403_not_logged_in_msg=It appears the login process has failed. Please close your browser and try to log in again.
9389

90+
403_is_eligible_default_header_text=Access denied
91+
403_is_eligible_default_text=Your account does not meet the criteria for accessing the service. Please log in with other account.
92+
403_is_eligible_default_button_text=Continue with other account.
93+
403_is_eligible_default_contact_text=If you think you have used an account which meets the criteria, and you are still prevented from logging in to the service, please contact us at
94+
9495
#GO TO REGISTRATION
9596
go_to_registration_title=Your activity is necessary
9697
go_to_registration_header1=Your activity is necessary to access the
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
2+
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
3+
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
4+
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
5+
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
6+
<%@ taglib prefix="ls" tagdir="/WEB-INF/tags/lsaai" %>
7+
8+
<ls:header />
9+
10+
<div class="error_message" style="word-wrap: break-word;">
11+
<h1><spring:message code="${outHeader}"/></h1>
12+
<p><spring:message code="${outMessage}"/></p>
13+
<c:if test="${hasTarget}">
14+
<form method="POST" action="" class="mb-4">
15+
<button class="btn btn-primary btn-block"><spring:message code="${outButton}"/></button>
16+
</form>
17+
</c:if>
18+
<p><spring:message code="${outContactP}"/>${" "}<a href="mailto:${contactMail}">${contactMail}</a></p>
19+
</div>
20+
21+
<ls:footer/>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" trimDirectiveWhitespaces="true" %>
2+
<%@ page import="java.util.ArrayList" %>
3+
<%@ page import="java.util.List" %>
4+
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
5+
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
6+
<%@ taglib prefix="t" tagdir="/WEB-INF/tags/common"%>
7+
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
8+
9+
<%
10+
11+
List<String> cssLinks = new ArrayList<>();
12+
13+
pageContext.setAttribute("cssLinks", cssLinks);
14+
15+
%>
16+
17+
<t:header title="${title}" reqURL="${reqURL}" baseURL="${baseURL}" cssLinks="${cssLinks}" theme="${theme}"/>
18+
19+
</div> <%-- header --%>
20+
21+
<div id="content">
22+
<div class="error_message" style="word-wrap: break-word;">
23+
<h1><spring:message code="${outHeader}"/></h1>
24+
<p><spring:message code="${outMessage}"/></p>
25+
<c:if test="${hasTarget}">
26+
<form method="POST" action="" class="mb-4">
27+
<button class="btn btn-primary btn-block"><spring:message code="${outButton}"/></button>
28+
</form>
29+
</c:if>
30+
<p><spring:message code="${outContactP}"/>${" "}<a href="mailto:${contactMail}">${contactMail}</a></p>
31+
</div>
32+
</div>
33+
</div><!-- ENDWRAP -->
34+
35+
<t:footer baseURL="${baseURL}" theme="${theme}"/>

perun-oidc-server-webapp/src/main/webapp/WEB-INF/web-context.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
<mvc:exclude-mapping path="#{T(cz.muni.ics.oidc.web.controllers.PerunUnapprovedController).UNAPPROVED_MAPPING}**" />
6666
<mvc:exclude-mapping path="#{T(cz.muni.ics.oidc.web.controllers.PerunUnapprovedController).UNAPPROVED_AUTHORIZATION}**" />
6767
<mvc:exclude-mapping path="#{T(cz.muni.ics.oidc.web.controllers.PerunUnapprovedController).UNAPPROVED_ENSURE_VO_MAPPING}**" />
68-
<mvc:exclude-mapping path="#{T(cz.muni.ics.oidc.web.controllers.PerunUnapprovedController).UNAPPROVED_IS_CESNET_ELIGIBLE_MAPPING}**" />
68+
<mvc:exclude-mapping path="#{T(cz.muni.ics.oidc.web.controllers.PerunUnapprovedController).UNAPPROVED_IS_ELIGIBLE_MAPPING}**" />
6969
<mvc:exclude-mapping path="#{T(cz.muni.ics.oidc.web.controllers.PerunUnapprovedController).UNAPPROVED_NOT_IN_MANDATORY_VOS_GROUPS}**" />
7070
<mvc:exclude-mapping path="#{T(cz.muni.ics.oidc.web.controllers.PerunUnapprovedController).UNAPPROVED_NOT_IN_PROD_VOS_GROUPS}**" />
7171
<mvc:exclude-mapping path="#{T(cz.muni.ics.oidc.web.controllers.PerunUnapprovedController).UNAPPROVED_NOT_IN_TEST_VOS_GROUPS}**" />

perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/claims/ClaimUtils.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,23 @@ private static boolean fillBooleanPropertyOrDefaultVal(String prop, boolean defa
6565
}
6666
}
6767

68+
public static int fillIntegerPropertyOrDefaultVal(String suffix, ClaimSourceInitContext ctx, int defaultVal) {
69+
return fillIntegerPropertyOrDefaultVal(ctx.getProperty(suffix, NO_VALUE), defaultVal);
70+
}
71+
72+
private static int fillIntegerPropertyOrDefaultVal(String prop, int defaultVal) {
73+
if (StringUtils.hasText(prop)) {
74+
try {
75+
return Integer.parseInt(prop);
76+
} catch (NumberFormatException e) {
77+
log.warn("Caught {}", e.getClass().getSimpleName(), e);
78+
return defaultVal;
79+
}
80+
} else {
81+
return defaultVal;
82+
}
83+
}
84+
6885
public static ArrayNode listToArrayNode(List<String> list) {
6986
ArrayNode res = JsonNodeFactory.instance.arrayNode();
7087
if (list != null && !list.isEmpty()) {
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package cz.muni.ics.oidc.server.claims.sources;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
5+
import cz.muni.ics.oauth2.model.SamlAuthenticationDetails;
6+
import cz.muni.ics.oidc.server.claims.ClaimSource;
7+
import cz.muni.ics.oidc.server.claims.ClaimSourceInitContext;
8+
import cz.muni.ics.oidc.server.claims.ClaimSourceProduceContext;
9+
import cz.muni.ics.oidc.server.claims.ClaimUtils;
10+
import java.time.LocalDate;
11+
import java.time.LocalDateTime;
12+
import java.time.format.DateTimeFormatter;
13+
import java.time.format.DateTimeParseException;
14+
import java.util.Collections;
15+
import java.util.Set;
16+
import lombok.extern.slf4j.Slf4j;
17+
import org.springframework.util.StringUtils;
18+
19+
/**
20+
* This source checks if the timestamp is within defined months subtracted from the current timestamp.
21+
* If so, returns TRUE, FALSE otherwise.
22+
*
23+
* Configuration (replace [claimName] with the name of the claim):
24+
* <ul>
25+
* <li><b>custom.claim.[claimName].source.attribute</b> - attribute containing the eligibility last seen timestamp passed via SAML authentication</li>
26+
* <li><b>custom.claim.[claimName].source.validityPeriodMonths</b> - amount of months that we subtract from the current timestamp and compare it with the eligibility timestamp. If not provided, default 12 months is used</li>
27+
* </ul>
28+
*
29+
* @author Pavol Pluta <[email protected]>
30+
* @author Dominik Frantisek Bucik <[email protected]>
31+
*/
32+
@Slf4j
33+
public class IsEligibleClaimSource extends ClaimSource {
34+
35+
private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss";
36+
private static final int DEFAULT_VALIDITY_PERIOD = 12; // 12 months
37+
38+
private static final String SOURCE_ATTR_NAME = "attribute";
39+
private static final String VALIDITY_PERIOD_MONTHS = "validityPeriodMonths";
40+
41+
private final String sourceAttr;
42+
private final int validityPeriodMonths;
43+
44+
public IsEligibleClaimSource(ClaimSourceInitContext ctx) {
45+
super(ctx);
46+
47+
this.sourceAttr = ClaimUtils.fillStringMandatoryProperty(SOURCE_ATTR_NAME, ctx, getClaimName());
48+
this.validityPeriodMonths = ClaimUtils.fillIntegerPropertyOrDefaultVal(VALIDITY_PERIOD_MONTHS, ctx, DEFAULT_VALIDITY_PERIOD);
49+
log.debug("{} - SAML attribute: '{}', validity period in months: '{}'", getClaimName(), sourceAttr, validityPeriodMonths);
50+
}
51+
52+
@Override
53+
public Set<String> getAttrIdentifiers() {
54+
return Collections.singleton(sourceAttr);
55+
}
56+
57+
@Override
58+
public JsonNode produceValue(ClaimSourceProduceContext pctx) {
59+
SamlAuthenticationDetails details = pctx.getSamlAuthenticationDetails();
60+
if (details == null || details.getAttributes() == null || details.getAttributes().isEmpty()) {
61+
log.warn("{} - no attribute set to get the source attribute from, returning FALSE", getClaimName());
62+
return JsonNodeFactory.instance.booleanNode(false);
63+
}
64+
String[] attrValue = details.getAttributes().getOrDefault(sourceAttr, new String[] {});
65+
if (attrValue == null || attrValue.length == 0) {
66+
log.warn("{} - no attribute to construct value from, returning FALSE", getClaimName());
67+
return JsonNodeFactory.instance.booleanNode(false);
68+
} else {
69+
if (attrValue.length > 1) {
70+
log.warn("{} - configured source attribute '{}' has more than one value, will use just the first one", getClaimName(), sourceAttr);
71+
}
72+
String timestamp = attrValue[0];
73+
return JsonNodeFactory.instance.booleanNode(isEligible(timestamp));
74+
}
75+
}
76+
77+
private boolean isEligible(String eligibleLastSeenValue) {
78+
if (!StringUtils.hasText(eligibleLastSeenValue)) {
79+
return false;
80+
}
81+
LocalDate eligibleLastSeenTimestamp;
82+
try {
83+
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(TIMESTAMP_FORMAT);
84+
eligibleLastSeenTimestamp = LocalDate.parse(eligibleLastSeenValue, formatter);
85+
} catch (DateTimeParseException e) {
86+
log.warn("{} - could not parse value '{}' as timestamp in format '{}'. Returning NOT ELIGIBLE.",
87+
getClaimName(), eligibleLastSeenValue, TIMESTAMP_FORMAT);
88+
return false;
89+
}
90+
91+
LocalDate now = LocalDateTime.now().toLocalDate();
92+
boolean isValid = !eligibleLastSeenTimestamp.isBefore(now.minusMonths(validityPeriodMonths));
93+
log.debug("{} - timestamp '{}' is time {} the defined period of '{} months'",
94+
getClaimName(), eligibleLastSeenTimestamp, isValid ? "within" : "out of", DEFAULT_VALIDITY_PERIOD);
95+
return isValid;
96+
}
97+
98+
}

perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/filters/FiltersUtils.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,15 +357,23 @@ public static void redirectUserCannotAccess(String base,
357357
public static String fillStringMandatoryProperty(String propertyName,
358358
String filterName,
359359
AuthProcFilterInitContext params) {
360-
String filled = params.getProperty(propertyName);
360+
String filled = fillStringProperty(propertyName, params, null);
361361

362-
if (!StringUtils.hasText(filled)) {
362+
if (filled == null) {
363363
throw new IllegalArgumentException("No value configured for '" + propertyName + "' in filter " + filterName);
364364
}
365365

366366
return filled;
367367
}
368368

369+
public static String fillStringProperty(String propertyName, AuthProcFilterInitContext params, String defaultValue) {
370+
String filled = params.getProperty(propertyName);
371+
if (!StringUtils.hasText(filled)) {
372+
return defaultValue;
373+
}
374+
return filled;
375+
}
376+
369377
private static void redirectToRegistrationForm(String base, HttpServletResponse response,
370378
String clientIdentifier, Facility facility, PerunUser user) {
371379
Map<String, String> params = new HashMap<>();

0 commit comments

Comments
 (0)