Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -96,13 +97,13 @@ public ConfigModelV7(
log.error("Cannot apply roles mapping resolution", e);
rolesMappingResolution = ConfigConstants.RolesMappingResolution.MAPPING_ONLY;
}

agr = reloadActionGroups(actiongroups);
securityRoles = reload(roles);
tenantHolder = new TenantHolder(roles, tenants);
roleMappingHolder = new RoleMappingHolder(rolemappings, dcm.getHostsResolverMode());
}

public Set<String> getAllConfiguredTenantNames() {
return Collections.unmodifiableSet(tenants.getCEntries().keySet());
}
Expand All @@ -114,7 +115,7 @@ public SecurityRoles getSecurityRoles() {
private static interface ActionGroupResolver {
Set<String> resolvedActions(final List<String> actions);
}

private ActionGroupResolver reloadActionGroups(SecurityDynamicConfiguration<ActionGroupsV7> actionGroups) {
return new ActionGroupResolver() {

Expand Down Expand Up @@ -968,10 +969,6 @@ private static boolean impliesTypePerm(Set<IndexPattern> ipatterns, Resolved res
);
}



//#######

private class TenantHolder {

private SetMultimap<String, Tuple<String, Boolean>> tenantsMM = null;
Expand All @@ -982,7 +979,7 @@ public TenantHolder(SecurityDynamicConfiguration<RoleV7> roles, SecurityDynamicC
final ExecutorService execs = Executors.newFixedThreadPool(10);

for(Entry<String, RoleV7> securityRole: roles.getCEntries().entrySet()) {

if(securityRole.getValue() == null) {
continue;
}
Expand All @@ -992,14 +989,21 @@ public TenantHolder(SecurityDynamicConfiguration<RoleV7> roles, SecurityDynamicC
public Tuple<String, Set<Tuple<String, Boolean>>> call() throws Exception {
final Set<Tuple<String, Boolean>> tuples = new HashSet<>();
final List<RoleV7.Tenant> tenants = securityRole.getValue().getTenant_permissions();

if (tenants != null) {

for (RoleV7.Tenant tenant : tenants) {

for(String matchingTenant: WildcardMatcher.from(tenant.getTenant_patterns()).getMatchAny(definedTenants.getCEntries().keySet(), Collectors.toList())) {

// find Wildcarded tenant patterns
List<String> matchingTenants = WildcardMatcher.from(tenant.getTenant_patterns()).getMatchAny(definedTenants.getCEntries().keySet(), Collectors.toList()) ;
for(String matchingTenant: matchingTenants ) {
tuples.add(new Tuple<String, Boolean>(matchingTenant, agr.resolvedActions(tenant.getAllowed_actions()).contains("kibana:saved_objects/*/write")));
}
// find parameter substitution specified tenant
Pattern parameterPattern = Pattern.compile("^\\$\\{attr");
List<String> matchingParameterTenantList = tenant.getTenant_patterns().stream().filter(parameterPattern.asPredicate()).collect(Collectors.toList());
for(String matchingParameterTenant : matchingParameterTenantList ) {
tuples.add(new Tuple<String, Boolean>(matchingParameterTenant,agr.resolvedActions(tenant.getAllowed_actions()).contains("kibana:saved_objects/*/write"))) ;
}
}
}

Expand Down Expand Up @@ -1050,11 +1054,22 @@ public Map<String, Boolean> mapTenants(final User user, Set<String> roles) {
result.put(user.getName(), true);

tenantsMM.entries().stream().filter(e -> roles.contains(e.getKey())).filter(e -> !user.getName().equals(e.getValue().v1())).forEach(e -> {
final String tenant = e.getValue().v1();

// replaceProperties for tenant name because
// at this point e.getValue().v1() can be in this form : "${attr.[internal|jwt|proxy|ldap].*}"
// let's substitute it with the eventual value of the user's attribute
final String tenant = replaceProperties(e.getValue().v1(),user);
final boolean rw = e.getValue().v2();

if (rw || !result.containsKey(tenant)) { //RW outperforms RO
result.put(tenant, rw);

// We want to make sure that we add a tenant that exissts
// Indeed, because we don't have control over what will be
// passed on as values of users' attributes, we have to make
// sure that we don't allow them to select tenants that do not exist.
if(ConfigModelV7.this.tenants.getCEntries().keySet().contains(tenant)) {
result.put(tenant, rw);
}
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,57 @@ public void testKibanaAlias65() throws Exception {
Assert.assertTrue(res.getBody().contains(".kibana_-900636979_kibanaro"));
}


@Test
public void testTenantParametersSubstitution() throws Exception {
final Settings settings = Settings.builder()
.build();
setup(settings);
final RestHelper rh = nonSslRestHelper();

HttpResponse res;

System.out.println("#### testTenantParametersSubstitution : user_tenant_parameters_substitution does not have access to tenant blafasel") ;
String body = "{\"buildNum\": 15460, \"defaultIndex\": \"plop\", \"tenant\": \"tenant_parameters_substitution\"}";
res = rh.executePutRequest(".kibana/config/5.6.0?pretty",body, new BasicHeader("securitytenant", "blafasel"), encodeBasicHeader("user_tenant_parameters_substitution","user_tenant_parameters_substitution")) ;
System.out.println(res.getBody());
Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode());

System.out.println("#### testTenantParametersSubstitution : hr_employee does not have access to tenant tenant_parameters_substitution") ;
body = "{\"buildNum\": 15460, \"defaultIndex\": \"plop\", \"tenant\": \"tenant_parameters_substitution\"}";
res = rh.executePutRequest(".kibana/config/5.6.0?pretty",body, new BasicHeader("securitytenant", "tenant_parameters_substitution"), encodeBasicHeader("hr_employee", "hr_employee")) ;
System.out.println(res.getBody());
Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode());

System.out.println("#### testTenantParametersSubstitution : hr_employee does not have access to tenant tenant_parameters_substitution_1") ;
body = "{\"buildNum\": 15460, \"defaultIndex\": \"plop\", \"tenant\": \"tenant_parameters_substitution_1\"}";
res = rh.executePutRequest(".kibana/config/5.6.0?pretty",body, new BasicHeader("securitytenant", "tenant_parameters_substitution"), encodeBasicHeader("hr_employee", "hr_employee")) ;
System.out.println(res.getBody());
Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode());

System.out.println("#### testTenantParametersSubstitution : user user_tenant_parameters_substitution creates a doc in tenant tenant_parameters_substitution that is a match of the attribute 'attribute1' for this user") ;
body = "{\"buildNum\": 15460, \"defaultIndex\": \"plop\", \"tenant\": \"tenant_parameters_substitution\"}";
res = rh.executePutRequest(".kibana/config/5.6.0?pretty",body, new BasicHeader("securitytenant", "tenant_parameters_substitution"), encodeBasicHeader("user_tenant_parameters_substitution", "user_tenant_parameters_substitution")) ;
System.out.println(res.getBody());
Assert.assertEquals(HttpStatus.SC_CREATED, res.getStatusCode());

System.out.println("#### testTenantParametersSubstitution : user user_tenant_parameters_substitution gets kibana index for tenant tenant_parameters_substitution that is a match of the attribute 'attribute1' for this user") ;
res = rh.executeGetRequest(".kibana/config/5.6.0?pretty",new BasicHeader("securitytenant", "tenant_parameters_substitution"), encodeBasicHeader("user_tenant_parameters_substitution", "user_tenant_parameters_substitution")) ;
System.out.println(res.getBody());
Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode());;
Assert.assertTrue(WildcardMatcher.from("*tenant_parameters_substitution*").test(res.getBody()));

System.out.println("#### testTenantParametersSubstitution : user user_tenant_parameters_substitution creates a doc in tenant tenant_parameters_substitution_1 that is a match of the attribute 'attribute1' for this use1r") ;
body = "{\"buildNum\": 15460, \"defaultIndex\": \"plop\", \"tenant\": \"tenant_parameters_substitution_1\"}";
res = rh.executePutRequest(".kibana/config/5.6.0?pretty",body, new BasicHeader("securitytenant", "tenant_parameters_substitution_1"), encodeBasicHeader("user_tenant_parameters_substitution", "user_tenant_parameters_substitution")) ;
System.out.println(res.getBody());
Assert.assertEquals(HttpStatus.SC_CREATED, res.getStatusCode());

System.out.println("#### testTenantParametersSubstitution : user user_tenant_parameters_substitution gets kibana index for tenant tenant_parameters_substitution_1 that is a value of the attribute 'attribute1' for this user") ;
res = rh.executeGetRequest(".kibana/config/5.6.0?pretty",new BasicHeader("securitytenant", "tenant_parameters_substitution_1"), encodeBasicHeader("user_tenant_parameters_substitution", "user_tenant_parameters_substitution")) ;
System.out.println(res.getBody());
System.out.println(res.getBody());
Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode());
Assert.assertTrue(WildcardMatcher.from("*tenant_parameters_substitution_1*").test(res.getBody()));
}
}
8 changes: 8 additions & 0 deletions src/test/resources/multitenancy/internal_users.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,11 @@ kibanaro:
backend_roles: []
attributes: {}
description: "Migrated from v6"
user_tenant_parameters_substitution:
hash: "$2y$12$xgJfGiHpYOkRpF9W9dXYZOpJJ4bHz3VTwdv7ZZYTwlvx7NbH62qUi"
reserved: false
hidden: false
backend_roles: []
attributes:
attribute1: "tenant_parameters_substitution"
description: "PR# 819 / Issue#817"
17 changes: 17 additions & 0 deletions src/test/resources/multitenancy/roles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -463,3 +463,20 @@ opendistro_security_role_starfleet_captains:
- "command_tenant"
allowed_actions:
- "kibana_all_write"
opendistro_security_role_tenant_parameters_substitution:
reserved: false
hidden: false
description: "PR#819 / Issue#817"
cluster_permissions:
- "OPENDISTRO_SECURITY_CLUSTER_COMPOSITE_OPS"
index_permissions:
- index_patterns:
- "?kibana"
allowed_actions:
- "ALL"
tenant_permissions:
- tenant_patterns:
- "${attr.internal.attribute1}"
- "${attr.internal.attribute1}_1"
allowed_actions:
- "kibana_all_write"
9 changes: 9 additions & 0 deletions src/test/resources/multitenancy/roles_mapping.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,12 @@ opendistro_security_kibana4_testindex:
- "test"
and_backend_roles: []
description: "Migrated from v6"
opendistro_security_role_tenant_parameters_substitution:
reserved: false
hidden: false
backend_roles: []
hosts: []
users:
- "user_tenant_parameters_substitution"
and_backend_roles: []
description: "PR#819 / Issue#817"
9 changes: 9 additions & 0 deletions src/test/resources/multitenancy/roles_tenants.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,12 @@ human_resources:
reserved: false
hidden: false
description: "Migrated from v6"
tenant_parameters_substitution:
reserved: false
hidden: false
description: "PR#819 / Issue#817"
tenant_parameters_substitution_1:
reserved: false
hidden: false
description: "PR#819 / Issue#817"