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

oracle.kv.impl.api.ops.MultiKeyOperationHandler Maven / Gradle / Ivy

Go to download

NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.

The newest version!
/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.impl.api.ops;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.Transaction;

import oracle.kv.Depth;
import oracle.kv.Direction;
import oracle.kv.KeyRange;
import oracle.kv.impl.api.ops.InternalOperation.OpCode;
import oracle.kv.impl.api.ops.InternalOperationHandler.Keyspace.KeyAccessChecker;
import oracle.kv.impl.api.ops.InternalOperationHandler.Keyspace.KeyspaceType;
import oracle.kv.impl.api.ops.InternalOperationHandler.PrivilegedTableAccessor;
import oracle.kv.impl.api.ops.OperationHandler.KVAuthorizer;
import oracle.kv.impl.security.ExecutionContext;
import oracle.kv.impl.security.KVStorePrivilege;
import oracle.kv.impl.security.SystemPrivilege;
import oracle.kv.impl.topo.PartitionId;

/**
 * A base server handler for subclasses of MultiKeyOperation.
 *
 * Throughput calculation for both iterators
 * +---------------------------------------------------------------------------+
 * |    Op         | Choice | # |          Read        |       Write           |
 * |---------------+--------+---+----------------------+-----------------------|
 * | MultiKeyOper..|  N/A   | - | sum of record sizes  |           0           |
 * +---------------------------------------------------------------------------+
 */
