Skip to content

Commit

Permalink
fix(authz/github): Add cache to team membership check to prevent exce…
Browse files Browse the repository at this point in the history
…ssive http requests (#200)

* fix(authz/github): Add cache to team membership check to prevent excessive http requests
* [squash later] review remarks
* [squash after] move cache initialization
  • Loading branch information
Fabian Heymann authored and Travis Tomsu committed Oct 13, 2017
1 parent 2798bb5 commit bcbdad1
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ public class GitHubProperties {
@Max(100L)
@Min(1L)
Integer paginationValue = 100;
@NotNull
Integer membershipCacheTTLSeconds = 60 * 1000;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@

package com.netflix.spinnaker.fiat.roles.github;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.netflix.spinnaker.fiat.model.resources.Role;
import com.netflix.spinnaker.fiat.roles.UserRolesProvider;
import com.netflix.spinnaker.fiat.roles.github.client.GitHubClient;
import com.netflix.spinnaker.fiat.roles.github.model.Team;
import com.netflix.spinnaker.fiat.roles.github.model.TeamMembership;
import lombok.Data;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
Expand All @@ -40,6 +44,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
Expand All @@ -61,9 +67,36 @@ public class GithubTeamsUserRolesProvider implements UserRolesProvider, Initiali
@Setter
private GitHubProperties gitHubProperties;

private LoadingCache<CacheKey, Boolean> teamMembershipCache;

private static final String ACTIVE = "active";

@Override
public void afterPropertiesSet() throws Exception {
Assert.state(gitHubProperties.getOrganization() != null, "Supply an organization");
Assert.state(gitHubProperties.getBaseUrl() != null, "Supply a base url");

this.initializeTeamMembershipCache();
}

private void initializeTeamMembershipCache() {
this.teamMembershipCache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(this.gitHubProperties.getMembershipCacheTTLSeconds(), TimeUnit.SECONDS)
.build(
new CacheLoader<CacheKey, Boolean>() {
public Boolean load(CacheKey key) {
try {
TeamMembership response = gitHubClient.isMemberOfTeam(key.getTeamId(), key.getUsername());
return (response.getState().equals(GithubTeamsUserRolesProvider.ACTIVE));
} catch (RetrofitError e) {
if (e.getResponse().getStatus() != 404) {
handleNon404s(e);
}
}
return false;
}
});
}

@Override
Expand Down Expand Up @@ -151,14 +184,10 @@ private List<Team> getTeamsInOrgPaginated(int page) {
}

private boolean isMemberOfTeam(Team t, String username) {
String ACTIVE = "active";
try {
TeamMembership response = gitHubClient.isMemberOfTeam(t.getId(), username);
return (response.getState().equals(ACTIVE));
} catch (RetrofitError e) {
if (e.getResponse().getStatus() != 404) {
handleNon404s(e);
}
return this.teamMembershipCache.get(new CacheKey(t.getId(), username));
} catch (ExecutionException e) {
log.error("Failed to read from cache when getting team membership", e);
}
return false;
}
Expand Down Expand Up @@ -197,4 +226,10 @@ public Map<String, Collection<Role>> multiLoadRoles(Collection<String> userEmail

return emailGroupsMap;
}

@Data
private class CacheKey {
private final Long teamId;
private final String username;
}
}

0 comments on commit bcbdad1

Please sign in to comment.