-
Notifications
You must be signed in to change notification settings - Fork 286
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
Add ability for plugins to inject roles #560
Conversation
Codecov Report
@@ Coverage Diff @@
## master #560 +/- ##
============================================
+ Coverage 63.15% 63.25% +0.10%
- Complexity 3050 3064 +14
============================================
Files 233 234 +1
Lines 16560 16592 +32
Branches 2989 2996 +7
============================================
+ Hits 10458 10496 +38
+ Misses 4551 4544 -7
- Partials 1551 1552 +1
Continue to review full report at Codecov.
|
@@ -100,7 +100,10 @@ | |||
public static final String OPENDISTRO_SECURITY_USER_HEADER = OPENDISTRO_SECURITY_CONFIG_PREFIX+"user_header"; | |||
|
|||
public static final String OPENDISTRO_SECURITY_INJECTED_USER = "injected_user"; | |||
|
|||
|
|||
public static final String OPENDISTRO_SECURITY_INJECTED_ROLES_ENABLED = "opendistro_security_injected_roles_enabled"; |
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.
Please follow the common format for other settings (opendistro_security.enable_inject_roles
)
@@ -961,6 +961,7 @@ public Settings additionalSettings() { | |||
settings.add(Setting.boolSetting(ConfigConstants.OPENDISTRO_SECURITY_UNSUPPORTED_LOAD_STATIC_RESOURCES, true, Property.NodeScope, Property.Filtered)); | |||
settings.add(Setting.boolSetting(ConfigConstants.OPENDISTRO_SECURITY_SSL_CERT_RELOAD_ENABLED, false, Property.NodeScope, Property.Filtered)); | |||
settings.add(Setting.boolSetting(ConfigConstants.OPENDISTRO_SECURITY_UNSUPPORTED_ACCEPT_INVALID_CONFIG, false, Property.NodeScope, Property.Filtered)); | |||
settings.add(Setting.boolSetting(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_ROLES_ENABLED, true, Property.NodeScope, Property.Filtered)); |
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.
What is the risk of enabling role injection by default? If there is no risk, why is it necessary to introduce enable settings?
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.
Removed OPENDISTRO_SECURITY_INJECTED_ROLES_ENABLED, its enabled all the time.With that Settings are not required, removed.
src/main/java/com/amazon/opendistroforelasticsearch/security/auth/RolesInjector.java
Outdated
Show resolved
Hide resolved
public final User getUser() { | ||
return user; | ||
} | ||
} |
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.
nit: new line
...ain/java/com/amazon/opendistroforelasticsearch/security/filter/OpenDistroSecurityFilter.java
Outdated
Show resolved
Hide resolved
...ain/java/com/amazon/opendistroforelasticsearch/security/filter/OpenDistroSecurityFilter.java
Outdated
Show resolved
Hide resolved
src/test/java/com/amazon/opendistroforelasticsearch/security/RolesInjectorIntegTest.java
Outdated
Show resolved
Hide resolved
src/main/java/com/amazon/opendistroforelasticsearch/security/auth/RolesInjector.java
Outdated
Show resolved
Hide resolved
...est/java/com/amazon/opendistroforelasticsearch/security/test/plugin/RolesInjectorPlugin.java
Outdated
Show resolved
Hide resolved
…he time.With that Settings are not required, removed.Refactored RolesInjector.java. Moved RolesInjectorPlugin class to the RolesInjectorPlugin. Added exception message for roles injection missing permissions.
@vrozov addressed the comments and published revision. |
public Set<String> injectUserAndRoles(final ThreadContext ctx) { | ||
|
||
if(log.isDebugEnabled()){ | ||
log.debug("Injected role str: "+ ctx.getTransient(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_ROLES)); |
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.
Use smart logging. Log after checking for null
.
log.debug("Injected role str: "+ ctx.getTransient(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_ROLES)); | ||
} | ||
String injectedStr = ctx.getTransient(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_ROLES); | ||
if (Strings.isNullOrEmpty(injectedStr)) { |
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.
Empty string should probably be considered the way as malformed, only check for null
is necessary.
if(log.isDebugEnabled()){ | ||
log.debug("Injected role str: "+ ctx.getTransient(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_ROLES)); | ||
} | ||
String injectedStr = ctx.getTransient(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_ROLES); |
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.
rename to injectedUserAndRoles
if (Strings.isNullOrEmpty(injectedStr)) { | ||
return null; | ||
} | ||
User user = parseUser(injectedStr); |
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.
Can the string be parsed once?
log.error("Roles must not be null, injected string was '{}.' Roles injection failed.", injectedStr); | ||
return null; | ||
} | ||
return new HashSet<>(Arrays.asList(strs[1].split(","))); |
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.
Use ImmutableSet.copyOf()
.
return; | ||
|
||
threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS, | ||
new TransportAddress(InetAddress.getLoopbackAddress(), 9300)); |
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.
What if it is not a default port? Is it necessary to set remote address when roles are injected?
6119e1d
to
e9c0a0f
Compare
} | ||
|
||
if(log.isDebugEnabled()){ | ||
log.debug("Injected roles: "+ injectedUserAndRoles); |
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.
nit: please use smart logging:
log.debug("Injected roles: "+ injectedUserAndRoles); | |
log.debug("Injected roles: {}", injectedUserAndRoles); |
return null; | ||
} | ||
|
||
if(log.isDebugEnabled()){ |
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.
nit: the check is not necessary with smart logging.
" Roles injection failed.", injectedUserAndRoles); | ||
return null; | ||
} | ||
if (Strings.isNullOrEmpty(strs[0].trim())) { |
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.
nit: trim()
does not return null
.
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.
Good catch, thanks.
listener.onFailure(new ElasticsearchSecurityException("no permissions for " + pres.getMissingPrivileges()+ " and "+user, RestStatus.FORBIDDEN)); | ||
String err = (injectedRoles == null) ? | ||
String.format("no permissions for %s and %s" , pres.getMissingPrivileges(), user) : | ||
String.format("no permissions for %s and associated roles %s ", pres.getMissingPrivileges(), injectedRoles); |
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.
nit: trim extra space in the error message.
"invalid", | ||
"role_1,role_2", | ||
" | ", | ||
" " |
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.
nit: add "|" use case.
log.error("Roles must not be null, injected string was '{}.' Roles injection failed.", injectedUserAndRoles); | ||
return null; | ||
} | ||
Set<String> roles = new HashSet<>(ImmutableSet.copyOf(strs[1].split(","))); |
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.
Set<String> roles = new HashSet<>(ImmutableSet.copyOf(strs[1].split(","))); | |
Set<String> roles = ImmutableSet.copyOf(strs[1].split(",")); |
} | ||
|
||
if (StringUtils.isEmpty(StringUtils.trim(strs[0]))) { | ||
log.error("Username must not be null, injected string was '{}.' Roles injection failed.", injectedUserAndRoles); |
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.
log.error("Username must not be null, injected string was '{}.' Roles injection failed.", injectedUserAndRoles); | |
log.error("Username must be provided, injected string was '{}.' Roles injection failed.", injectedUserAndRoles); |
User user = new User(strs[0]); | ||
|
||
if (strs.length < 2 || StringUtils.isEmpty(StringUtils.trim(strs[0]))) { | ||
log.error("Roles must not be null, injected string was '{}.' Roles injection failed.", injectedUserAndRoles); |
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.
log.error("Roles must not be null, injected string was '{}.' Roles injection failed.", injectedUserAndRoles); | |
log.error("Roles must be provided, injected string was '{}.' Roles injection failed.", injectedUserAndRoles); |
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.
LGTM
@@ -112,6 +114,7 @@ public OpenDistroSecurityFilter(final Settings settings, final PrivilegesEvaluat | |||
this.compatConfig = compatConfig; | |||
this.indexResolverReplacer = indexResolverReplacer; | |||
this.immutableIndicesMatcher = WildcardMatcher.from(settings.getAsList(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_IMMUTABLE_INDICES, Collections.emptyList())); | |||
this.rolesInjector = new RolesInjector(); |
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.
nit: This can be injected via constructor ? or the class function can be made static as it has no state (other than logger) ?
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.
Initially we had injected via constructor, then we separated to make it clean. Would prefer not to change this.
Issue #, if available:
opendistro-for-elasticsearch/alerting#215
Description of changes:
This is used to inject opendistro-roles into the request when there is no user involved, like periodic plugin background jobs. The roles injection is done using thread-context at transport layer only. You can't inject roles using REST api. Using this we can enforce fine-grained-access-control for the transport layer calls plugins make.
Format for the injected string:
user_name|role_1,role_2
. User name is ignored. And roles are opendistro-roles.threadContext.putTransient("opendistro_security_injected_roles", "bones|role_1,role_2");
The request will be evaluated usingrole1 and role2
. These changes are similar to User injection functionality.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.