Grouper and Shibboleth front door authorization

Grouper and Shibboleth front door authorization

 

Penn front door authorization as presented in the June 2025 InCommon IAM Online.

 

Summary

See this marketing video to explain front door authorization.

Shibboleth SAML Service Providers (SP's) can have a front door authorization policy assigned which will ensure that users are eligible for the service.

When a user is not allowed to access an application (SP), they are sent an error page (could be customized or an external link) instead of the application.

There are currently a dozen coarse-grained options for the eligibility population (described below).  A policy group could be used instead of or in addition.

Application admins can self-manage their front-door configuration.

This is an extra security control on top of whatever specific authorization a service performs.  You can think of it like a security guard at the front of a building.  Applications still need to provide the same authorization, but deprovisioning is not as urgent.

Frequently asked questions

  1. If my application is protected by Workforce, and someone is being blocked, how do I remedy that?

    1. If the user is working at Penn, they should have an affiliation which reflects that relationship.  Assuming they are not paid by Penn, their sponsor should give them a contractor or research affiliation.

    2. If the user is not working at Penn, then perhaps the Workforce coarse grained reference group is not appropriate for this service and the front door for this service should be reconfigured to use something wider (e.g. Member which includes students and related organizations like the children's hospital).

  2. Does this affect local log ins?

    1. If a user is authenticating and is not using Shibboleth, (i.e. a local password), then the front door is not used

    2. Note: your application should not allow local logins by policy.  You might have a local login extraordinary situations (e.g. for super admins who need to reconfigure SSO).

  3. How will this affect the user experience?

    1. The user will not know the front door is being used, unless they leave Penn or their reference/policy group

  4. Do I have to provide a list of at least two admins or owners?

    1. No, you can let the IT dept manage the front door if you do not have the technical staff to manage it

User flow

 

  1. User attempts to log in to an application

  2. Shibboleth determines which population is eligible for that service based on how it is configured.  The membership of that PennGroup (eligible population) is checked for the user (i.e. are they eligible)

  3. If the user is in the population, the user is sent to the service.  

    1. The service has more specific authorization checks and the user might be able to use the application or see an application error page

  4. If the user is not in the population, the user will be sent to a generic error handler at Grouper

  5. If the service does not have any Grouper error handling configured, the user will go to a generic error page

  6. The error page could be hosted by the service owner at some location (not on the service that is protected)

  7. The error page in Grouper could be customized to provide specific information to the user

    1. To tell the user whom to contact for support

    2. To clearly communicate the access requirements

Coarse-grained eligibility populations

Note: “Requires Two-step” means all users must be enrolled in Duo.

If the application selects a reference group for the eligibility requirement, they can pick: Workforce, Workforce in Two-Step, Member, Member in Two-step, etc.

Error page - generic

This is a dynamic page that will attempt to reduce tickets based on the state of the user.

  • If the service has no eligibility, then users will never see the error page.

  • If the service has only a reference group eligibility, then if the user is not in that group then they will see this page.  If the reference group requires Two-step then the user must be enrolled (not bypassed) in Two-Step.

  • If the service has only a policy group eligibility, then if the user is not in that group then they will see this page.  If the policy group requires Two-step (via composite) then the user must be enrolled (not bypassed) in Two-Step.  It is easier to add a reference group that requires Two-step.

  • If the service has both a reference group and a policy group then the user must be in both groups.  If the reference group requires Two-step then the user must be enrolled (not bypassed) in Two-Step.

 

Basic page:

Two step error:

 

Timing error:

 

 

Generic error page - custom UI

See examples of screen above

grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.0.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.0.label = Entity ID grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.0.order = 10 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.0.userQueryType = url grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.0.variableToAssign = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.0.variableType = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.1.bindVar0 = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.1.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.1.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.1.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.1.label = SP name grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.1.order = 20 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.1.query = select name from sso_prod_entity_front_door where entity_id = ? grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.1.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.1.variableToAssign = cu_spName grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.1.variableType = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.10.bindVar0 = \u0024{subject.id} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.10.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.10.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.10.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.10.label = Has been in workforce grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.10.order = 110 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.10.query = select case when exists (select 1 from penn_custom_ui_mships_v where group_name = 'penn\u003Acommunity\u003AemployeeOrContractorIncludingUphs' and subject_id = ? and subject_source_id in ('pennperson') and has_been_in_group = 'T') then 1 else 0 end as has_been_in_group grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.10.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.10.variableToAssign = cu_hasBeenInWorkforceGroup grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.10.variableType = boolean grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.11.bindVar0 = \u0024{subject.id} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.11.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.11.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.11.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.11.label = Workforce membership ended minutes ago grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.11.order = 120 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.11.query = select coalesce(minutes_since_in_group, 99999999) as min_since_in_group from (select ((extract(epoch from now()) - user_end_secs_since_1970)/60)\u003A\u003Anumeric\u003A\u003Ainteger as minutes_since_in_group from penn_custom_ui_mships_v where group_name = 'penn\u003Acommunity\u003AemployeeOrContractorIncludingUphs' and subject_id = ? and user_end_secs_since_1970 is not null and subject_source_id in ('pennperson') limit 1) as minutes_since_in_group_table grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.11.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.11.variableToAssign = cu_workforceEndedMinutesAgo grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.11.variableType = integer grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.12.bindVar0 = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.12.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.12.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.12.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.12.label = Requires two-step grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.12.order = 35 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.12.query = select case when exists (select 1 from sso_prod_entity_front_door where entity_id = ? and requires_twostep = 'T') then 1 else 0 end as requires_twostep_integer grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.12.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.12.variableToAssign = cu_requiresTwostep grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.12.variableType = boolean grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.13.bindVar0 = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.13.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.13.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.13.label = Error page type grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.13.order = 140 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.13.query = select error_page_type from sso_prod_entity_front_door where entity_id = ? grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.13.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.13.variableToAssign = cu_errorPageType grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.14.bindVar0 = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.14.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.14.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.14.label = Error page URL grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.14.order = 150 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.14.query = select error_page_url from sso_prod_entity_front_door where entity_id = ? grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.14.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.14.variableToAssign = cu_errorPageUrl grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.15.bindVar0 = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.15.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.15.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.15.label = Error page text grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.15.order = 160 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.15.query = select error_page_text || '<br /><br />' from sso_prod_entity_front_door where entity_id = ? grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.15.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.15.variableToAssign = cu_errorPageText grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.2.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.2.label = SP found grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.2.order = 30 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.2.script = \u0024{!grouperUtil.isBlank(cu_var) && !grouperUtil.isBlank(cu_spName)} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.2.userQueryType = expressionLanguage grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.2.variableToAssign = cu_spFound grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.2.variableType = boolean grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.3.bindVar0 = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.3.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.3.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.3.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.3.label = Eligibility group grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.3.order = 40 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.3.query = select group_name from sso_prod_entity_groups_v where entity_id = ? grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.3.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.3.variableToAssign = cu_frontDoorPolicyGroup grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.3.variableType = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.bindVar0 = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.bindVar1 = \u0024{subject.id} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.bindVar1Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.label = In eligibility group grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.order = 50 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.query = select is_in_group from sso_prod_entity_mships2_v where entity_id = ? and subject_id = ? grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.variableToAssign = cu_inEligibilityGroup grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.4.variableType = boolean grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.bindVar0 = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.bindVar1 = \u0024{subject.id} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.bindVar1Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.label = Has been in eligibility group grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.order = 60 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.query = select has_been_in_group from sso_prod_entity_mships2_v where entity_id = ? and subject_id = ? grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.variableToAssign = cu_hasBeenInEligibilityGroup grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.5.variableType = boolean grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.bindVar0 = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.bindVar1 = \u0024{subject.id} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.bindVar1Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.label = Group membership ended minutes ago grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.order = 70 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.query = select min_since_in_group from sso_prod_entity_mships2_v where entity_id = ? and subject_id = ? grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.variableToAssign = cu_groupMembershipEndedMinutesAgo grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.6.variableType = integer grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.bindVar0 = cu_var grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.bindVar1 = \u0024{subject.id} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.bindVar1Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.label = Has been in group for minutes grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.order = 80 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.query = select min_been_in_group from sso_prod_entity_mships2_v where entity_id = ? and subject_id = ? grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.variableToAssign = cu_beenInGroupForMinutes grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.7.variableType = integer grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.8.bindVar0 = \u0024{subject.id} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.8.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.8.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.8.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.8.label = In two step group grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.8.order = 90 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.8.query = select case when exists (select 1 from penn_custom_ui_mships_v where group_name = 'penn\u003Acommunity\u003Aauthentication\u003AtwoStepUsers' and subject_id = ? and subject_source_id in ('pennperson') and is_user = 'T') then 1 else 0 end as is_user grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.8.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.8.variableToAssign = cu_inTwoStepGroup grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.8.variableType = boolean grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.9.bindVar0 = \u0024{subject.id} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.9.bindVar0Type = string grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.9.configId = grouper grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.9.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.9.label = Has been in two-step group for minutes grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.9.order = 100 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.9.query = select minutes_been_in_group as min_been_in_group from (select ((extract(epoch from now()) - user_start_secs_since_1970)/60)\u003A\u003Anumeric\u003A\u003Ainteger as minutes_been_in_group from penn_custom_ui_mships_v where group_name = 'penn\u003Acommunity\u003Aauthentication\u003AtwoStepUsers' and subject_id = ? and user_start_secs_since_1970 is not null and subject_source_id in ('pennperson') limit 1) as minutes_been_in_group_table grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.9.userQueryType = sql grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.9.variableToAssign = cu_beenInTwoStepGroupForMinutes grouperCustomUI.webLoginFrontDoorErrorPageProd.cuQuery.9.variableType = integer grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.0.defaultText = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.0.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.0.endIfMatches = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.0.index = 10 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.0.text = <h1>Penn WebLogin - not authorized</h1> grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.0.textType = header grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.1.defaultText = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.1.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.1.endIfMatches = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.1.index = 10 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.1.text =   grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.1.textType = helpLink grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.10.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.10.endIfMatches = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.10.index = -100 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.10.script = \u0024{cu_errorPageType == 'other_url' } grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.10.text = \u0024{cu_errorPageUrl} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.10.textType = redirectToUrl grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.2.defaultText = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.2.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.2.index = 10 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.2.textBoolean = false grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.2.textType = enrollButtonShow grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.3.endIfMatches = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.3.index = 0 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.3.text = Find a person to see their error page grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.3.textType = managerInstructions grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.4.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.4.endIfMatches = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.4.index = 10 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.4.script = \u0024{grouperUtil.isBlank(cu_var) || grouperUtil.isBlank(cu_spName)} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.4.text = The specifics of the error is unknown. You are not allowed to access the application. Discuss your status with your university manager or sponsor. grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.4.textType = enrollmentLabel grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.5.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.5.endIfMatches = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.5.index = 20 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.5.script = \u0024{cu_spFound && cu_inEligibilityGroup && cu_beenInGroupForMinutes > 30 && (!cu_requiresTwostep || (cu_inTwoStepGroup && cu_beenInTwoStepGroupForMinutes > 30)) } grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.5.text = You are allowed to access the application\u003A \u0024{grouperUtil.defaultString(cu_spName)}. You should not be experiencing an error. Wait some time and try again or open a ticket to resolve the issue. grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.5.textType = enrollmentLabel grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.6.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.6.endIfMatches = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.6.index = 30 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.6.script = \u0024{cu_spFound && cu_inEligibilityGroup && (!cu_requiresTwostep || (cu_inTwoStepGroup )) } grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.6.text = You are allowed to access the application\u003A \u0024{grouperUtil.defaultString(cu_spName)}. Since your access has been granted recently, and it takes thirty minutes for the access to be enabled, please try again shortly. grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.6.textType = enrollmentLabel grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.7.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.7.index = 40 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.7.script = \u0024{cu_spFound && cu_requiresTwostep & !cu_inTwoStepGroup } grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.7.text = <font style="color\u003Abrown"><b>Error\u003A</b></font> You must enroll in <a href="https\u003A//www.isc.upenn.edu/how-to/two-step-verification-getting-started">Two-Step Verification</a><br /> grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.7.textType = enrollmentLabel grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.8.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.8.index = 50 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.8.script = \u0024{cu_spFound && !cu_inEligibilityGroup} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.8.text = <font style="color\u003Abrown"><b>Error\u003A</b></font> You are not authorized to access the application\u003A \u0024{grouperUtil.defaultString(cu_spName, "")}. Discuss your status with your university manager or sponsor.<br /> grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.8.textType = enrollmentLabel grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.9.enabled = true grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.9.index = -10 grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.9.script = \u0024{cu_errorPageType == 'customized'} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.9.text = \u0024{cu_errorPageText} grouperCustomUI.webLoginFrontDoorErrorPageProd.cuTextConfig.9.textType = enrollmentLabel grouperCustomUI.webLoginFrontDoorErrorPageProd.groupCanAssignVariables = penn\u003Aisc\u003Ats\u003Aiam\u003Aweblogin\u003Aservice\u003Apolicy\u003AidpFrontDoorProd\u003Asecurity\u003AidpFrontDoorProdCustomUIAdmins grouperCustomUI.webLoginFrontDoorErrorPageProd.groupCanSeeScreenState = penn\u003Aisc\u003Ats\u003Aiam\u003Aweblogin\u003Aservice\u003Apolicy\u003AidpFrontDoorProd\u003Asecurity\u003AidpFrontDoorProdCustomUIAdmins grouperCustomUI.webLoginFrontDoorErrorPageProd.groupCanSeeUserEnvironment = penn\u003Aisc\u003Ats\u003Aiam\u003Aweblogin\u003Aservice\u003Apolicy\u003AidpFrontDoorProd\u003Asecurity\u003AidpFrontDoorProdCustomUIAdmins grouperCustomUI.webLoginFrontDoorErrorPageProd.groupOfManagers = penn\u003Aisc\u003Ats\u003Aiam\u003Aweblogin\u003Aservice\u003Apolicy\u003AidpFrontDoorProd\u003Asecurity\u003AidpFrontDoorProdCustomUIAdmins grouperCustomUI.webLoginFrontDoorErrorPageProd.groupUUIDOrName = penn\u003Aisc\u003Ats\u003Aiam\u003Aweblogin\u003Aservice\u003Apolicy\u003AidpFrontDoorProd\u003Aservice\u003Aref\u003AidpFrontDoorProdErrorPage grouperCustomUI.webLoginFrontDoorErrorPageProd.numberOfQueries = 16 grouperCustomUI.webLoginFrontDoorErrorPageProd.numberOfTextConfigs = 11

 

Error page - custom

This error page has custom HTML embedded.  Note all the dynamic features of the generic page exist in the custom page, and it has additional static HTML information as configured.

 

Feed from splunk to Grouper about log ins to service providers

There is a table for entity id's

There is a table of login logs from splunk

This is populated by GSH daemon which runs every ten minutes

import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TimeZone; import org.apache.commons.lang3.StringUtils; import com.fasterxml.jackson.databind.JsonNode; import edu.internet2.middleware.grouper.GrouperSession; import edu.internet2.middleware.grouper.Member; import edu.internet2.middleware.grouper.MemberFinder; import edu.internet2.middleware.grouper.SubjectFinder; import edu.internet2.middleware.grouper.app.loader.GrouperLoaderConfig; import edu.internet2.middleware.grouper.app.loader.OtherJobScript; import edu.internet2.middleware.grouper.misc.GrouperStartup; import edu.internet2.middleware.grouper.util.GrouperHttpClient; import edu.internet2.middleware.grouper.util.GrouperHttpMethod; import edu.internet2.middleware.grouper.util.GrouperUtil; import edu.internet2.middleware.grouperClient.jdbc.GcDbAccess; import edu.internet2.middleware.grouperClient.util.GrouperClientUtils; import edu.internet2.middleware.subject.Subject; //public class Test79splunkSpLogins { public static Set<Long> memberIdIndexInGroup(String groupName, Collection<Long> memberIdIndexesCollection) { int theBatchSize = 1000; List<Long> memberIdIndexesList = new ArrayList<>(GrouperUtil.nonNull(memberIdIndexesCollection)); int theNumberOfBatches = GrouperUtil.batchNumberOfBatches(memberIdIndexesList, theBatchSize, false); Set<Long> resultMemberIdIndexes = new HashSet<>(); // go through in batches for (int i=0;i<theNumberOfBatches;i++) { List<Long> memberIdIndexesBatch = GrouperUtil.batchList(memberIdIndexesList, theBatchSize, i); List<Long> memberIdIndexesExist = new GcDbAccess().connectionName("awsProdReadonly").sql( "select gm.id_index from grouper_memberships_lw_v gmlv, grouper_members gm where " + " gm.id = gmlv.member_id and gmlv.list_name = 'members' " + " and gmlv.group_name = ? and gm.id_index in (" + GrouperClientUtils.appendQuestions(memberIdIndexesBatch.size()) + ")"). addBindVar(groupName).addBindVars(memberIdIndexesBatch).selectList(Long.class); resultMemberIdIndexes.addAll(memberIdIndexesExist); } return resultMemberIdIndexes; } // public static void main(String[] args) { GrouperStartup.startup(); GrouperSession grouperSession = GrouperSession.startRootSession(); Map<String, Object> debugMap = new LinkedHashMap<>(); String splunkBaseUrl = GrouperUtil.stripLastSlashIfExists(GrouperLoaderConfig.retrieveConfig().propertyValueStringRequired("splunk.url")); String splunkBearerToken = GrouperLoaderConfig.retrieveConfig().propertyValueStringRequired("splunk.bearerTokenSecret"); long startedMillis = System.currentTimeMillis(); // https://sls.isc.upenn.edu:8089/servicesNS/-/upenn_isc_attribution/search/jobs/export?search=search%20%60sso_authentications%60&exec_mode=oneshot&output_mode=json GrouperHttpClient grouperHttpClient = new GrouperHttpClient(). assignGrouperHttpMethod(GrouperHttpMethod.get). addHeader("Content-Type", "application/json"). addHeader("Accept", "application/json"). addHeader("Authorization", "Bearer " + splunkBearerToken). assignUrl(splunkBaseUrl + "/servicesNS/-/upenn_isc_attribution/search/jobs/export"). addUrlParameter("search", "search `sso_authentications`"). addUrlParameter("output_mode", "json"). addUrlParameter("exec_mode", "oneshot"). executeRequest(); int responseCode = grouperHttpClient.getResponseCode(); String body = grouperHttpClient.getResponseBody(); if (responseCode != 200) { debugMap.put("splunkResponseCode", responseCode); throw new RuntimeException(responseCode + "," + body); } debugMap.put("splunkQueryTookMs", System.currentTimeMillis() - startedMillis); // {"preview":false,"offset":37239,"result":{"_time":"2024-01-25 11:08:31.132 EST","user":"aaravrr","dest_app":"courses.upenn.edu/sam_rMReby8Vl3M1BiyVWMAT"}} // {"preview":false,"offset":37252,"result":{"_time":"2024-01-25 11:08:28.546 EST","user":"baronche","dest_app":"https://cluster-prod.apps.upenn.edu/shibboleth"}} String[] bodyLines = new String[0]; if (!StringUtils.isBlank(body)) { bodyLines = GrouperUtil.splitTrim(body, "\n"); } int linesTotal = bodyLines.length; debugMap.put("linesTotal", linesTotal); int linesDontMatch = 0; int invalidDates = 0; int invalidUsers = 0; Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("America/New_York")); Set<String> pennkeys = new HashSet<String>(); Set<String> pennids = new HashSet<String>(); Set<String> entityIds = new HashSet<String>(); Timestamp minTimestamp = null; List<Object[]> timeUserSps = new ArrayList<Object[]>(); Timestamp maxTimestampAlreadyLogged = new GcDbAccess().sql("select max(login_timestamp) from sso_prod_logs_person_sp").select(Timestamp.class); if (maxTimestampAlreadyLogged == null) { maxTimestampAlreadyLogged = new Timestamp(0L); } int recordsAlreadyLogged = 0; Map<String, Set<String>> entityIdToPennkeyOrPennid = new HashMap<>(); for (String bodyLine : bodyLines) { if (StringUtils.isBlank(bodyLine)) { continue; } JsonNode mainNode = GrouperUtil.jsonJacksonNode(bodyLine); JsonNode resultNode = mainNode == null ? null : GrouperUtil.jsonJacksonGetNode(mainNode, "result"); String time = resultNode == null ? null : GrouperUtil.jsonJacksonGetString(resultNode, "_time"); String user = resultNode == null ? null : GrouperUtil.jsonJacksonGetString(resultNode, "user"); String dest_app = resultNode == null ? null : GrouperUtil.jsonJacksonGetString(resultNode, "dest_app"); String timeSuffix1 = " EST"; String timeSuffix2 = " EDT"; if (StringUtils.isBlank(time) || StringUtils.isBlank(user) || StringUtils.isBlank(dest_app) || (!time.endsWith(timeSuffix1) && !time.endsWith(timeSuffix2))) { if (linesDontMatch < 10) { debugMap.put("linesDontMatchExample_" + linesDontMatch, bodyLine); } linesDontMatch++; continue; } time = time.substring(0, time.length() - timeSuffix1.length()); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); df.setTimeZone(cal.getTimeZone()); Timestamp timestamp = null; try { Date date = null; date = df.parse(time); timestamp = new Timestamp(date.getTime()); } catch (Exception e) { if (invalidDates < 10) { debugMap.put("invalidDatesExample_" + invalidDates, time); } invalidDates++; } if (timestamp.before(maxTimestampAlreadyLogged)) { recordsAlreadyLogged++; continue; } if (!user.matches("[a-z0-9]{2,8}")) { if (invalidUsers < 10) { debugMap.put("invalidUser_" + invalidUsers, user); } if (OtherJobScript.retrieveFromThreadLocal() == null) { System.out.println("Invalid user: " + user); } invalidUsers++; } if (user.matches("[0-9]{2,8}")) { pennids.add(user); } else { pennkeys.add(user); } entityIds.add(dest_app); if (minTimestamp == null || timestamp.before(minTimestamp)) { minTimestamp = timestamp; } timeUserSps.add(GrouperUtil.toArrayObject(timestamp, user, dest_app)); Set<String> pennkeyOrPennids = entityIdToPennkeyOrPennid.get(dest_app); if (pennkeyOrPennids == null) { pennkeyOrPennids = new HashSet<>(); entityIdToPennkeyOrPennid.put(dest_app, pennkeyOrPennids); } pennkeyOrPennids.add(user); } if (OtherJobScript.retrieveFromThreadLocal() != null) { OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().setTotalCount(bodyLines.length); } debugMap.put("pennkeysCount", pennkeys.size()); debugMap.put("pennidsCount", pennids.size()); debugMap.put("recordsAlreadyLogged", recordsAlreadyLogged); debugMap.put("linesDontMatch", linesDontMatch); debugMap.put("invalidUsers", invalidUsers); debugMap.put("matchedLinesTotal", timeUserSps.size()); Map<String, Long> loginIdToIdIndex = new HashMap<String, Long>(); Map<String, String> loginIdToPennid = new HashMap<String, String>(); // retrieve / create users List<String> pennkeyList = new ArrayList<String>(pennkeys); int batchSize = 1000; int numberOfBatches = GrouperUtil.batchNumberOfBatches(pennkeyList.size(), batchSize, false); int membersCreated = 0; int subjectsNotFound = 0; for (int i=0;i<numberOfBatches;i++) { List<String> batchPennkeys = GrouperUtil.batchList(pennkeyList, batchSize, i); Set<String> batchPennkeysSet = new HashSet<String>(batchPennkeys); String sql = """ select gm.subject_identifier0 AS pennkey, id_index, subject_id from grouper_members gm """ + " where subject_identifier0 in (" + GrouperClientUtils.appendQuestions(batchPennkeys.size()) + ")"; GcDbAccess gcDbAccess = new GcDbAccess().sql(sql).connectionName("awsProdReadonly"); for (String pennkey : batchPennkeys) { gcDbAccess.addBindVar(pennkey); } List<Object[]> subjectIdentifierIdIndexes = gcDbAccess.selectList(Object[].class); for (Object[] subjectIdentifierIdIndex : subjectIdentifierIdIndexes) { String subjectIdentifier = (String)subjectIdentifierIdIndex[0]; Long idIndex = GrouperUtil.longValue(subjectIdentifierIdIndex[1]); batchPennkeysSet.remove(subjectIdentifier); loginIdToIdIndex.put(subjectIdentifier, idIndex); String pennId = (String)subjectIdentifierIdIndex[2]; loginIdToPennid.put(subjectIdentifier, pennId); } for (String pennkey : batchPennkeysSet) { Subject subject = SubjectFinder.findByIdentifierAndSource(pennkey, "pennperson", false); if (subject == null) { if (subjectsNotFound < 10) { debugMap.put("subjectNotFound_" + subjectsNotFound, pennkey); } subjectsNotFound++; continue; } loginIdToPennid.put(pennkey, subject.getId()); Member member = MemberFinder.findBySubject(grouperSession, subject, true); if (OtherJobScript.retrieveFromThreadLocal() != null) { OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().addInsertCount(1); } loginIdToIdIndex.put(pennkey, member.getIdIndex()); if (membersCreated < 10) { debugMap.put("memberCreated_" + membersCreated, pennkey); } membersCreated++; } } List<String> pennidList = new ArrayList<String>(pennids); numberOfBatches = GrouperUtil.batchNumberOfBatches(pennidList.size(), batchSize, false); int membersPennIdCreated = 0; int subjectsPennIdNotFound = 0; for (int i=0;i<numberOfBatches;i++) { List<String> batchPennids = GrouperUtil.batchList(pennidList, batchSize, i); Set<String> batchPennidsSet = new HashSet<String>(batchPennids); String sql = """ select gm.subject_id AS subject_id, id_index from grouper_members gm """ + " where subject_id in (" + GrouperClientUtils.appendQuestions(batchPennids.size()) + ")"; GcDbAccess gcDbAccess = new GcDbAccess().sql(sql).connectionName("awsProdReadonly"); for (String pennid : batchPennids) { gcDbAccess.addBindVar(pennid); loginIdToPennid.put(pennid, pennid); } List<Object[]> subjectIdIdIndexes = gcDbAccess.selectList(Object[].class); for (Object[] subjectIdIdIndex : subjectIdIdIndexes) { String subjectId = (String)subjectIdIdIndex[0]; Long idIndex = GrouperUtil.longValue(subjectIdIdIndex[1]); batchPennidsSet.remove(subjectId); loginIdToIdIndex.put(subjectId, idIndex); } for (String pennid : batchPennidsSet) { Subject subject = SubjectFinder.findByIdAndSource(pennid, "pennperson", false); if (subject == null) { if (subjectsPennIdNotFound < 10) { debugMap.put("subjectNotFound_" + subjectsPennIdNotFound, pennid); } subjectsPennIdNotFound++; continue; } Member member = MemberFinder.findBySubject(grouperSession, subject, true); if (OtherJobScript.retrieveFromThreadLocal() != null) { OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().addInsertCount(1); } loginIdToIdIndex.put(pennid, member.getIdIndex()); if (membersPennIdCreated < 10) { debugMap.put("memberCreated_" + membersPennIdCreated, pennid); } membersPennIdCreated++; } } debugMap.put("membersPennIdCreated", membersPennIdCreated); debugMap.put("subjectsPennIdNotFound", subjectsPennIdNotFound); // retrieve / create sps Map<String, Long> entityIdToIdIndex = new HashMap<String, Long>(); List<String> entityIdList = new ArrayList<String>(entityIds); numberOfBatches = GrouperUtil.batchNumberOfBatches(entityIdList.size(), batchSize, false); int entityIdsCreated = 0; Long maxEntityIdIndex = new GcDbAccess().sql("select max(id_index) from sso_prod_entity_id").select(long.class); if (maxEntityIdIndex == null) { maxEntityIdIndex = 0L; } else { maxEntityIdIndex++; } List<List<Object>> entityIdIdIndexToCreate = new ArrayList<>(); Map<String, String> entityIdToFrontDoorGroupName = new HashMap<>(); for (int i=0;i<numberOfBatches;i++) { List<String> batchEntityIds = GrouperUtil.batchList(entityIdList, batchSize, i); Set<String> batchEntityIdsSet = new HashSet<String>(batchEntityIds); String sql = "select spei.entity_id, spei.id_index, spefd.front_door_policy_group from sso_prod_entity_id spei " + " left join sso_prod_entity_front_door spefd on spei.entity_id = spefd.entity_id " + " where spei.entity_id in (" + GrouperClientUtils.appendQuestions(batchEntityIds.size()) + ")"; GcDbAccess gcDbAccess = new GcDbAccess().connectionName("awsProdReadonly").sql(sql); for (String sp : batchEntityIds) { gcDbAccess.addBindVar(sp); } List<Object[]> entityIdIdIndexGroupNames = gcDbAccess.selectList(Object[].class); for (Object[] entityIdIdIndexGroupName : entityIdIdIndexGroupNames) { String entityId = (String)entityIdIdIndexGroupName[0]; Long idIndex = GrouperUtil.longValue(entityIdIdIndexGroupName[1]); String frontDoorGroupName = (String)entityIdIdIndexGroupName[2]; batchEntityIdsSet.remove(entityId); entityIdToIdIndex.put(entityId, idIndex); if (!StringUtils.isBlank(frontDoorGroupName)) { entityIdToFrontDoorGroupName.put(entityId, frontDoorGroupName); } } for (String entityId : batchEntityIdsSet) { entityIdToIdIndex.put(entityId, maxEntityIdIndex); entityIdIdIndexToCreate.add(GrouperUtil.toListObject(entityId, maxEntityIdIndex)); if (OtherJobScript.retrieveFromThreadLocal() != null) { OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().addInsertCount(1); } if (entityIdsCreated < 10) { debugMap.put("entityIdCreated_" + entityIdsCreated, entityId); } entityIdsCreated++; maxEntityIdIndex++; } } new GcDbAccess().sql("insert into sso_prod_entity_id (entity_id, id_index) values (?, ?)").batchBindVars(entityIdIdIndexToCreate).executeBatchSql(); // insert data int logRecordsCreated = 0; int subjectsIdIndexNotFound = 0; Long maxLogRecordIdIndex = new GcDbAccess().sql("select max(id_index) from sso_prod_logs_person_sp").select(long.class); if (maxLogRecordIdIndex == null) { maxLogRecordIdIndex = 0L; } else { maxLogRecordIdIndex++; } Map<String, Set<Long>> entityIdHasFrontDoorGroupHasMemberIdIndexes = new HashMap(); for (String entityId : entityIdToFrontDoorGroupName.keySet()) { String frontDoorGroupName = entityIdToFrontDoorGroupName.get(entityId); Set<String> pennkeyOrPennids = entityIdToPennkeyOrPennid.get(entityId); List<Long> memberIdIndexes = new ArrayList<>(); for (String pennkeyOrPennid : pennkeyOrPennids) { Long memberIdIndex = loginIdToIdIndex.get(pennkeyOrPennid); if (memberIdIndex != null) { memberIdIndexes.add(memberIdIndex); } } Set<Long> memberIdsInGroup = memberIdIndexInGroup(frontDoorGroupName, memberIdIndexes); entityIdHasFrontDoorGroupHasMemberIdIndexes.put(entityId, memberIdsInGroup); } Set<Long> memberIdIndexes = new HashSet<>(loginIdToIdIndex.values()); Set<Long> twoStepMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:enrolledInTwoStep", memberIdIndexes); Set<Long> workforceMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:idpWorkforce", memberIdIndexes); Set<Long> memberMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:member", memberIdIndexes); Set<Long> affiliateMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:idpAffiliate", memberIdIndexes); Set<Long> recentAffiliateMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:idpRecentAffiliate", memberIdIndexes); Set<Long> alumOrAffiliateMemberIdIndexes = memberIdIndexInGroup("penn:isc:ts:iam:weblogin:service:policy:idpCoarseGrained:idpAffiliateOrAlum", memberIdIndexes); Set<Long> uphsOnlyMemberIdIndexes = memberIdIndexInGroup("penn:community:uphsOnly", memberIdIndexes); Set<Long> alumOnlyMemberIdIndexes = memberIdIndexInGroup("penn:community:alumni:alumniOnly", memberIdIndexes); Set<Long> lockedOutMemberIdIndexes = memberIdIndexInGroup("penn:etc:deprovisioning:usersWhoHaveBeenDeprovisioned_employee", memberIdIndexes); Set<Long> uphsNotPennpayNotStudentMemberIdIndexes = memberIdIndexInGroup("penn:community:uphsNotPennpayNotStudent", memberIdIndexes); List<List<Object>> logRecordIdIndexUserIndexSpIndexTimestamp = new ArrayList<>(); for (Object[] timeUserSp : timeUserSps) { Timestamp timestamp = (Timestamp)timeUserSp[0]; String pennkey = (String)timeUserSp[1]; Long userIdIndex = loginIdToIdIndex.get(pennkey); String pennid = loginIdToPennid.get(pennkey); String entityId = (String)timeUserSp[2]; Long entityIdIdIndex = entityIdToIdIndex.get(entityId); if (userIdIndex == null) { if (subjectsIdIndexNotFound < 10) { debugMap.put("subjectsIdIndexNotFound_" + subjectsIdIndexNotFound, entityId); } subjectsIdIndexNotFound++; continue; } Integer inFrontDoor = null; String frontDoorGroupName = entityIdToFrontDoorGroupName.get(entityId); if (!StringUtils.isBlank(frontDoorGroupName)) { Set<Long> memberIdsInFrontDoorGroup = entityIdHasFrontDoorGroupHasMemberIdIndexes.get(entityId); inFrontDoor = memberIdsInFrontDoorGroup != null && memberIdsInFrontDoorGroup.contains(userIdIndex) ? 1 : 0; } Integer nonPersistent = null; if (!StringUtils.isBlank(pennid)) { nonPersistent = pennid.startsWith("9") ? 1 : 0; } logRecordIdIndexUserIndexSpIndexTimestamp.add(GrouperUtil.toListObject(maxLogRecordIdIndex, userIdIndex, entityIdIdIndex, timestamp, twoStepMemberIdIndexes.contains(userIdIndex) ? 1 : 0, workforceMemberIdIndexes.contains(userIdIndex) ? 1 : 0, memberMemberIdIndexes.contains(userIdIndex) ? 1 : 0, affiliateMemberIdIndexes.contains(userIdIndex) ? 1 : 0, recentAffiliateMemberIdIndexes.contains(userIdIndex) ? 1 : 0, alumOrAffiliateMemberIdIndexes.contains(userIdIndex) ? 1 : 0, uphsOnlyMemberIdIndexes.contains(userIdIndex) ? 1 : 0, alumOnlyMemberIdIndexes.contains(userIdIndex) ? 1 : 0, inFrontDoor, lockedOutMemberIdIndexes.contains(userIdIndex) ? 1 : 0, nonPersistent, uphsNotPennpayNotStudentMemberIdIndexes.contains(userIdIndex) ? 1 : 0)); if (OtherJobScript.retrieveFromThreadLocal() != null) { OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().addInsertCount(1); } if (logRecordsCreated < 10) { debugMap.put("logRecordsCreated_" + logRecordsCreated, timestamp.toString() + ", " + pennkey + ", " + entityId); } logRecordsCreated++; maxLogRecordIdIndex++; } new GcDbAccess().sql("insert into sso_prod_logs_person_sp (id_index, user_member_id_index, sp_entity_id_index, login_timestamp, two_step_enrolled, workforce, member, " + " affiliate, recent_affiliate, alum_or_affiliate, uphs_only, alum_only, in_front_door, locked_out, " + "non_persistent, uphs_not_pennpay_not_student) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"). batchBindVars(logRecordIdIndexUserIndexSpIndexTimestamp).executeBatchSql(); debugMap.put("subjectsIdIndexNotFound", subjectsIdIndexNotFound); debugMap.put("logRecordsCreated", logRecordsCreated); if (OtherJobScript.retrieveFromThreadLocal() != null) { OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().appendJobMessage(GrouperUtil.mapToString(debugMap)); } else { System.exit(0); } // } //}

 

Managers and owners in Grouper for each service provider

Manage front door with GSH template

 

Config