-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Configurable Keto variables #870
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,20 +33,22 @@ | |
public class KetoAuthorizationProvider implements AuthorizationProvider { | ||
|
||
private final EnginesApi apiInstance; | ||
private final Map<String, Object> options; | ||
|
||
/** | ||
* Initializes the KetoAuthorizationProvider | ||
* | ||
* @param options String K/V pair of options to initialize the provider with. Expects at least a | ||
* "basePath" for the provider URL | ||
*/ | ||
public KetoAuthorizationProvider(Map<String, String> options) { | ||
public KetoAuthorizationProvider(Map<String, Object> options) { | ||
if (options == null) { | ||
throw new IllegalArgumentException("Cannot pass empty or null options to KetoAuth"); | ||
} | ||
ApiClient defaultClient = Configuration.getDefaultApiClient(); | ||
defaultClient.setBasePath(options.get("basePath")); | ||
defaultClient.setBasePath((String) options.get("basePath")); | ||
this.apiInstance = new EnginesApi(defaultClient); | ||
this.options = options; | ||
} | ||
|
||
/** | ||
|
@@ -57,20 +59,28 @@ public KetoAuthorizationProvider(Map<String, String> options) { | |
* @return AuthorizationResult result of authorization query | ||
*/ | ||
public AuthorizationResult checkAccess(String project, Authentication authentication) { | ||
String email = getEmailFromAuth(authentication); | ||
String subject = (String) this.options.get("subject"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A better name for this option is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rename the option and the variable actually |
||
if ((subject == null) || (subject.isEmpty())) subject = "email"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
String subjectValue = getSubjectFromAuth(authentication, subject); | ||
try { | ||
// Get all roles from Keto | ||
List<OryAccessControlPolicyRole> roles = | ||
this.apiInstance.listOryAccessControlPolicyRoles("glob", 500L, 500L, email); | ||
this.apiInstance.listOryAccessControlPolicyRoles("glob", 500L, 500L, subjectValue); | ||
List<String> roleTemplates = (List<String>) this.options.get("roles"); | ||
|
||
// Loop through all roles the user has | ||
for (OryAccessControlPolicyRole role : roles) { | ||
// If the user has an admin or project specific role, return. | ||
if (("roles:admin").equals(role.getId()) | ||
|| (String.format("roles:feast:%s-member", project)).equals(role.getId())) { | ||
return AuthorizationResult.success(); | ||
for (String roleTemplate : roleTemplates) { | ||
if (roleTemplate.contains("{PROJECT}")) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
roleTemplate = roleTemplate.replace("{PROJECT}", project); | ||
} | ||
// Loop through all roles the user has | ||
for (OryAccessControlPolicyRole role : roles) { | ||
// If the user has a role matching one of the templates, return. | ||
if ((roleTemplate).equals(role.getId())) { | ||
return AuthorizationResult.success(); | ||
} | ||
} | ||
} | ||
|
||
} catch (ApiException e) { | ||
System.err.println("Exception when calling EnginesApi#doOryAccessControlPoliciesAllow"); | ||
System.err.println("Status code: " + e.getCode()); | ||
|
@@ -80,27 +90,32 @@ public AuthorizationResult checkAccess(String project, Authentication authentica | |
} | ||
// Could not determine project membership, deny access. | ||
return AuthorizationResult.failed( | ||
String.format("Access denied to project %s for user %s", project, email)); | ||
String.format("Access denied to project %s for user %s", project, subjectValue)); | ||
} | ||
|
||
/** | ||
* Get user email from their authentication object. | ||
* | ||
* @param authentication Spring Security Authentication object, used to extract user details | ||
* @param subject Subject from the Auth token | ||
* @return String user email | ||
*/ | ||
private String getEmailFromAuth(Authentication authentication) { | ||
private String getSubjectFromAuth(Authentication authentication, String subject) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rename |
||
Jwt principle = ((Jwt) authentication.getPrincipal()); | ||
Map<String, Object> claims = principle.getClaims(); | ||
String email = (String) claims.get("email"); | ||
String subjectValue = (String) claims.get(subject); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
if (email.isEmpty()) { | ||
throw new IllegalStateException("JWT does not have a valid email set."); | ||
if (subjectValue.isEmpty()) { | ||
throw new IllegalStateException(String.format("JWT does not have a valid %s.", subject)); | ||
} | ||
boolean validEmail = (new EmailValidator()).isValid(email, null); | ||
if (!validEmail) { | ||
throw new IllegalStateException("JWT contains an invalid email address"); | ||
|
||
if (subject.equals("email")) { | ||
boolean validEmail = (new EmailValidator()).isValid(subjectValue, null); | ||
if (!validEmail) { | ||
throw new IllegalStateException("JWT contains an invalid email address"); | ||
} | ||
} | ||
return email; | ||
|
||
return subjectValue; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,6 @@ public static class AuthorizationProperties { | |
private String provider; | ||
|
||
// K/V options to initialize the provider with | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is no longer a k/V pair since we allow a list for roles. may be worth a conversation |
||
private Map<String, String> options; | ||
private Map<String, Object> options; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,6 +94,10 @@ feast: | |
provider: none | ||
options: | ||
basePath: http://localhost:3000 | ||
subject: email | ||
roles: | ||
- role:feast:admin | ||
- role:feast:{PROJECT}-member | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a bit wary about using complex objects as options. I would strongly prefer to stick to K/V here, so perhaps we can turn roles into a CSV. |
||
|
||
grpc: | ||
server: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a better name for the option is
authorizationUrl