abstract class MultiKeyOperationHandler
        extends InternalOperationHandler
        implements PrivilegedTableAccessor {

    private static final KVAuthorizer UNIVERSAL_AUTHORIZER =
        new KVAuthorizer() {
            @Override
            public boolean allowAccess(DatabaseEntry keyEntry) {
                return true;
            }

            @Override
            public boolean allowFullAccess() {
                return true;
            }
        };

    MultiKeyOperationHandler(OperationHandler handler,
                             OpCode opCode,
                             Class operationType) {
        super(handler, opCode, operationType);
    }

    /**
     * Iterate and return the next batch.
     *
     * @return the key at which to resume the iteration, or null if complete.
     */
    boolean iterate(MultiKeyOperation op,
                    Transaction txn,
                    PartitionId partitionId,
                    byte[] parentKey,
                    boolean majorPathComplete,
                    KeyRange subRange,
                    Depth depth,
                    Direction direction,
                    int batchSize,
                    byte[] resumeKey,
                    CursorConfig cursorConfig,
                    final List results,
                    final KVAuthorizer auth) {

        Scanner scanner = new Scanner(op, txn,
                                      partitionId,
                                      getRepNode(),
                                      parentKey,
                                      majorPathComplete,
                                      subRange,
                                      depth,
                                      direction,
                                      resumeKey,
                                      true, /*moveAfterResumeKey*/
                                      cursorConfig,
                                      LockMode.DEFAULT,
                                      false); /* not key-only */

        DatabaseEntry keyEntry = scanner.getKey();
        DatabaseEntry dataEntry = scanner.getData();
        Cursor cursor = scanner.getCursor();
        boolean moreElements;

        /*
         * When doing a scan that includes the parent key the parent is handled
         * separately from the descendants. The parent key does not make a
         * valid resume key, so if the batch size is 1, increase it to ensure
         * that the parent key is not the resume key.
         */
        if (resumeKey == null && batchSize == 1) {
            ++batchSize;
        }
        try {
            while ((moreElements = scanner.next()) == true) {
                if (!auth.allowAccess(keyEntry)) {

                    /*
                     * The requestor is not permitted to see this entry,
                     * so silently skip it.
                     */
                    continue;
                }

                final ResultValueVersion valVers =
                    makeValueVersion(cursor, dataEntry, scanner.getResult());

                results.add(new ResultKeyValueVersion(
                                keyEntry.getData(),
                                valVers.getValueBytes(),
                                valVers.getVersion(),
                                valVers.getExpirationTime()));

                if (batchSize != 0 && results.size() >= batchSize) {
                    break;
                }
            }
        } finally {
            scanner.close();

        }
        return moreElements;
    }

    /**
     * Iterate and return the next batch.
     *
     * @return the key at which to resume the iteration, or null if complete.
     */
    boolean iterateKeys(MultiKeyOperation op,
                        Transaction txn,
                        PartitionId partitionId,
                        byte[] parentKey,
                        boolean majorPathComplete,
                        KeyRange subRange,
                        Depth depth,
                        Direction direction,
                        int batchSize,
                        byte[] resumeKey,
                        CursorConfig cursorConfig,
                        final List results,
                        final KVAuthorizer auth) {

        Scanner scanner = new Scanner(op, txn,
                                      partitionId,
                                      getRepNode(),
                                      parentKey,
                                      majorPathComplete,
                                      subRange,
                                      depth,
                                      direction,
                                      resumeKey,
                                      true, /*moveAfterResumeKey*/
                                      cursorConfig,
                                      LockMode.DEFAULT,
                                      true); /* key-only */

        DatabaseEntry keyEntry = scanner.getKey();
        boolean moreElements;
        /*
         * When doing a scan that includes the parent key the parent is handled
         * separately from the descendants. The parent key does not make a
         * valid resume key, so if the batch size is 1, increase it to ensure
         * that the parent key is not the resume key.
         */
        if (resumeKey == null && batchSize == 1) {
            ++batchSize;
        }
        try {
            while ((moreElements = scanner.next()) == true) {
                if (!auth.allowAccess(keyEntry)) {

                    /*
                     * The requestor is not permitted to see this entry,
                     * so silently skip it.
                     */
                    continue;
                }
                op.addReadBytes(getStorageSize(scanner.getCursor()));
                addKeyResult(results,
                             keyEntry.getData(),
                             scanner.getExpirationTime());
                if (batchSize != 0 && results.size() >= batchSize) {
                    break;
                }
            }
        } finally {
            scanner.close();

        }
        return moreElements;
    }

    /**
     * When the parent key is null or too short to determine the keyspace it
     * may access, returns a KVAuthorizer instance that will determine on a
     * per-KV entry basis whether entries are visible to the user. This
     * implements a policy that allows callers to iterate over the store
     * without generating errors if they come across something that they aren't
     * allowed access to.
     */
    KVAuthorizer checkPermission(T op) {
        return checkPermission(op.getParentKey());
    }

    KVAuthorizer checkPermission(byte[] key) {

        /* If security is not enabled, returns a fully accessible checker */
        if (ExecutionContext.getCurrent() == null) {
            return UNIVERSAL_AUTHORIZER;
        }

        final Set checkers = new HashSet<>();

        /* Checks if access server private keyspace is legal */
        if (!isInternalRequestor()) {
            if (key == null ||
                Keyspace.mayBePrivateAccess(key)) {
                checkers.add(Keyspace.privateKeyAccessChecker);
            }
        }

        /* Checks if access Avro schema keyspace is legal */
        if (!hasSchemaAccessPrivileges()) {
            if (key == null ||
                Keyspace.mayBeSchemaAccess(key)) {
                checkers.add(Keyspace.schemaKeyAccessChecker);
            }
        }

        /*
         * Checks if access general keyspace is legal.  When building the
         * execution context, the possibility of accessing exact private or
         * schema keyspace has been check. So at here, we know that we will
         * possibly access the general keyspace. If users do not have a full
         * access to general keyspace, the table access checker will be added
         * to allow table-specific permission checking.
         */
        if (!hasGeneralAccessPrivileges()) {
            checkers.add(new TableAccessChecker(operationHandler, this));
        } else {
            /*
             * Check if accessing system tables is legal when current execution
             * context has general access privileges. Only read access is
             * allowed for client request.
             */
            checkers.add(new SysTableAccessChecker(operationHandler));
        }

        if (checkers.isEmpty()) {
            /*
             * Entries either cannot possible fall into the server private and
             * schema key space, or else we have an legal keyspace requestor,
             * so each access is guaranteed to be authorized.
             */
            return UNIVERSAL_AUTHORIZER;
        }

        /*
         * We have a user-level requestor, and either the parent key is null or
         * the parent key is not null, but is too short to be sure that no
         * illegal access will result, so entries could possibly fall into the
         * server private or schema key space.  Return an authorizer that will
         * check keys on each access.
         */
        return new KeyspaceAccessAuthorizer(checkers);
    }

    /**
     * Checks whether the requestor has required privileges for schema access.
     */
    boolean hasSchemaAccessPrivileges() {
        final ExecutionContext currentContext = ExecutionContext.getCurrent();
        if (currentContext == null) {
            return true;
        }
        return currentContext.hasAllPrivileges(schemaAccessPrivileges());
    }

    /**
     * Checks whether the requestor has required privileges for general access.
     */
    boolean hasGeneralAccessPrivileges() {
        final ExecutionContext currentContext = ExecutionContext.getCurrent();
        if (currentContext == null) {
            return true;
        }
        return currentContext.hasAllPrivileges(generalAccessPrivileges());
    }

    @Override
    List getRequiredPrivileges(T op) {
        /*
         * If the parent key exactly falls in schema, private or general
         * keyspace, we can quickly check whether the user has the required
         * privilege.
         */
        final byte[] parentKey = op.getParentKey();
        if (parentKey != null) {
            final KeyspaceType keyspace = Keyspace.identifyKeyspace(parentKey);
            switch (keyspace) {
            case PRIVATE:
                return SystemPrivilege.internalPrivList;
            case SCHEMA:
                return schemaAccessPrivileges();
            case GENERAL:
                if (!Keyspace.mayBePrivateAccess(parentKey) &&
                    !Keyspace.mayBeSchemaAccess(parentKey)) {

                    /* Access exactly the general space, checks only the basic
                     * privilege for authentication, and let per-key checking
                     * be performed in each iteration.
                     */
                    return SystemPrivilege.usrviewPrivList;
                }
                break;
            default:
                throw new AssertionError();
            }
        }
        /*
         * The key is null or is too short to determine, we just check the
         * basic authentication here and defer the per-key checking to
         * iteration.
         */
        return SystemPrivilege.usrviewPrivList;
    }

    /**
     * Returns the required privileges for Avro schema keyspace access.
     */
    abstract List schemaAccessPrivileges();

    /**
     * Returns the required privileges for accessing the whole store keyspace
     * outside the schema and the server private keyspaces.
     */
    abstract List generalAccessPrivileges();

    /**
     * An authorizer for checking the permission of keyspace access of KVStore.
     * Currently, we implement the checking for the server-private keyspace,
     * the Avro schema keyspace, the table keyspace and non-table general
     * keyspace.
     */
    private static class KeyspaceAccessAuthorizer implements KVAuthorizer {

        private final Set keyCheckers;

        /**
         * Constructs a KeyspaceAccessAuthorizer
         *
         * @param keyCheckers set of KeyAccessCheckers.
         */
        private KeyspaceAccessAuthorizer(Set keyCheckers) {
            this.keyCheckers = keyCheckers;
        }

        @Override
        public boolean allowAccess(DatabaseEntry keyEntry) {
            final byte[] key = keyEntry.getData();
            for (final KeyAccessChecker checker : keyCheckers) {
                if (!checker.allowAccess(key)) {
                    return false;
                }
            }
            return true;
        }

        @Override
        public boolean allowFullAccess() {
            return false;
        }
    }

    static void addKeyResult(List results,
                             byte[] key,
                             long expirationTime) {
        results.add(new ResultKey(key, expirationTime));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy