5555import cz .metacentrum .perun .core .api .exceptions .MfaInvalidRolesException ;
5656import cz .metacentrum .perun .core .api .exceptions .MfaPrivilegeException ;
5757import cz .metacentrum .perun .core .api .exceptions .MfaRolePrivilegeException ;
58+ import cz .metacentrum .perun .core .api .exceptions .MfaTimeoutException ;
5859import cz .metacentrum .perun .core .api .exceptions .PolicyNotExistsException ;
5960import cz .metacentrum .perun .core .api .exceptions .ResourceNotExistsException ;
6061import cz .metacentrum .perun .core .api .exceptions .RoleAlreadySetException ;
100101
101102import static cz .metacentrum .perun .core .api .AuthzResolver .MFA_CRITICAL_ATTR ;
102103import static cz .metacentrum .perun .core .api .PerunPrincipal .ACCESS_TOKEN ;
104+ import static cz .metacentrum .perun .core .api .PerunPrincipal .ACR_MFA ;
105+ import static cz .metacentrum .perun .core .api .PerunPrincipal .AUTH_TIME ;
103106import static cz .metacentrum .perun .core .api .PerunPrincipal .ISSUER ;
104- import static cz .metacentrum .perun .core .api .PerunPrincipal .MFA_TIMESTAMP ;
105107import static org .apache .commons .lang3 .StringUtils .isBlank ;
106108
107109/**
@@ -2562,7 +2564,7 @@ public static synchronized void refreshAuthz(PerunSession sess) {
25622564 sess .getPerunPrincipal ().getRoles ().clear ();
25632565 }
25642566
2565- if (isAuthorizedByMfa (sess )) {
2567+ if (isAuthorizedByMfa (sess , false )) {
25662568 sess .getPerunPrincipal ().getRoles ().putAuthzRole (Role .MFA );
25672569 }
25682570 }
@@ -2623,10 +2625,6 @@ public static void refreshMfa(PerunSession sess) throws ExpiredTokenException, M
26232625 throw new MFAuthenticationException ("MFA enforcement is turned off" );
26242626 }
26252627
2626- if (!BeansUtils .getCoreConfig ().getRequestUserInfoEndpoint ()) {
2627- throw new MFAuthenticationException ("Cannot verify MFA - UserInfo endpoint not configured." );
2628- }
2629-
26302628 String accessToken = sess .getPerunPrincipal ().getAdditionalInformations ().get (ACCESS_TOKEN );
26312629 if (accessToken == null ) {
26322630 throw new MFAuthenticationException ("Cannot verify MFA - access token is missing." );
@@ -2637,7 +2635,7 @@ public static void refreshMfa(PerunSession sess) throws ExpiredTokenException, M
26372635 throw new MFAuthenticationException ("Cannot verify MFA - issuer is missing." );
26382636 }
26392637
2640- if (isAuthorizedByMfa (sess )) {
2638+ if (isAuthorizedByMfa (sess , true )) {
26412639 sess .getPerunPrincipal ().getRoles ().putAuthzRole (Role .MFA );
26422640 }
26432641 }
@@ -4356,34 +4354,47 @@ private static Map<String, Integer> createMappingOfValues(PerunBean complementar
43564354
43574355 /**
43584356 * Checks, if principal was authorized by Multi-factor authentication.
4359- * The information is resolved in UserInfoEndpointCall and stored in principal's additionalInformations.
4360- * The timestamp of MFA must not be older than mfa timeout set in config.
4357+ * The information is resolved from headers (apache IntrospectionEndpoint call) and stored in principal's additionalInformations.
4358+ * Check if the auth time + mfa timeout is not older than the current time
4359+ * auth time = time of the first authentication
4360+ * mfa timeout = amount of time defined in the config for how long the MFA should be valid (since SFA)
43614361 *
43624362 * @param sess session
4363+ * @param throwError if this method should throw errors or just return boolean
43634364 * @return true if principal authorized by MFA in allowed limit, false otherwise
43644365 */
4365- private static boolean isAuthorizedByMfa (PerunSession sess ) {
4366+ private static boolean isAuthorizedByMfa (PerunSession sess , boolean throwError ) {
43664367 if (!BeansUtils .getCoreConfig ().isEnforceMfa ()) {
43674368 return false ;
43684369 }
43694370
4370- if (!sess .getPerunPrincipal ().getAdditionalInformations ().containsKey (MFA_TIMESTAMP )) {
4371- return false ;
4372- }
4373-
4374- String returnedTimestamp = sess .getPerunPrincipal ().getAdditionalInformations ().get (MFA_TIMESTAMP );
4375- Instant parsedReturnedTimestamp ;
4371+ String returnedAuthTime = sess .getPerunPrincipal ().getAdditionalInformations ().get (AUTH_TIME );
4372+ Instant parsedReturnedAuthTime ;
43764373 try {
4377- parsedReturnedTimestamp = Instant .parse (returnedTimestamp );
4374+ parsedReturnedAuthTime = Instant .parse (returnedAuthTime );
43784375 } catch (DateTimeParseException e ) {
4379- throw new InternalErrorException ("MFA timestamp " + returnedTimestamp + " could not be parsed" , e );
4376+ throw new InternalErrorException ("MFA timestamp " + returnedAuthTime + " could not be parsed" , e );
43804377 }
4381- if (parsedReturnedTimestamp .isAfter (Instant .now ())) {
4382- throw new InternalErrorException ("MFA auth timestamp " + returnedTimestamp + " was greater than current time" );
4378+ if (parsedReturnedAuthTime .isAfter (Instant .now ())) {
4379+ throw new InternalErrorException ("MFA auth timestamp " + returnedAuthTime + " was greater than current time" );
43834380 }
43844381
4385- long mfaTimeoutInSec = Duration .ofHours (BeansUtils .getCoreConfig ().getMfaAuthTimeout ()).getSeconds ();
4386- Instant mfaValidUntil = parsedReturnedTimestamp .plusSeconds (mfaTimeoutInSec );
4387- return mfaValidUntil .isAfter (Instant .now ());
4382+ long mfaTimeoutInSec = Duration .ofMinutes (BeansUtils .getCoreConfig ().getMfaAuthTimeout ()).getSeconds ();
4383+ Instant mfaValidUntil = parsedReturnedAuthTime .plusSeconds (mfaTimeoutInSec );
4384+
4385+ // check if the auth time + mfa timeout > the current time
4386+ if (mfaValidUntil .isAfter (Instant .now ())) {
4387+ // if user has MFA and it is still valid
4388+ return sess .getPerunPrincipal ().getAdditionalInformations ().containsKey (ACR_MFA );
4389+ } else {
4390+ if (!throwError ) return false ;
4391+ if (sess .getPerunPrincipal ().getAdditionalInformations ().containsKey (ACR_MFA )) {
4392+ // MFA is no longer valid
4393+ throw new MfaTimeoutException ("Your MFA timestamp " + returnedAuthTime + " is not valid anymore, you'll need to reauthenticate" );
4394+ } else {
4395+ // user is authenticated by SFA but the mfa timeout would cause an error, so we need to reauthenticate this user
4396+ throw new MfaTimeoutException ("Your single factor authentication timestamp " + returnedAuthTime + " is not valid anymore, you'll need to reauthenticate" );
4397+ }
4398+ }
43884399 }
43894400}
0 commit comments