Skip to content

Commit

Permalink
restrict realm access based on the defined github team (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahirujayathilake authored Feb 6, 2025
1 parent a375f40 commit 8d31fcc
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 3 deletions.
1 change: 1 addition & 0 deletions keycloak-config-cli/config/master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ identityProviders:
clientSecret: $(env:GH_CLIENT_SECRET)
defaultScope: openid read:org user:email
organization: nasa-impact
team: veda-auth
caseSensitiveOriginalUsername: "false"
syncMode: FORCE

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ public class GithubOrgIdentityProvider extends GitHubIdentityProvider {

private final String apiUrl;
private final String organization;
private final String team;

private static final String DEFAULT_SCOPE = "user:email read:org";

public GithubOrgIdentityProvider(KeycloakSession session, OAuth2IdentityProviderConfig config) {
super(session, config);

organization = config.getConfig().get("organization");
team = config.getConfig().get("team");
apiUrl = super.getUrlFromConfig(config, super.API_URL_KEY, super.DEFAULT_API_URL);
}

Expand All @@ -38,13 +40,21 @@ protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
throw new IdentityBrokerException("User is not a member of the required organization.");
}
String username = user.getUsername();
boolean isMember = checkOrganizationMembership(accessToken, organization, username);
if (!isMember) {
boolean isOrgMember = checkOrganizationMembership(accessToken, organization, username);
if (!isOrgMember) {
logger.warn(String.format("User '%s' is NOT a member of the required organization '%s.", username, organization));
throw new IdentityBrokerException("User is not a member of the required organization.");
}

logger.warn(String.format("User '%s' is a member of the required organization '%s.", username, organization));
if (team != null && !team.isEmpty()) {
boolean isTeamMember = checkTeamMembership(accessToken, organization, team, username);
if (!isTeamMember) {
logger.warn(String.format("User '%s' is NOT a member of the required team '%s' in organization '%s'.", username, team, organization));
throw new IdentityBrokerException("User is not a member of the required team.");
}
}

logger.info(String.format("User '%s' is a member of the required organization '%s` and team `%s'", username, organization, team));
return user;
}

Expand All @@ -63,6 +73,20 @@ private boolean checkOrganizationMembership(String accessToken, String organizat
}
}

private boolean checkTeamMembership(String accessToken, String organization, String team, String username) {
String teamMembershipUrl = apiUrl + String.format("/orgs/%s/teams/%s/memberships/%s", organization, team, username);
try {
SimpleHttp.Response response = SimpleHttp.doGet(teamMembershipUrl, session)
.header("Authorization", "Bearer " + accessToken)
.header("Accept", "application/vnd.github+json")
.asResponse();
int statusCode = response.getStatus();
return statusCode == 200;
} catch (IOException e) {
throw new IdentityBrokerException("Could not verify team membership", e);
}
}

@Override
protected String getDefaultScopes() {
return DEFAULT_SCOPE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ public List<ProviderConfigProperty> getConfigProperties() {
.helpText("GitHub organization to check for membership.")
.type(ProviderConfigProperty.STRING_TYPE)
.add()
.property()
.name("team")
.label("Required GitHub Team")
.helpText("GitHub team to check for membership within the organization.")
.type(ProviderConfigProperty.STRING_TYPE)
.add()
.build();
}
}

0 comments on commit 8d31fcc

Please sign in to comment.