-
Notifications
You must be signed in to change notification settings - Fork 110
Encode the user’s roles directly into the JWT #722
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
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 |
---|---|---|
|
@@ -13,17 +13,21 @@ | |
*/ | ||
package io.trino.gateway.ha.security; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
import io.trino.gateway.ha.config.AuthorizationConfiguration; | ||
import io.trino.gateway.ha.config.LdapConfiguration; | ||
import io.trino.gateway.ha.config.UserConfiguration; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
public class AuthorizationManager | ||
{ | ||
private final Map<String, UserConfiguration> presetUsers; | ||
private final LbLdapClient lbLdapClient; | ||
private final AuthorizationConfiguration authorizationConfiguration; | ||
|
||
public AuthorizationManager(AuthorizationConfiguration configuration, | ||
Map<String, UserConfiguration> presetUsers) | ||
|
@@ -35,9 +39,42 @@ public AuthorizationManager(AuthorizationConfiguration configuration, | |
else { | ||
lbLdapClient = null; | ||
} | ||
this.authorizationConfiguration = configuration; | ||
} | ||
|
||
public Optional<String> getPrivileges(String username) | ||
@VisibleForTesting | ||
public AuthorizationManager(Map<String, UserConfiguration> presetUsers, LbLdapClient lbLdapClient, AuthorizationConfiguration authorizationConfiguration) | ||
{ | ||
this.presetUsers = presetUsers; | ||
this.lbLdapClient = lbLdapClient; | ||
this.authorizationConfiguration = authorizationConfiguration; | ||
} | ||
|
||
public String getPrivileges(String username) | ||
{ | ||
if (authorizationConfiguration == null) { | ||
return "ADMIN_USER_API"; | ||
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’d prefer not to give any privileges when |
||
} | ||
Optional<String> memberOf = getMemberOf(username); | ||
List<String> privileges = new ArrayList<String>(); | ||
|
||
if (authorizationConfiguration.getAdmin() != null) { | ||
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. would it be cleaner to replace the ifs with
|
||
memberOf.filter(m -> m.matches(authorizationConfiguration.getAdmin())).ifPresent(m -> privileges.add("ADMIN")); | ||
} | ||
if (authorizationConfiguration.getUser() != null) { | ||
memberOf.filter(m -> m.matches(authorizationConfiguration.getUser())).ifPresent(m -> privileges.add("USER")); | ||
} | ||
if (authorizationConfiguration.getApi() != null) { | ||
memberOf.filter(m -> m.matches(authorizationConfiguration.getApi())).ifPresent(m -> privileges.add("API")); | ||
} | ||
|
||
if (privileges.isEmpty()) { | ||
return ""; | ||
} | ||
return String.join("_", privileges); | ||
} | ||
|
||
public Optional<String> getMemberOf(String username) | ||
{ | ||
//check the preset users | ||
String privs = ""; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,22 +13,23 @@ | |
*/ | ||
package io.trino.gateway.ha.security; | ||
|
||
import com.auth0.jwt.interfaces.Claim; | ||
import io.airlift.log.Logger; | ||
import io.trino.gateway.ha.security.util.AuthenticationException; | ||
import io.trino.gateway.ha.security.util.IdTokenAuthenticator; | ||
|
||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
public class FormAuthenticator | ||
implements IdTokenAuthenticator | ||
{ | ||
private static final Logger log = Logger.get(FormAuthenticator.class); | ||
private final LbFormAuthManager formAuthManager; | ||
private final AuthorizationManager authorizationManager; | ||
|
||
public FormAuthenticator(LbFormAuthManager formAuthManager, | ||
AuthorizationManager authorizationManager) | ||
public FormAuthenticator(LbFormAuthManager formAuthManager) | ||
{ | ||
this.formAuthManager = formAuthManager; | ||
this.authorizationManager = authorizationManager; | ||
} | ||
|
||
/** | ||
|
@@ -43,11 +44,27 @@ public Optional<LbPrincipal> authenticate(String idToken) | |
throws AuthenticationException | ||
{ | ||
String userIdField = formAuthManager.getUserIdField(); | ||
return formAuthManager | ||
.getClaimsFromIdToken(idToken) | ||
.map(c -> c.get(userIdField)) | ||
.map(Object::toString) | ||
.map(s -> s.replace("\"", "")) | ||
.map(sub -> new LbPrincipal(sub, authorizationManager.getPrivileges(sub))); | ||
String privilegesField = formAuthManager.getPrivilegesField(); | ||
|
||
Map<String, Claim> claims = null; | ||
try { | ||
claims = formAuthManager.getClaimsFromIdToken(idToken).orElseThrow(); | ||
} | ||
catch (Exception e) { | ||
return Optional.empty(); | ||
} | ||
String userId = claims.get(userIdField).asString().replace("\"", ""); | ||
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. why do we need to replace |
||
|
||
Claim claim = claims.get(privilegesField); | ||
if (claim == null || claim.asString() == null) { | ||
log.warn("No privileges found for user %s in idToken", userId); | ||
throw new AuthenticationException("No privileges found for user " + userId + " in idToken"); | ||
} | ||
|
||
String privileges = claim.asString(); | ||
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. Rather than setting the privileges, let's set the membefOf and everything should fall in place. |
||
if (privileges == null) { | ||
privileges = ""; | ||
} | ||
return Optional.of(new LbPrincipal(userId, privileges)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,18 +15,17 @@ | |
|
||
import java.security.Principal; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
|
||
public class LbPrincipal | ||
implements Principal | ||
{ | ||
private final String name; | ||
private final Optional<String> memberOf; | ||
private final String privileges; | ||
|
||
public LbPrincipal(String name, Optional<String> memberOf) | ||
public LbPrincipal(String name, String privileges) | ||
{ | ||
this.name = name; | ||
this.memberOf = memberOf; | ||
this.privileges = privileges; | ||
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. It's not a good idea to set the privileges directly in the Principal. The privileges should be calculated dynamically. If any privilege for a user changes, we need to invalidate the session which is going to be difficult. This is especially true with the LDAP based privileges. |
||
} | ||
|
||
@Override | ||
|
@@ -39,13 +38,13 @@ public boolean equals(Object o) | |
return false; | ||
} | ||
LbPrincipal that = (LbPrincipal) o; | ||
return name.equals(that.name) && memberOf.equals(that.memberOf); | ||
return name.equals(that.name) && privileges.equals(that.privileges); | ||
} | ||
|
||
@Override | ||
public int hashCode() | ||
{ | ||
return Objects.hash(name, memberOf); | ||
return Objects.hash(name, privileges); | ||
} | ||
|
||
@Override | ||
|
@@ -54,8 +53,8 @@ public String getName() | |
return name; | ||
} | ||
|
||
public Optional<String> getMemberOf() | ||
public String getPrivileges() | ||
{ | ||
return this.memberOf; | ||
return this.privileges; | ||
} | ||
} |
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.
is this change necessary?
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.
inside getFormAuthManager(configuration) i am using authorizationManager variable. that is why i want to initialize authorizationManager first and then call that method