Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 05b3a571658312fa4259b652d9b8478ecec278e7
Author: jaymode <[email protected]>
Date:   Thu Jan 31 15:48:48 2019 -0700

    Move request interceptors to AuthorizationService

    This change moves the RequestInterceptor iteration from the action
    filter to the AuthorizationService. This is done to remove the need
    for the use of a role within the request interceptors and replace it
    with the AuthorizationEngine. The AuthorizationEngine interface was
    also enhanced with a new method that is used to determine if a users
    permission on one index is a subset of their permissions on a list
    of indices or aliases.

    Additionally, this change addresses some leftover cleanups.

commit 0e1c191
Merge: 3280607 b7de8e1
Author: jaymode <[email protected]>
Date:   Thu Jan 31 08:56:45 2019 -0700

    Merge branch 'master' into security_authz_engine

commit 3280607
Author: Jay Modi <[email protected]>
Date:   Tue Jan 29 14:17:37 2019 -0700

    Allow authorization engines as an extension (elastic#37785)

    Authorization engines can now be registered by implementing a plugin,
    which also has a service implementation of a security extension. Only
    one extension may register an authorization engine and this engine will
    be used for all users except reserved realm users and internal users.

commit d628008
Author: jaymode <[email protected]>
Date:   Tue Jan 29 10:06:09 2019 -0700

    fix RBACEngine after restricted indices changes

commit 5074683
Merge: 74f2e99 3c9f703
Author: jaymode <[email protected]>
Date:   Tue Jan 29 08:09:39 2019 -0700

    Merge branch 'master' into security_authz_engine

commit 74f2e99
Merge: 7846ee8 899dfc3
Author: jaymode <[email protected]>
Date:   Fri Jan 25 15:02:07 2019 -0700

    Merge branch 'master' into security_authz_engine

commit 7846ee8
Merge: b9a2c81 a81931b
Author: jaymode <[email protected]>
Date:   Thu Jan 24 07:52:08 2019 -0700

    Merge branch 'master' into security_authz_engine

commit b9a2c81
Author: jaymode <[email protected]>
Date:   Tue Jan 22 09:48:11 2019 -0700

    Fix resolving restricted indices after merging

commit d98a77a
Merge: 83cde40 5c1a1f7
Author: jaymode <[email protected]>
Date:   Tue Jan 22 09:09:23 2019 -0700

    Merge branch 'master' into security_authz_engine

commit 83cde40
Author: Jay Modi <[email protected]>
Date:   Tue Jan 22 08:03:19 2019 -0700

    Add javadoc to the AuthorizationEngine interface (elastic#37620)

    This commit adds javadocs to the AuthorizationEngine interface aimed at
    developers of an authorization engine. Additionally, some classes were
    also moved to the core project so that they are ready to be exposed
    once we allow authorization engines to be plugged in.

commit 9a240c6
Author: Jay Modi <[email protected]>
Date:   Thu Jan 17 19:33:35 2019 -0700

    Encapsulate request, auth, and action name (elastic#37495)

    This change introduces a new class called RequestInfo that encapsulates
    the common objects that are passed to the authorization engine methods.
    By doing so, we give ourselves a way of adding additional data without
    breaking the interface. Additionally, this also reduces the need to
    ensure we pass these three parameters in the same order everywhere for
    consistency.

commit 6278eab
Merge: c555a44 4351a5e
Author: jaymode <[email protected]>
Date:   Thu Jan 17 07:51:32 2019 -0700

    Merge branch 'master' into security_authz_engine

commit c555a44
Merge: 1362ab6 ecf0de3
Author: jaymode <[email protected]>
Date:   Wed Jan 16 10:24:33 2019 -0700

    Merge branch 'master' into security_authz_engine

commit 1362ab6
Author: Jay Modi <[email protected]>
Date:   Wed Jan 16 10:23:45 2019 -0700

    Replace AuthorizedIndices class with a List (elastic#37328)

    This change replaces the AuthorizedIndices class with a simple list.
    The change to a simple list does remove the lazy loading of the
    authorized indices in favor of simpler code as the loading of this
    list is now an asynchronous operation that is delegated to the
    authorization engine.

commit 0246442
Merge: 8ccdc19 a2a40c5
Author: jaymode <[email protected]>
Date:   Tue Jan 15 10:49:12 2019 -0700

    Merge branch 'master' into security_authz_engine

commit 8ccdc19
Author: Jay Modi <[email protected]>
Date:   Mon Jan 7 13:43:22 2019 -0700

    Introduce asynchronous RBACEngine (elastic#36245)

    In order to support the concept of different authorization engines, this
    change begins the refactoring of the AuthorizationService to support
    this. Previously, the asynchronous work for authorization was performed
    by the AsyncAuthorizer class, but this tied the authorization service
    to a role based implementation. In this change, the authorize method
    become asynchronous and delegates much of the actual permission checking
    to an AuthorizationEngine. The pre-existing RBAC permission checking
    has been abstracted into the RBACEngine. The majority of calls to
    AuthorizationEngine instances are asynchronous as the underlying
    implementation may need to make network calls that should not block
    the current thread, which are often network threads.

    This change is meant to be built upon. The basic concepts are introduced
    without proper documentation, plumbing to enable other
    AuthorizationEngine types, and some items we may want to refactor.
    For example, the AuthorizedIndices class is lazily loaded but this might
    actually be something we want to make asynchronous. We pass a lot of the
    same arguments to the various methods and it would be prudent to wrap
    these in a class; this class would provide a way for us to pass
    additional items needed by future enhancements without breaking the
    interface and requiring updates to all implementations.

    See elastic#32435
  • Loading branch information
jaymode committed Feb 1, 2019
1 parent 1fa413a commit e5848b4
Show file tree
Hide file tree
Showing 66 changed files with 3,486 additions and 1,685 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ allprojects {
"org.elasticsearch.plugin:aggs-matrix-stats-client:${version}": ':modules:aggs-matrix-stats',
"org.elasticsearch.plugin:percolator-client:${version}": ':modules:percolator',
"org.elasticsearch.plugin:rank-eval-client:${version}": ':modules:rank-eval',
// for security example plugins
"org.elasticsearch.plugin:x-pack-core:${version}": ':x-pack:plugin:core',
"org.elasticsearch.client.x-pack-transport:${version}": ':x-pack:transport-client'
]

/*
Expand Down
46 changes: 46 additions & 0 deletions plugins/examples/security-authorization-engine/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
apply plugin: 'elasticsearch.esplugin'

esplugin {
name 'security-authorization-engine'
description 'An example spi extension plugin for security that implements an Authorization Engine'
classname 'org.elasticsearch.example.AuthorizationEnginePlugin'
extendedPlugins = ['x-pack-security']
}

dependencies {
compileOnly "org.elasticsearch.plugin:x-pack-core:${version}"
testCompile "org.elasticsearch.client.x-pack-transport:${version}"
}


integTestRunner {
systemProperty 'tests.security.manager', 'false'
}

integTestCluster {
dependsOn buildZip
setting 'xpack.security.enabled', 'true'
setting 'xpack.ilm.enabled', 'false'
setting 'xpack.ml.enabled', 'false'
setting 'xpack.monitoring.enabled', 'false'
setting 'xpack.license.self_generated.type', 'trial'

// This is important, so that all the modules are available too.
// There are index templates that use token filters that are in analysis-module and
// processors are being used that are in ingest-common module.
distribution = 'default'

setupCommand 'setupDummyUser',
'bin/elasticsearch-users', 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'custom_superuser'
waitCondition = { node, ant ->
File tmpFile = new File(node.cwd, 'wait.success')
ant.get(src: "http://${node.httpUri()}/_cluster/health?wait_for_nodes=>=${numNodes}&wait_for_status=yellow",
dest: tmpFile.toString(),
username: 'test_user',
password: 'x-pack-test-password',
ignoreerrors: true,
retries: 10)
return tmpFile.exists()
}
}
check.dependsOn integTest
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.example;

import org.elasticsearch.plugins.ActionPlugin;
import org.elasticsearch.plugins.Plugin;

/**
* Plugin class that is required so that the code contained here may be loaded as a plugin.
* Additional items such as settings and actions can be registered using this plugin class.
*/
public class AuthorizationEnginePlugin extends Plugin implements ActionPlugin {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.example;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine;
import org.elasticsearch.xpack.core.security.authz.ResolvedIndices;
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl.IndexAccessControl;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.core.security.user.User;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
* A custom implementation of an authorization engine. This engine is extremely basic in that it
* authorizes based upon the name of a single role. If users have this role they are granted access.
*/
public class CustomAuthorizationEngine implements AuthorizationEngine {

@Override
public void resolveAuthorizationInfo(RequestInfo requestInfo, ActionListener<AuthorizationInfo> listener) {
final Authentication authentication = requestInfo.getAuthentication();
if (authentication.getUser().isRunAs()) {
final CustomAuthorizationInfo authenticatedUserAuthzInfo =
new CustomAuthorizationInfo(authentication.getUser().authenticatedUser().roles(), null);
listener.onResponse(new CustomAuthorizationInfo(authentication.getUser().roles(), authenticatedUserAuthzInfo));
} else {
listener.onResponse(new CustomAuthorizationInfo(authentication.getUser().roles(), null));
}
}

@Override
public void authorizeRunAs(RequestInfo requestInfo, AuthorizationInfo authorizationInfo, ActionListener<AuthorizationResult> listener) {
if (isSuperuser(requestInfo.getAuthentication().getUser().authenticatedUser())) {
listener.onResponse(AuthorizationResult.granted());
} else {
listener.onResponse(AuthorizationResult.deny());
}
}

@Override
public void authorizeClusterAction(RequestInfo requestInfo, AuthorizationInfo authorizationInfo,
ActionListener<AuthorizationResult> listener) {
if (isSuperuser(requestInfo.getAuthentication().getUser())) {
listener.onResponse(AuthorizationResult.granted());
} else {
listener.onResponse(AuthorizationResult.deny());
}
}

@Override
public void authorizeIndexAction(RequestInfo requestInfo, AuthorizationInfo authorizationInfo,
AsyncSupplier<ResolvedIndices> indicesAsyncSupplier,
Function<String, AliasOrIndex> aliasOrIndexFunction,
ActionListener<IndexAuthorizationResult> listener) {
if (isSuperuser(requestInfo.getAuthentication().getUser())) {
indicesAsyncSupplier.getAsync(ActionListener.wrap(resolvedIndices -> {
Map<String, IndexAccessControl> indexAccessControlMap = new HashMap<>();
for (String name : resolvedIndices.getLocal()) {
indexAccessControlMap.put(name, new IndexAccessControl(true, FieldPermissions.DEFAULT, null));
}
IndicesAccessControl indicesAccessControl =
new IndicesAccessControl(true, Collections.unmodifiableMap(indexAccessControlMap));
listener.onResponse(new IndexAuthorizationResult(true, indicesAccessControl));
}, listener::onFailure));
} else {
listener.onResponse(new IndexAuthorizationResult(true, IndicesAccessControl.DENIED));
}
}

@Override
public void loadAuthorizedIndices(RequestInfo requestInfo, AuthorizationInfo authorizationInfo,
Map<String, AliasOrIndex> aliasAndIndexLookup, ActionListener<List<String>> listener) {
if (isSuperuser(requestInfo.getAuthentication().getUser())) {
listener.onResponse(new ArrayList<>(aliasAndIndexLookup.keySet()));
} else {
listener.onResponse(Collections.emptyList());
}
}

@Override
public void validateIndexPermissionsAreSubset(RequestInfo requestInfo, AuthorizationInfo authorizationInfo,
Map<String, List<String>> indexNameToNewNames,
ActionListener<AuthorizationResult> listener) {
if (isSuperuser(requestInfo.getAuthentication().getUser())) {
listener.onResponse(AuthorizationResult.granted());
} else {
listener.onResponse(AuthorizationResult.deny());
}
}

public static class CustomAuthorizationInfo implements AuthorizationInfo {

private final String[] roles;
private final CustomAuthorizationInfo authenticatedAuthzInfo;

CustomAuthorizationInfo(String[] roles, CustomAuthorizationInfo authenticatedAuthzInfo) {
this.roles = roles;
this.authenticatedAuthzInfo = authenticatedAuthzInfo;
}

@Override
public Map<String, Object> asMap() {
return Collections.singletonMap("roles", roles);
}

@Override
public CustomAuthorizationInfo getAuthenticatedUserAuthorizationInfo() {
return authenticatedAuthzInfo;
}
}

private boolean isSuperuser(User user) {
return Arrays.binarySearch(user.roles(), "custom_superuser") > -1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.example;

import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.core.security.SecurityExtension;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine;

/**
* Security extension class that registers the custom authorization engine to be used
*/
public class ExampleAuthorizationEngineExtension implements SecurityExtension {

@Override
public AuthorizationEngine getAuthorizationEngine(Settings settings) {
return new CustomAuthorizationEngine();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.elasticsearch.example.ExampleAuthorizationEngineExtension
Loading

0 comments on commit e5848b4

Please sign in to comment.