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

oracle.kv.impl.rep.SecurityMetadataManager 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.rep;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import oracle.kv.impl.fault.DatabaseNotReadyException;
import oracle.kv.impl.fault.RNUnavailableException;
import oracle.kv.impl.fault.SystemFaultException;
import oracle.kv.impl.metadata.Metadata;
import oracle.kv.impl.metadata.Metadata.MetadataType;
import oracle.kv.impl.security.metadata.SecurityMDChange;
import oracle.kv.impl.security.metadata.SecurityMDListener;
import oracle.kv.impl.security.metadata.SecurityMetadata;
import oracle.kv.impl.security.metadata.SecurityMetadataInfo;
import oracle.kv.impl.security.metadata.UserRoleUpdater;

import com.sleepycat.je.rep.ReplicatedEnvironment;

/**
 * All access to the in-memory copy of security metadata on RepNode is
 * coordinated through this class.
 * 

* Note that the RepNode does NOT build a new SecurityMetadata instance when it * starts. The Admin will push its security metadata to the RepNode when it is * created. The push will be performed through the update() of this class. *

* The persistence of the in-memory security metadata copy will be performed * whenever updated. The security metadata will be stored in the DB of RepNode * via the EntityStore on the RepNode's environment. *

* This class may also need to provide a series of update listeners, which will * be used in session state updating caused by any security definition changes. */ public class SecurityMetadataManager extends MetadataManager { /* Default value of maximum length of changes maintained. */ private static final int MAX_CHANGES = 1000; /* In-memory copy of security metadata */ private volatile SecurityMetadata securityMetadata; private final String kvstoreName; /* The max number of changes retained in the security metadata */ private final int maxChanges; /* Track user role changes in security metadata */ private final UserRoleUpdater userRoleTracker = new UserRoleUpdater(); public SecurityMetadataManager(final RepNode repNode, final String storeName, final Logger logger) { this(repNode, storeName, MAX_CHANGES, logger); } public SecurityMetadataManager(final RepNode repNode, final String storeName, final int maxSecMDChanges, final Logger logger) { super(repNode, logger); if (maxSecMDChanges <= 0) { throw new IllegalArgumentException( "Max change limit must be a positive integer."); } this.kvstoreName = storeName; this.maxChanges = maxSecMDChanges; } @Override protected MetadataType getType() { return MetadataType.SECURITY; } /** * Adds a MD change update listener to help track SecurityMD changes. * * @param listener the new listener */ void addUpdateListener(SecurityMDListener listener) { userRoleTracker.addListener(listener); } /** * Notify listeners security metadata changes. * * @param oldMd security metadata memory copy before changed. * @param mdChanges security metadata changes. */ private void invokeListeners(SecurityMetadata oldMd, List mdChanges){ /* * Pass old metadata memory copy to UserRoleTracker for resolving and * filtering metadata changes. */ userRoleTracker.notifyListeners(oldMd, mdChanges); } /** * Updates the security metadata by replacing the entire copy with a new * instance. This is typically done in response to a request from the Admin. * Or if the security metadata cannot be updated incrementally because the * necessary sequence of changes is not available in incremental form. *

* The update is only done if the security metadata is not current. If the * security metadata needs to be updated, but the update failed false is * returned. Otherwise true is returned. Note that a null security metadata * has zero as its sequence number by default. * * @param newSecurityMD the new security metadata copy * * @return false if the update failed */ public synchronized boolean update(final SecurityMetadata newSecurityMD) { if (newSecurityMD == null) { throw new NullPointerException( "The new copy of SecurityMetadata should not be null."); } if (!kvstoreName.equals(newSecurityMD.getKVStoreName())) { throw new IllegalStateException( "Trying to update with security metadata from store: " + newSecurityMD.getKVStoreName() + ", but expected: " + kvstoreName); } final ReplicatedEnvironment repEnv = repNode.getEnv(1); if ((repEnv == null) || !repEnv.getState().isMaster()) { ensureAvailableLogSize(); return false; } securityMetadata = getSecurityMetadata(); final int currentSeqNum = (securityMetadata == null) ? Metadata.EMPTY_SEQUENCE_NUMBER : securityMetadata.getSequenceNumber(); final int newSeqNum = newSecurityMD.getSequenceNumber(); if (currentSeqNum >= newSeqNum) { logger.log(Level.INFO, "Security metadata update skipped. " + "Current seq #: {0} Update seq #: {1}", new Object[]{currentSeqNum, newSeqNum}); return true; } if (persistMetadata(newSecurityMD, maxChanges)) { /* In case previous changes have been discarded */ final int startSeqNum = Math.max(currentSeqNum, newSecurityMD.getFirstChangeSeqNum()); invokeListeners(securityMetadata, newSecurityMD.getChanges(startSeqNum)); securityMetadata = newSecurityMD; logger.log(Level.INFO, "Security metadata updated from seq#: {0} to {1}", new Object[] { currentSeqNum, newSeqNum }); } else { ensureAvailableLogSize(); } return true; } /** * Performs an incremental update to the security metadata. *

* An update may result in the security metadata changes being pruned so * that only the configured number of changes are retained. *

* * @param newSecurityMDInfo the SecurityMetadataInfo containing the changes * to be made * * @return the sequence number of the security metadata resulting from the * operation. If the input metadata contains no changes or if it has a * base sequence number that is greater than the current security * metadata sequence number then no change to the metadata is made. */ public synchronized int update(final SecurityMetadataInfo newSecurityMDInfo) { final ReplicatedEnvironment repEnv = repNode.getEnv(1); if (repEnv == null) { return Metadata.EMPTY_SEQUENCE_NUMBER; } securityMetadata = getSecurityMetadata(); if (!repEnv.getState().isMaster()) { ensureAvailableLogSize(); return securityMetadata.getSequenceNumber(); } final int prevSeqNum = (securityMetadata == null) ? Metadata.EMPTY_SEQUENCE_NUMBER : securityMetadata.getSequenceNumber(); if (newSecurityMDInfo.getChanges() == null || newSecurityMDInfo.getChanges().isEmpty()) { /* Unexpected, should not happen. */ logger.warning( "Empty change list sent for security metadata update"); return prevSeqNum; } int startSeqNum = newSecurityMDInfo.getChanges().get(0).getSeqNum(); if (startSeqNum > (prevSeqNum + 1)) { logger.info("Ignoring security metadata update request. " + "Current seq num: " + prevSeqNum + " first change: " + newSecurityMDInfo.getChanges().get(0).getSeqNum()); return prevSeqNum; } final SecurityMetadata secMDCopy = (securityMetadata == null) ? new SecurityMetadata( kvstoreName, newSecurityMDInfo.getSecurityMetadataId()) : securityMetadata.getCopy(); if (!secMDCopy.apply(newSecurityMDInfo.getChanges())) { /* Not changed */ return prevSeqNum; } if (persistMetadata(secMDCopy, maxChanges)) { /* In case previous changes have been discarded */ startSeqNum = Math.max(startSeqNum, secMDCopy.getFirstChangeSeqNum()); invokeListeners(securityMetadata, secMDCopy.getChanges(startSeqNum)); securityMetadata = secMDCopy; logger.log(Level.INFO, "Security metadata updated from seq#: {0} to {1}", new Object[] { prevSeqNum, securityMetadata.getSequenceNumber() }); } else { ensureAvailableLogSize(); } return securityMetadata.getSequenceNumber(); } /** * Forces a refresh of the security metadata due to it being updated by * the master. Called from the database trigger. */ @Override protected synchronized void update(ReplicatedEnvironment repEnv) { if (securityMetadata == null) { return; } final SecurityMetadata oldMd = securityMetadata.getCopy(); int startSeqNum = oldMd.getSequenceNumber(); securityMetadata = null; getSecurityMetadata(); /* In case previous changes have been discarded */ startSeqNum = Math.max( startSeqNum, securityMetadata.getFirstChangeSeqNum()); invokeListeners(oldMd, securityMetadata.getChanges(startSeqNum)); } public synchronized SecurityMetadata getSecurityMetadata() { if (securityMetadata == null) { try { securityMetadata = fetchMetadata(); } catch (DatabaseNotReadyException dnre) { /* Throwing RNUnavailableException should cause a retry */ throw new RNUnavailableException( "Security metadata database is not opened yet."); } } return securityMetadata; } /** * Check if we are reaching the disk limit when the update method is called * but we are not really updating the in-memory or persistent copy. We are * not really updating for various reasons, including the disk limit * exception itself and updating on replica, etc. When that happens, it * means we will have stale security metadata for a while before the disk * limit problem can be solved. The best approach then to protect us from * unauthorized access is shut down this node without restart. */ private void ensureAvailableLogSize() { if (repNode.hasAvailableLogSize()) { return; } throw new SystemFaultException( "Failed to update security metadata and " + "reached disk limit", new RuntimeException( String.format("Available log size: %d", repNode.getAvailableLogSize()))); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy