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/**
@@ -2563,7 +2565,7 @@ public static synchronized void refreshAuthz(PerunSession sess) {
25632565 sess .getPerunPrincipal ().getRoles ().clear ();
25642566 }
25652567
2566- if (isAuthorizedByMfa (sess )) {
2568+ if (isAuthorizedByMfa (sess , false )) {
25672569 sess .getPerunPrincipal ().getRoles ().putAuthzRole (Role .MFA );
25682570 }
25692571 }
@@ -2624,10 +2626,6 @@ public static void refreshMfa(PerunSession sess) throws ExpiredTokenException, M
26242626 throw new MFAuthenticationException ("MFA enforcement is turned off" );
26252627 }
26262628
2627- if (!BeansUtils .getCoreConfig ().getRequestUserInfoEndpoint ()) {
2628- throw new MFAuthenticationException ("Cannot verify MFA - UserInfo endpoint not configured." );
2629- }
2630-
26312629 String accessToken = sess .getPerunPrincipal ().getAdditionalInformations ().get (ACCESS_TOKEN );
26322630 if (accessToken == null ) {
26332631 throw new MFAuthenticationException ("Cannot verify MFA - access token is missing." );
@@ -2638,7 +2636,7 @@ public static void refreshMfa(PerunSession sess) throws ExpiredTokenException, M
26382636 throw new MFAuthenticationException ("Cannot verify MFA - issuer is missing." );
26392637 }
26402638
2641- if (isAuthorizedByMfa (sess )) {
2639+ if (isAuthorizedByMfa (sess , true )) {
26422640 sess .getPerunPrincipal ().getRoles ().putAuthzRole (Role .MFA );
26432641 }
26442642 }
@@ -4357,34 +4355,47 @@ private static Map<String, Integer> createMappingOfValues(PerunBean complementar
43574355
43584356 /**
43594357 * Checks, if principal was authorized by Multi-factor authentication.
4360- * The information is resolved in UserInfoEndpointCall and stored in principal's additionalInformations.
4361- * The timestamp of MFA must not be older than mfa timeout set in config.
4358+ * The information is resolved from headers (apache IntrospectionEndpoint call) and stored in principal's additionalInformations.
4359+ * Check if the auth time + mfa timeout is not older than the current time
4360+ * auth time = time of the first authentication
4361+ * mfa timeout = amount of time defined in the config for how long the MFA should be valid (since SFA)
43624362 *
43634363 * @param sess session
4364+ * @param throwError if this method should throw errors or just return boolean
43644365 * @return true if principal authorized by MFA in allowed limit, false otherwise
43654366 */
4366- private static boolean isAuthorizedByMfa (PerunSession sess ) {
4367+ private static boolean isAuthorizedByMfa (PerunSession sess , boolean throwError ) {
43674368 if (!BeansUtils .getCoreConfig ().isEnforceMfa ()) {
43684369 return false ;
43694370 }
43704371
4371- if (!sess .getPerunPrincipal ().getAdditionalInformations ().containsKey (MFA_TIMESTAMP )) {
4372- return false ;
4373- }
4374-
4375- String returnedTimestamp = sess .getPerunPrincipal ().getAdditionalInformations ().get (MFA_TIMESTAMP );
4376- Instant parsedReturnedTimestamp ;
4372+ String returnedAuthTime = sess .getPerunPrincipal ().getAdditionalInformations ().get (AUTH_TIME );
4373+ Instant parsedReturnedAuthTime ;
43774374 try {
4378- parsedReturnedTimestamp = Instant .parse (returnedTimestamp );
4375+ parsedReturnedAuthTime = Instant .parse (returnedAuthTime );
43794376 } catch (DateTimeParseException e ) {
4380- throw new InternalErrorException ("MFA timestamp " + returnedTimestamp + " could not be parsed" , e );
4377+ throw new InternalErrorException ("MFA timestamp " + returnedAuthTime + " could not be parsed" , e );
43814378 }
4382- if (parsedReturnedTimestamp .isAfter (Instant .now ())) {
4383- throw new InternalErrorException ("MFA auth timestamp " + returnedTimestamp + " was greater than current time" );
4379+ if (parsedReturnedAuthTime .isAfter (Instant .now ())) {
4380+ throw new InternalErrorException ("MFA auth timestamp " + returnedAuthTime + " was greater than current time" );
43844381 }
43854382
4386- long mfaTimeoutInSec = Duration .ofHours (BeansUtils .getCoreConfig ().getMfaAuthTimeout ()).getSeconds ();
4387- Instant mfaValidUntil = parsedReturnedTimestamp .plusSeconds (mfaTimeoutInSec );
4388- return mfaValidUntil .isAfter (Instant .now ());
4383+ long mfaTimeoutInSec = Duration .ofMinutes (BeansUtils .getCoreConfig ().getMfaAuthTimeout ()).getSeconds ();
4384+ Instant mfaValidUntil = parsedReturnedAuthTime .plusSeconds (mfaTimeoutInSec );
4385+
4386+ // check if the auth time + mfa timeout > the current time
4387+ if (mfaValidUntil .isAfter (Instant .now ())) {
4388+ // if user has MFA and it is still valid
4389+ return sess .getPerunPrincipal ().getAdditionalInformations ().containsKey (ACR_MFA );
4390+ } else {
4391+ if (!throwError ) return false ;
4392+ if (sess .getPerunPrincipal ().getAdditionalInformations ().containsKey (ACR_MFA )) {
4393+ // MFA is no longer valid
4394+ throw new MfaTimeoutException ("Your MFA timestamp " + returnedAuthTime + " is not valid anymore, you'll need to reauthenticate" );
4395+ } else {
4396+ // user is authenticated by SFA but the mfa timeout would cause an error, so we need to reauthenticate this user
4397+ throw new MfaTimeoutException ("Your single factor authentication timestamp " + returnedAuthTime + " is not valid anymore, you'll need to reauthenticate" );
4398+ }
4399+ }
43894400 }
43904401}
0 commit comments