|  | 
|  | 1 | +/* | 
|  | 2 | + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | 
|  | 3 | + * or more contributor license agreements. Licensed under the Elastic License | 
|  | 4 | + * 2.0; you may not use this file except in compliance with the Elastic License | 
|  | 5 | + * 2.0. | 
|  | 6 | + */ | 
|  | 7 | + | 
|  | 8 | +package org.elasticsearch.xpack.security; | 
|  | 9 | + | 
|  | 10 | +import org.elasticsearch.client.Request; | 
|  | 11 | +import org.elasticsearch.client.Response; | 
|  | 12 | +import org.elasticsearch.client.ResponseException; | 
|  | 13 | +import org.elasticsearch.client.RestClient; | 
|  | 14 | +import org.elasticsearch.common.bytes.BytesReference; | 
|  | 15 | +import org.elasticsearch.common.settings.SecureString; | 
|  | 16 | +import org.elasticsearch.common.settings.Settings; | 
|  | 17 | +import org.elasticsearch.common.util.concurrent.ThreadContext; | 
|  | 18 | +import org.elasticsearch.test.cluster.ElasticsearchCluster; | 
|  | 19 | +import org.elasticsearch.test.cluster.local.distribution.DistributionType; | 
|  | 20 | +import org.elasticsearch.test.cluster.local.model.User; | 
|  | 21 | +import org.elasticsearch.test.cluster.util.resource.Resource; | 
|  | 22 | +import org.elasticsearch.xcontent.ObjectPath; | 
|  | 23 | +import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; | 
|  | 24 | +import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; | 
|  | 25 | +import org.junit.Before; | 
|  | 26 | +import org.junit.ClassRule; | 
|  | 27 | + | 
|  | 28 | +import java.io.IOException; | 
|  | 29 | +import java.util.HashMap; | 
|  | 30 | +import java.util.HashSet; | 
|  | 31 | +import java.util.Map; | 
|  | 32 | +import java.util.Set; | 
|  | 33 | + | 
|  | 34 | +import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; | 
|  | 35 | +import static org.hamcrest.Matchers.equalTo; | 
|  | 36 | + | 
|  | 37 | +public class GetRolesIT extends SecurityInBasicRestTestCase { | 
|  | 38 | + | 
|  | 39 | +    private static final String ADMIN_USER = "admin_user"; | 
|  | 40 | +    private static final SecureString ADMIN_PASSWORD = new SecureString("admin-password".toCharArray()); | 
|  | 41 | +    protected static final String READ_SECURITY_USER = "read_security_user"; | 
|  | 42 | +    private static final SecureString READ_SECURITY_PASSWORD = new SecureString("read-security-password".toCharArray()); | 
|  | 43 | + | 
|  | 44 | +    @Before | 
|  | 45 | +    public void initialize() { | 
|  | 46 | +        new ReservedRolesStore(); | 
|  | 47 | +    } | 
|  | 48 | + | 
|  | 49 | +    @ClassRule | 
|  | 50 | +    public static ElasticsearchCluster cluster = ElasticsearchCluster.local() | 
|  | 51 | +        .distribution(DistributionType.DEFAULT) | 
|  | 52 | +        .nodes(2) | 
|  | 53 | +        .setting("xpack.security.enabled", "true") | 
|  | 54 | +        .setting("xpack.license.self_generated.type", "basic") | 
|  | 55 | +        .rolesFile(Resource.fromClasspath("roles.yml")) | 
|  | 56 | +        .user(ADMIN_USER, ADMIN_PASSWORD.toString(), User.ROOT_USER_ROLE, true) | 
|  | 57 | +        .user(READ_SECURITY_USER, READ_SECURITY_PASSWORD.toString(), "read_security_user_role", false) | 
|  | 58 | +        .build(); | 
|  | 59 | + | 
|  | 60 | +    @Override | 
|  | 61 | +    protected Settings restAdminSettings() { | 
|  | 62 | +        String token = basicAuthHeaderValue(ADMIN_USER, ADMIN_PASSWORD); | 
|  | 63 | +        return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", token).build(); | 
|  | 64 | +    } | 
|  | 65 | + | 
|  | 66 | +    @Override | 
|  | 67 | +    protected Settings restClientSettings() { | 
|  | 68 | +        String token = basicAuthHeaderValue(READ_SECURITY_USER, READ_SECURITY_PASSWORD); | 
|  | 69 | +        return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", token).build(); | 
|  | 70 | +    } | 
|  | 71 | + | 
|  | 72 | +    @Override | 
|  | 73 | +    protected String getTestRestCluster() { | 
|  | 74 | +        return cluster.getHttpAddresses(); | 
|  | 75 | +    } | 
|  | 76 | + | 
|  | 77 | +    public void testGetAllRolesNoNative() throws Exception { | 
|  | 78 | +        // Test get roles API with operator admin_user | 
|  | 79 | +        getAllRolesAndAssert(adminClient(), ReservedRolesStore.names()); | 
|  | 80 | +        // Test get roles API with read_security_user | 
|  | 81 | +        getAllRolesAndAssert(client(), ReservedRolesStore.names()); | 
|  | 82 | +    } | 
|  | 83 | + | 
|  | 84 | +    public void testGetAllRolesWithNative() throws Exception { | 
|  | 85 | +        createRole("custom_role", "Test custom native role.", Map.of("owner", "test")); | 
|  | 86 | + | 
|  | 87 | +        Set<String> expectedRoles = new HashSet<>(ReservedRolesStore.names()); | 
|  | 88 | +        expectedRoles.add("custom_role"); | 
|  | 89 | + | 
|  | 90 | +        // Test get roles API with operator admin_user | 
|  | 91 | +        getAllRolesAndAssert(adminClient(), expectedRoles); | 
|  | 92 | +        // Test get roles API with read_security_user | 
|  | 93 | +        getAllRolesAndAssert(client(), expectedRoles); | 
|  | 94 | +    } | 
|  | 95 | + | 
|  | 96 | +    public void testGetReservedOnly() throws Exception { | 
|  | 97 | +        createRole("custom_role", "Test custom native role.", Map.of("owner", "test")); | 
|  | 98 | + | 
|  | 99 | +        Set<String> rolesToGet = new HashSet<>(); | 
|  | 100 | +        rolesToGet.add("custom_role"); | 
|  | 101 | +        rolesToGet.addAll(randomSet(1, 5, () -> randomFrom(ReservedRolesStore.names()))); | 
|  | 102 | + | 
|  | 103 | +        getRolesAndAssert(adminClient(), rolesToGet); | 
|  | 104 | +        getRolesAndAssert(client(), rolesToGet); | 
|  | 105 | +    } | 
|  | 106 | + | 
|  | 107 | +    public void testGetNativeOnly() throws Exception { | 
|  | 108 | +        createRole("custom_role1", "Test custom native role.", Map.of("owner", "test1")); | 
|  | 109 | +        createRole("custom_role2", "Test custom native role.", Map.of("owner", "test2")); | 
|  | 110 | + | 
|  | 111 | +        Set<String> rolesToGet = Set.of("custom_role1", "custom_role2"); | 
|  | 112 | + | 
|  | 113 | +        getRolesAndAssert(adminClient(), rolesToGet); | 
|  | 114 | +        getRolesAndAssert(client(), rolesToGet); | 
|  | 115 | +    } | 
|  | 116 | + | 
|  | 117 | +    public void testGetMixedRoles() throws Exception { | 
|  | 118 | +        createRole("custom_role", "Test custom native role.", Map.of("owner", "test")); | 
|  | 119 | + | 
|  | 120 | +        Set<String> rolesToGet = new HashSet<>(); | 
|  | 121 | +        rolesToGet.add("custom_role"); | 
|  | 122 | +        rolesToGet.addAll(randomSet(1, 5, () -> randomFrom(ReservedRolesStore.names()))); | 
|  | 123 | + | 
|  | 124 | +        getRolesAndAssert(adminClient(), rolesToGet); | 
|  | 125 | +        getRolesAndAssert(client(), rolesToGet); | 
|  | 126 | +    } | 
|  | 127 | + | 
|  | 128 | +    public void testNonExistentRole() { | 
|  | 129 | +        var e = expectThrows( | 
|  | 130 | +            ResponseException.class, | 
|  | 131 | +            () -> client().performRequest(new Request("GET", "/_security/role/non_existent_role")) | 
|  | 132 | +        ); | 
|  | 133 | +        assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(404)); | 
|  | 134 | +    } | 
|  | 135 | + | 
|  | 136 | +    private void createRole(String roleName, String description, Map<String, Object> metadata) throws IOException { | 
|  | 137 | +        Request request = new Request("POST", "/_security/role/" + roleName); | 
|  | 138 | +        Map<String, Object> requestMap = new HashMap<>(); | 
|  | 139 | +        if (description != null) { | 
|  | 140 | +            requestMap.put(RoleDescriptor.Fields.DESCRIPTION.getPreferredName(), description); | 
|  | 141 | +        } | 
|  | 142 | +        if (metadata != null) { | 
|  | 143 | +            requestMap.put(RoleDescriptor.Fields.METADATA.getPreferredName(), metadata); | 
|  | 144 | +        } | 
|  | 145 | +        BytesReference source = BytesReference.bytes(jsonBuilder().map(requestMap)); | 
|  | 146 | +        request.setJsonEntity(source.utf8ToString()); | 
|  | 147 | +        Response response = adminClient().performRequest(request); | 
|  | 148 | +        assertOK(response); | 
|  | 149 | +        Map<String, Object> responseMap = responseAsMap(response); | 
|  | 150 | +        assertTrue(ObjectPath.eval("role.created", responseMap)); | 
|  | 151 | +    } | 
|  | 152 | + | 
|  | 153 | +    private void getAllRolesAndAssert(RestClient client, Set<String> expectedRoles) throws IOException { | 
|  | 154 | +        final Response response = client.performRequest(new Request("GET", "/_security/role")); | 
|  | 155 | +        assertOK(response); | 
|  | 156 | +        final Map<String, Object> responseMap = responseAsMap(response); | 
|  | 157 | +        assertThat(responseMap.keySet(), equalTo(expectedRoles)); | 
|  | 158 | +    } | 
|  | 159 | + | 
|  | 160 | +    private void getRolesAndAssert(RestClient client, Set<String> rolesToGet) throws IOException { | 
|  | 161 | +        final Response response = client.performRequest(new Request("GET", "/_security/role/" + String.join(",", rolesToGet))); | 
|  | 162 | +        assertOK(response); | 
|  | 163 | +        final Map<String, Object> responseMap = responseAsMap(response); | 
|  | 164 | +        assertThat(responseMap.keySet(), equalTo(rolesToGet)); | 
|  | 165 | +    } | 
|  | 166 | +} | 
0 commit comments