All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.client.security.user.privileges.AbstractIndicesPrivileges Maven / Gradle / Ivy

There is a newer version: 8.0.0-alpha2
Show newest version
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

package org.elasticsearch.client.security.user.privileges;

import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.xcontent.ConstructingObjectParser;
import org.elasticsearch.xcontent.ParseField;
import org.elasticsearch.xcontent.ToXContentObject;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentParser;
import org.elasticsearch.xcontent.XContentType;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg;

public abstract class AbstractIndicesPrivileges {
    static final ParseField NAMES = new ParseField("names");
    static final ParseField ALLOW_RESTRICTED_INDICES = new ParseField("allow_restricted_indices");
    static final ParseField PRIVILEGES = new ParseField("privileges");
    static final ParseField FIELD_PERMISSIONS = new ParseField("field_security");
    static final ParseField QUERY = new ParseField("query");

    protected final Set indices;
    protected final Set privileges;
    protected final boolean allowRestrictedIndices;

    AbstractIndicesPrivileges(Collection indices, Collection privileges, boolean allowRestrictedIndices) {
        if (null == indices || indices.isEmpty()) {
            throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern");
        }
        if (null == privileges || privileges.isEmpty()) {
            throw new IllegalArgumentException("indices privileges must define at least one privilege");
        }
        this.indices = Collections.unmodifiableSet(new HashSet<>(indices));
        this.privileges = Collections.unmodifiableSet(new HashSet<>(privileges));
        this.allowRestrictedIndices = allowRestrictedIndices;
    }

    /**
     * The indices names covered by the privileges.
     */
    public Set getIndices() {
        return this.indices;
    }

    /**
     * The privileges acting over indices. There is a canonical predefined set of
     * such privileges, but the {@code String} datatype allows for flexibility in defining
     * finer grained privileges.
     */
    public Set getPrivileges() {
        return this.privileges;
    }

    /**
     * True if the privileges cover restricted internal indices too. Certain indices are reserved for internal services and should be
     * transparent to ordinary users. For that matter, when granting privileges, you also have to toggle this flag to confirm that all
     * indices, including restricted ones, are in the scope of this permission. By default this is false.
     */
    public boolean allowRestrictedIndices() {
        return this.allowRestrictedIndices;
    }

    /**
     * If {@code true} some documents might not be visible. Only the documents
     * matching {@code query} will be readable.
     */
    public abstract boolean isUsingDocumentLevelSecurity();

    /**
     * If {@code true} some document fields might not be visible.
     */
    public abstract boolean isUsingFieldLevelSecurity();

    public static class FieldSecurity implements ToXContentObject {
        static final ParseField GRANT_FIELDS = new ParseField("grant");
        static final ParseField EXCEPT_FIELDS = new ParseField("except");

        private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(
            FIELD_PERMISSIONS.getPreferredName(),
            true,
            FieldSecurity::buildObjectFromParserArgs
        );

        @SuppressWarnings("unchecked")
        private static FieldSecurity buildObjectFromParserArgs(Object[] args) {
            return new FieldSecurity((Collection) args[0], (Collection) args[1]);
        }

        static {
            PARSER.declareStringArray(optionalConstructorArg(), GRANT_FIELDS);
            PARSER.declareStringArray(optionalConstructorArg(), EXCEPT_FIELDS);
        }

        static FieldSecurity parse(XContentParser parser, Void context) throws IOException {
            return PARSER.parse(parser, context);
        }

        // null or singleton '*' means all fields are granted, empty means no fields are granted
        private final Set grantedFields;
        // null or empty means no fields are denied
        private final Set deniedFields;

        FieldSecurity(Collection grantedFields, Collection deniedFields) {
            // unspecified granted fields means no restriction
            this.grantedFields = grantedFields == null ? null : Collections.unmodifiableSet(new HashSet<>(grantedFields));
            // unspecified denied fields means no restriction
            this.deniedFields = deniedFields == null ? null : Collections.unmodifiableSet(new HashSet<>(deniedFields));
        }

        /**
         * The document fields that can be read or queried. Can be null, in this case
         * all the document's fields are granted access to. Can also be empty, in which
         * case no fields are granted access to.
         */
        public Set getGrantedFields() {
            return grantedFields;
        }

        /**
         * The document fields that cannot be accessed or queried. Can be null or empty,
         * in which case no fields are denied.
         */
        public Set getDeniedFields() {
            return deniedFields;
        }

        public boolean isUsingFieldLevelSecurity() {
            return limitsGrantedFields() || hasDeniedFields();
        }

        private boolean hasDeniedFields() {
            return deniedFields != null && false == deniedFields.isEmpty();
        }

        private boolean limitsGrantedFields() {
            // we treat just '*' as no FLS since that's what the UI defaults to
            if (grantedFields == null || (grantedFields.size() == 1 && grantedFields.iterator().next().equals("*"))) {
                return false;
            }
            return true;
        }

        @Override
        public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
            builder.startObject();
            if (grantedFields == null) {
                // The role parser will reject a field_security object that doesn't have a "granted" field
                builder.field(GRANT_FIELDS.getPreferredName(), Collections.singletonList("*"));
            } else {
                builder.field(GRANT_FIELDS.getPreferredName(), grantedFields);
            }
            if (deniedFields != null) {
                builder.field(EXCEPT_FIELDS.getPreferredName(), deniedFields);
            }
            return builder.endObject();
        }

        @Override
        public String toString() {
            try {
                return XContentHelper.toXContent(this, XContentType.JSON, true).utf8ToString();
            } catch (IOException e) {
                throw new UncheckedIOException("Unexpected", e);
            }
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            final FieldSecurity that = (FieldSecurity) o;
            return Objects.equals(this.grantedFields, that.grantedFields) && Objects.equals(this.deniedFields, that.deniedFields);
        }

        @Override
        public int hashCode() {
            return Objects.hash(grantedFields, deniedFields);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy