提交 f5768513 编写于 作者: J jbachorik

8029755: Enhance subject class

Reviewed-by: sla, dfuchs, hawtin
上级 d40fba14
/* /*
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -34,22 +34,14 @@ import javax.security.auth.Subject; ...@@ -34,22 +34,14 @@ import javax.security.auth.Subject;
import javax.management.remote.SubjectDelegationPermission; import javax.management.remote.SubjectDelegationPermission;
import com.sun.jmx.remote.util.CacheMap; import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
public class SubjectDelegator { public class SubjectDelegator {
private static final int PRINCIPALS_CACHE_SIZE = 10;
private static final int ACC_CACHE_SIZE = 10;
private CacheMap<Subject, Principal[]> principalsCache;
private CacheMap<Subject, AccessControlContext> accCache;
/* Return the AccessControlContext appropriate to execute an /* Return the AccessControlContext appropriate to execute an
operation on behalf of the delegatedSubject. If the operation on behalf of the delegatedSubject. If the
authenticatedAccessControlContext does not have permission to authenticatedAccessControlContext does not have permission to
delegate to that subject, throw SecurityException. */ delegate to that subject, throw SecurityException. */
public synchronized AccessControlContext public AccessControlContext
delegatedContext(AccessControlContext authenticatedACC, delegatedContext(AccessControlContext authenticatedACC,
Subject delegatedSubject, Subject delegatedSubject,
boolean removeCallerContext) boolean removeCallerContext)
...@@ -58,56 +50,14 @@ public class SubjectDelegator { ...@@ -58,56 +50,14 @@ public class SubjectDelegator {
if (System.getSecurityManager() != null && authenticatedACC == null) { if (System.getSecurityManager() != null && authenticatedACC == null) {
throw new SecurityException("Illegal AccessControlContext: null"); throw new SecurityException("Illegal AccessControlContext: null");
} }
if (principalsCache == null || accCache == null) {
principalsCache =
new CacheMap<>(PRINCIPALS_CACHE_SIZE);
accCache =
new CacheMap<>(ACC_CACHE_SIZE);
}
// Retrieve the principals for the given
// delegated subject from the cache
//
Principal[] delegatedPrincipals = principalsCache.get(delegatedSubject);
// Convert the set of principals stored in the
// delegated subject into an array of principals
// and store it in the cache
//
if (delegatedPrincipals == null) {
delegatedPrincipals =
delegatedSubject.getPrincipals().toArray(new Principal[0]);
principalsCache.put(delegatedSubject, delegatedPrincipals);
}
// Retrieve the access control context for the
// given delegated subject from the cache
//
AccessControlContext delegatedACC = accCache.get(delegatedSubject);
// Build the access control context to be used
// when executing code as the delegated subject
// and store it in the cache
//
if (delegatedACC == null) {
if (removeCallerContext) {
delegatedACC =
JMXSubjectDomainCombiner.getDomainCombinerContext(
delegatedSubject);
} else {
delegatedACC =
JMXSubjectDomainCombiner.getContext(delegatedSubject);
}
accCache.put(delegatedSubject, delegatedACC);
}
// Check if the subject delegation permission allows the // Check if the subject delegation permission allows the
// authenticated subject to assume the identity of each // authenticated subject to assume the identity of each
// principal in the delegated subject // principal in the delegated subject
// //
final Principal[] dp = delegatedPrincipals; Collection<Principal> ps = getSubjectPrincipals(delegatedSubject);
final Collection<Permission> permissions = new ArrayList<>(dp.length); final Collection<Permission> permissions = new ArrayList<>(ps.size());
for(Principal p : dp) { for(Principal p : ps) {
final String pname = p.getClass().getName() + "." + p.getName(); final String pname = p.getClass().getName() + "." + p.getName();
permissions.add(new SubjectDelegationPermission(pname)); permissions.add(new SubjectDelegationPermission(pname));
} }
...@@ -122,7 +72,15 @@ public class SubjectDelegator { ...@@ -122,7 +72,15 @@ public class SubjectDelegator {
}; };
AccessController.doPrivileged(action, authenticatedACC); AccessController.doPrivileged(action, authenticatedACC);
return delegatedACC; return getDelegatedAcc(delegatedSubject, removeCallerContext);
}
private AccessControlContext getDelegatedAcc(Subject delegatedSubject, boolean removeCallerContext) {
if (removeCallerContext) {
return JMXSubjectDomainCombiner.getDomainCombinerContext(delegatedSubject);
} else {
return JMXSubjectDomainCombiner.getContext(delegatedSubject);
}
} }
/** /**
...@@ -137,11 +95,9 @@ public class SubjectDelegator { ...@@ -137,11 +95,9 @@ public class SubjectDelegator {
public static synchronized boolean public static synchronized boolean
checkRemoveCallerContext(Subject subject) { checkRemoveCallerContext(Subject subject) {
try { try {
final Principal[] dp = for (Principal p : getSubjectPrincipals(subject)) {
subject.getPrincipals().toArray(new Principal[0]);
for (int i = 0 ; i < dp.length ; i++) {
final String pname = final String pname =
dp[i].getClass().getName() + "." + dp[i].getName(); p.getClass().getName() + "." + p.getName();
final Permission sdp = final Permission sdp =
new SubjectDelegationPermission(pname); new SubjectDelegationPermission(pname);
AccessController.checkPermission(sdp); AccessController.checkPermission(sdp);
...@@ -151,4 +107,19 @@ public class SubjectDelegator { ...@@ -151,4 +107,19 @@ public class SubjectDelegator {
} }
return true; return true;
} }
/**
* Retrieves the {@linkplain Subject} principals
* @param subject The subject
* @return If the {@code Subject} is immutable it will return the principals directly.
* If the {@code Subject} is mutable it will create an unmodifiable copy.
*/
private static Collection<Principal> getSubjectPrincipals(Subject subject) {
if (subject.isReadOnly()) {
return subject.getPrincipals();
}
List<Principal> principals = Arrays.asList(subject.getPrincipals().toArray(new Principal[0]));
return Collections.unmodifiableList(principals);
}
} }
/*
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jmx.remote.util;
import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.WeakHashMap;
import com.sun.jmx.mbeanserver.Util;
/**
* <p>Like WeakHashMap, except that the keys of the <em>n</em> most
* recently-accessed entries are kept as {@link SoftReference soft
* references}. Accessing an element means creating it, or retrieving
* it with {@link #get(Object) get}. Because these entries are kept
* with soft references, they will tend to remain even if their keys
* are not referenced elsewhere. But if memory is short, they will
* be removed.</p>
*/
public class CacheMap<K, V> extends WeakHashMap<K, V> {
/**
* <p>Create a <code>CacheMap</code> that can keep up to
* <code>nSoftReferences</code> as soft references.</p>
*
* @param nSoftReferences Maximum number of keys to keep as soft
* references. Access times for {@link #get(Object) get} and
* {@link #put(Object, Object) put} have a component that scales
* linearly with <code>nSoftReferences</code>, so this value
* should not be too great.
*
* @throws IllegalArgumentException if
* <code>nSoftReferences</code> is negative.
*/
public CacheMap(int nSoftReferences) {
if (nSoftReferences < 0) {
throw new IllegalArgumentException("nSoftReferences = " +
nSoftReferences);
}
this.nSoftReferences = nSoftReferences;
}
public V put(K key, V value) {
cache(key);
return super.put(key, value);
}
public V get(Object key) {
cache(Util.<K>cast(key));
return super.get(key);
}
/* We don't override remove(Object) or try to do something with
the map's iterators to detect removal. So we may keep useless
entries in the soft reference list for keys that have since
been removed. The assumption is that entries are added to the
cache but never removed. But the behavior is not wrong if
they are in fact removed -- the caching is just less
performant. */
private void cache(K key) {
Iterator<SoftReference<K>> it = cache.iterator();
while (it.hasNext()) {
SoftReference<K> sref = it.next();
K key1 = sref.get();
if (key1 == null)
it.remove();
else if (key.equals(key1)) {
// Move this element to the head of the LRU list
it.remove();
cache.add(0, sref);
return;
}
}
int size = cache.size();
if (size == nSoftReferences) {
if (size == 0)
return; // degenerate case, equivalent to WeakHashMap
it.remove();
}
cache.add(0, new SoftReference<K>(key));
}
/* List of soft references for the most-recently referenced keys.
The list is in most-recently-used order, i.e. the first element
is the most-recently referenced key. There are never more than
nSoftReferences elements of this list.
If we didn't care about J2SE 1.3 compatibility, we could use
LinkedHashSet in conjunction with a subclass of SoftReference
whose equals and hashCode reflect the referent. */
private final LinkedList<SoftReference<K>> cache =
new LinkedList<SoftReference<K>>();
private final int nSoftReferences;
}
/*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 7654321
* @summary Tests the CacheMap class.
* @author Eamonn McManus
* @run clean CacheMapTest
* @run build CacheMapTest
* @run main CacheMapTest
*/
import java.util.Iterator;
import java.util.Map;
import com.sun.jmx.remote.util.CacheMap;
public class CacheMapTest {
public static void main(String[] args) {
try {
boolean ok = test(5) && test(100);
if (ok) {
System.out.println("Test completed");
return;
} else {
System.out.println("Test failed!");
System.exit(1);
}
} catch (Exception e) {
System.err.println("Unexpected exception: " + e);
e.printStackTrace();
System.exit(1);
}
}
private static boolean test(int cacheSize) throws Exception {
System.out.println("CacheMap test with cache size " + cacheSize);
CacheMap map = new CacheMap(cacheSize);
int size = 0;
int maxIterations = cacheSize * 10;
while (map.size() == size && size < maxIterations) {
Integer key = new Integer(size);
Object x = map.put(key, "x");
if (x != null) {
System.out.println("Map already had entry " + key + "!");
return false;
}
x = map.get(key);
if (!"x".equals(x)) {
System.out.println("Got back surprising value: " + x);
return false;
}
size++;
}
System.out.println("Map size is " + map.size() + " after inserting " +
size + " elements");
do {
System.gc();
Thread.sleep(1);
System.out.println("Map size is " + map.size() + " after GC");
} while (map.size() > cacheSize);
if (map.size() < cacheSize) {
System.out.println("Map shrank to less than cache size: " +
map.size() + " (surprising but not wrong)");
} else
System.out.println("Map shrank to cache size as expected");
int lowest = size - cacheSize;
// lowest value that can still be in cache if LRU is respected
for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry) it.next();
Integer x = (Integer) entry.getKey();
int xx = x.intValue();
if (xx < lowest || xx >= size) {
System.out.println("Old value remained (" + x + "), " +
"expected none earlier than " + lowest);
return false;
}
Object xxx = entry.getValue();
if (!"x".equals(xxx)) {
System.out.println("Got back surprising value: " + xxx);
return false;
}
}
if (map.size() > 0)
System.out.println("Remaining elements are the most recent ones");
System.out.println("Test passed");
return true;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册