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

org.wildfly.security.auth.realm.CachingSecurityRealm Maven / Gradle / Ivy

The newest version!
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2016 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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.wildfly.security.auth.realm;

import static org.wildfly.common.Assert.checkNotNullParam;
import static org.wildfly.security.auth.realm.ElytronMessages.log;
import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS;

import java.security.Principal;
import java.security.Provider;
import java.security.spec.AlgorithmParameterSpec;
import java.util.function.Supplier;

import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.server.IdentityCredentials;
import org.wildfly.security.auth.server.RealmIdentity;
import org.wildfly.security.auth.server.RealmUnavailableException;
import org.wildfly.security.auth.server.SecurityRealm;
import org.wildfly.security.auth.server.event.RealmEvent;
import org.wildfly.security.authz.Attributes;
import org.wildfly.security.authz.AuthorizationIdentity;
import org.wildfly.security.cache.RealmIdentityCache;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.evidence.Evidence;
import org.wildfly.security.evidence.PasswordGuessEvidence;
import org.wildfly.security.password.Password;
import org.wildfly.security.password.interfaces.ClearPassword;

/**
 * 

A wrapper class that provides caching capabilities for a {@link SecurityRealm} and its identities. * * @author Pedro Igor */ public class CachingSecurityRealm implements SecurityRealm { private final Supplier providerSupplier; private final SecurityRealm realm; private final RealmIdentityCache cache; /** * Creates a new instance. * * @param realm the {@link SecurityRealm} whose {@link RealmIdentity} should be cached. * @param cache the {@link RealmIdentityCache} instance */ public CachingSecurityRealm(SecurityRealm realm, RealmIdentityCache cache) { this(realm, cache, INSTALLED_PROVIDERS); } /** * Creates a new instance. * * @param realm the {@link SecurityRealm} whose {@link RealmIdentity} should be cached. * @param cache the {@link RealmIdentityCache} instance * @param providerSupplier the provider supplier to use for verification purposes (must not be {@code null}) */ public CachingSecurityRealm(SecurityRealm realm, RealmIdentityCache cache, Supplier providerSupplier) { this.realm = checkNotNullParam("realm", realm); this.cache = checkNotNullParam("cache", cache); this.providerSupplier = checkNotNullParam("providers", providerSupplier); if (realm instanceof CacheableSecurityRealm) { CacheableSecurityRealm cacheable = CacheableSecurityRealm.class.cast(realm); cacheable.registerIdentityChangeListener(this::removeFromCache); } else { throw ElytronMessages.log.realmCacheUnexpectedType(realm, CacheableSecurityRealm.class); } } @Override public RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailableException { RealmIdentity cached = cache.get(principal); if (cached != null) { log.tracef("Returning cached RealmIdentity for '%s'", principal); return cached; } RealmIdentity realmIdentity = getCacheableRealm().getRealmIdentity(principal); if (!realmIdentity.exists()) { log.tracef("RealmIdentity for '%s' does not exist, skipping cache.'", principal); return realmIdentity; } RealmIdentity cachedIdentity = new RealmIdentity() { final RealmIdentity identity = realmIdentity; AuthorizationIdentity authorizationIdentity = null; Attributes attributes = null; IdentityCredentials credentials = IdentityCredentials.NONE; @Override public Principal getRealmIdentityPrincipal() { return identity.getRealmIdentityPrincipal(); } @Override public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { if (credentials.contains(credentialType, algorithmName, parameterSpec)) { if (log.isTraceEnabled()) { log.tracef("getCredentialAcquireSupport credentialType='%s' with algorithmName='%' known for pincipal='%s'", credentialType.getName(), algorithmName, principal.getName()); } return credentials.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); } Credential credential = identity.getCredential(credentialType, algorithmName, parameterSpec); if (credential != null) { if (log.isTraceEnabled()) { log.tracef("getCredentialAcquireSupport Credential for credentialType='%s' with algorithmName='%' obtained from identity - caching for principal='%s'", credentialType.getName(), algorithmName, principal.getName()); } credentials = credentials.withCredential(credential); } return credentials.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); } @Override public C getCredential(Class credentialType) throws RealmUnavailableException { if (credentials.contains(credentialType)) { if (log.isTraceEnabled()) { log.tracef("getCredential credentialType='%s' cached, returning cached credential for principal='%s'", credentialType.getName(), principal.getName()); } return credentials.getCredential(credentialType); } Credential credential = identity.getCredential(credentialType); if (credential != null) { if (log.isTraceEnabled()) { log.tracef("getCredential credentialType='%s' obtained from identity - caching for principal='%s'", credentialType.getName(), principal.getName()); } credentials = credentials.withCredential(credential); } return credentials.getCredential(credentialType); } @Override public C getCredential(Class credentialType, String algorithmName) throws RealmUnavailableException { if (credentials.contains(credentialType, algorithmName)) { if (log.isTraceEnabled()) { log.tracef("getCredential credentialType='%s' with algorithmName='%' cached, returning cached credential for principal='%s'", credentialType.getName(), algorithmName, principal.getName()); } return credentials.getCredential(credentialType, algorithmName); } Credential credential = identity.getCredential(credentialType, algorithmName); if (credential != null) { if (log.isTraceEnabled()) { log.tracef("getCredential credentialType='%s' with algorithmName='%' obtained from identity - caching.", credentialType.getName(), algorithmName); } credentials = credentials.withCredential(credential); } return credentials.getCredential(credentialType, algorithmName); } @Override public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { if (credentials.contains(credentialType, algorithmName, parameterSpec)) { if (log.isTraceEnabled()) { log.tracef("getCredential credentialType='%s' with algorithmName='%' cached, returning cached credential for principal='%s'", credentialType.getName(), algorithmName, principal.getName()); } return credentials.getCredential(credentialType, algorithmName, parameterSpec); } Credential credential = identity.getCredential(credentialType, algorithmName, parameterSpec); if (credential != null) { if (log.isTraceEnabled()) { log.tracef("getCredential credentialType='%s' with algorithmName='%' obtained from identity - caching for principal='%s'", credentialType.getName(), algorithmName, principal.getName()); } credentials = credentials.withCredential(credential); } return credentials.getCredential(credentialType, algorithmName, parameterSpec); } @Override public void updateCredential(Credential credential) throws RealmUnavailableException { log.tracef("updateCredential For principal='%s'", principal); try { identity.updateCredential(credential); } finally { removeFromCache(identity.getRealmIdentityPrincipal()); } } @Override public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { if (PasswordGuessEvidence.class.isAssignableFrom(evidenceType)) { if (credentials.canVerify(evidenceType, algorithmName)) { if (log.isTraceEnabled()) { log.tracef("getEvidenceVerifySupport evidenceType='%s' with algorithmName='%' can verify from cache for principal='%s'", evidenceType.getName(), algorithmName, principal.getName()); } return SupportLevel.SUPPORTED; } Credential credential = identity.getCredential(PasswordCredential.class); if (credential != null) { if (log.isTraceEnabled()) { log.tracef("getEvidenceVerifySupport evidenceType='%s' with algorithmName='%' credential obtained from identity and cached for principal='%s'", evidenceType.getName(), algorithmName, principal.getName()); } credentials = credentials.withCredential(credential); if (credential.canVerify(evidenceType, algorithmName)) { return SupportLevel.SUPPORTED; } } } if (log.isTraceEnabled()) { log.tracef("getEvidenceVerifySupport evidenceType='%s' with algorithmName='%' falling back to direct support of identity for principal='%s'", evidenceType.getName(), algorithmName, principal.getName()); } return identity.getEvidenceVerifySupport(evidenceType, algorithmName); } @Override public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { if (evidence instanceof PasswordGuessEvidence) { if (credentials.canVerify(evidence)) { log.tracef("verifyEvidence For principal='%s' using cached credential", principal); boolean credentialsVerified = credentials.verify(providerSupplier, evidence); if (!credentialsVerified) { // since verification failed then verify evidence directly on an identity log.tracef("verifyEvidence for principal='%1$s' using cached credential failed, so trying verifyEvidence for principal='%1$s' using underlying security realm", principal); char[] guess = ((PasswordGuessEvidence) evidence).getGuess(); Password password = ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, guess); if (identity.verifyEvidence(evidence)) { credentials = credentials.without(PasswordCredential.class); credentials = credentials.withCredential(new PasswordCredential(password)); attributes = null; authorizationIdentity = null; return true; } } return credentialsVerified; } Credential credential = identity.getCredential(PasswordCredential.class); if (credential != null) { log.tracef("verifyEvidence Credential obtained from identity and cached for principal='%s'", principal); credentials = credentials.withCredential(credential); if (credential.canVerify(evidence)) { return credential.verify(providerSupplier, evidence); } } char[] guess = ((PasswordGuessEvidence) evidence).getGuess(); Password password = ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, guess); log.tracef("verifyEvidence Falling back to direct support of identity for principal='%s'", principal); if (identity.verifyEvidence(evidence)) { credentials = credentials.withCredential(new PasswordCredential(password)); return true; } return false; } return identity.verifyEvidence(evidence); } @Override public boolean exists() throws RealmUnavailableException { return true; // non-existing identities will not be wrapped } @Override public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { if (authorizationIdentity == null) { log.tracef("getAuthorizationIdentity Caching AuthorizationIdentity for principal='%s'", principal); authorizationIdentity = identity.getAuthorizationIdentity(); } return authorizationIdentity; } @Override public Attributes getAttributes() throws RealmUnavailableException { if (attributes == null) { log.tracef("getAttributes Caching Attributes for principal='%s'", principal); attributes = identity.getAttributes(); } return attributes; } @Override public void dispose() { identity.dispose(); } }; log.tracef("Created wrapper RealmIdentity for '%s' and placing in cache.", principal); cache.put(principal, cachedIdentity); return cachedIdentity; } @Override public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { return getCacheableRealm().getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); } @Override public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { return getCacheableRealm().getEvidenceVerifySupport(evidenceType, algorithmName); } @Override public void handleRealmEvent(RealmEvent event) { getCacheableRealm().handleRealmEvent(event); } /** * Removes a {@link RealmIdentity} referenced by the specified {@link Principal} from the cache. * * @param principal the {@link Principal} that references a previously cached realm identity */ public void removeFromCache(Principal principal) { cache.remove(principal); } /** * Removes all cached identities from the cache. */ public void removeAllFromCache() { cache.clear(); } /** * Gets wrapped backing realm. * * @return the wrapped backing realm */ protected SecurityRealm getCacheableRealm() { return realm; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy