Null safety via JSpecify spring-security-acl
Closes gh-18401
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
id 'javadoc-warnings-error'
|
||||
id 'security-nullability'
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
|
||||
+5
-3
@@ -18,6 +18,8 @@ package org.springframework.security.acls.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.acls.model.AccessControlEntry;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AuditableAccessControlEntry;
|
||||
@@ -36,7 +38,7 @@ public class AccessControlEntryImpl implements AccessControlEntry, AuditableAcce
|
||||
|
||||
private Permission permission;
|
||||
|
||||
private final Serializable id;
|
||||
private final @Nullable Serializable id;
|
||||
|
||||
private final Sid sid;
|
||||
|
||||
@@ -46,7 +48,7 @@ public class AccessControlEntryImpl implements AccessControlEntry, AuditableAcce
|
||||
|
||||
private final boolean granting;
|
||||
|
||||
public AccessControlEntryImpl(Serializable id, Acl acl, Sid sid, Permission permission, boolean granting,
|
||||
public AccessControlEntryImpl(@Nullable Serializable id, Acl acl, Sid sid, Permission permission, boolean granting,
|
||||
boolean auditSuccess, boolean auditFailure) {
|
||||
Assert.notNull(acl, "Acl required");
|
||||
Assert.notNull(sid, "Sid required");
|
||||
@@ -133,7 +135,7 @@ public class AccessControlEntryImpl implements AccessControlEntry, AuditableAcce
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable getId() {
|
||||
public @Nullable Serializable getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -99,7 +99,8 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
Authentication authentication = context.getAuthentication();
|
||||
// Check if authorized by virtue of ACL ownership
|
||||
Sid currentUser = createCurrentUser(authentication);
|
||||
if (currentUser.equals(acl.getOwner())
|
||||
Sid owner = acl.getOwner();
|
||||
if (owner != null && currentUser.equals(owner)
|
||||
&& ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
|
||||
return;
|
||||
}
|
||||
@@ -108,8 +109,8 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
Collection<? extends GrantedAuthority> reachableGrantedAuthorities = this.roleHierarchy
|
||||
.getReachableGrantedAuthorities(authentication.getAuthorities());
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(reachableGrantedAuthorities);
|
||||
if (acl.getOwner() instanceof GrantedAuthoritySid
|
||||
&& authorities.contains(((GrantedAuthoritySid) acl.getOwner()).getGrantedAuthority())) {
|
||||
if (owner instanceof GrantedAuthoritySid
|
||||
&& authorities.contains(((GrantedAuthoritySid) owner).getGrantedAuthority())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.acls.model.AccessControlEntry;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AuditableAcl;
|
||||
@@ -41,7 +43,7 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
|
||||
private Acl parentAcl;
|
||||
private @Nullable Acl parentAcl;
|
||||
|
||||
private transient AclAuthorizationStrategy aclAuthorizationStrategy;
|
||||
|
||||
@@ -54,10 +56,10 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
private Serializable id;
|
||||
|
||||
// OwnershipAcl
|
||||
private Sid owner;
|
||||
private @Nullable Sid owner;
|
||||
|
||||
// includes all SIDs the WHERE clause covered, even if there was no ACE for a SID
|
||||
private List<Sid> loadedSids = null;
|
||||
private @Nullable List<Sid> loadedSids = null;
|
||||
|
||||
private boolean entriesInheriting = true;
|
||||
|
||||
@@ -97,8 +99,8 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
* @param owner the owner (required)
|
||||
*/
|
||||
public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,
|
||||
PermissionGrantingStrategy grantingStrategy, Acl parentAcl, List<Sid> loadedSids, boolean entriesInheriting,
|
||||
Sid owner) {
|
||||
PermissionGrantingStrategy grantingStrategy, @Nullable Acl parentAcl, @Nullable List<Sid> loadedSids,
|
||||
boolean entriesInheriting, Sid owner) {
|
||||
Assert.notNull(objectIdentity, "Object Identity required");
|
||||
Assert.notNull(id, "Id required");
|
||||
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
|
||||
@@ -117,7 +119,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
* Private no-argument constructor for use by reflection-based persistence tools along
|
||||
* with field-level access.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@SuppressWarnings({ "unused", "NullAway.Init" })
|
||||
private AclImpl() {
|
||||
}
|
||||
|
||||
@@ -199,7 +201,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSidLoaded(List<Sid> sids) {
|
||||
public boolean isSidLoaded(@Nullable List<Sid> sids) {
|
||||
// If loadedSides is null, this indicates all SIDs were loaded
|
||||
// Also return true if the caller didn't specify a SID to find
|
||||
if ((this.loadedSids == null) || (sids == null) || sids.isEmpty()) {
|
||||
@@ -238,19 +240,19 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sid getOwner() {
|
||||
public @Nullable Sid getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(Acl newParent) {
|
||||
public void setParent(@Nullable Acl newParent) {
|
||||
this.aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
|
||||
Assert.isTrue(newParent == null || !newParent.equals(this), "Cannot be the parent of yourself");
|
||||
this.parentAcl = newParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Acl getParentAcl() {
|
||||
public @Nullable Acl getParentAcl() {
|
||||
return this.parentAcl;
|
||||
}
|
||||
|
||||
|
||||
+10
-4
@@ -18,6 +18,8 @@ package org.springframework.security.acls.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.security.acls.model.AclCache;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
@@ -78,13 +80,13 @@ public class SpringCacheBasedAclCache implements AclCache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
|
||||
public @Nullable MutableAcl getFromCache(ObjectIdentity objectIdentity) {
|
||||
Assert.notNull(objectIdentity, "ObjectIdentity required");
|
||||
return getFromCache((Object) objectIdentity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableAcl getFromCache(Serializable pk) {
|
||||
public @Nullable MutableAcl getFromCache(Serializable pk) {
|
||||
Assert.notNull(pk, "Primary key (identifier) required");
|
||||
return getFromCache((Object) pk);
|
||||
}
|
||||
@@ -101,12 +103,16 @@ public class SpringCacheBasedAclCache implements AclCache {
|
||||
this.cache.put(acl.getId(), acl);
|
||||
}
|
||||
|
||||
private MutableAcl getFromCache(Object key) {
|
||||
private @Nullable MutableAcl getFromCache(Object key) {
|
||||
Cache.ValueWrapper element = this.cache.get(key);
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return initializeTransientFields((MutableAcl) element.get());
|
||||
Object value = element.get();
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return initializeTransientFields((MutableAcl) value);
|
||||
}
|
||||
|
||||
private MutableAcl initializeTransientFields(MutableAcl value) {
|
||||
|
||||
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Basic implementation of access control lists (ACLs) interfaces.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.UUID;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
@@ -67,10 +68,10 @@ class AclClassIdUtils {
|
||||
* @return The identifier in the appropriate target Java type. Typically Long or UUID.
|
||||
* @throws SQLException
|
||||
*/
|
||||
Serializable identifierFrom(Serializable identifier, ResultSet resultSet) throws SQLException {
|
||||
if (isString(identifier) && hasValidClassIdType(resultSet)
|
||||
&& canConvertFromStringTo(classIdTypeFrom(resultSet))) {
|
||||
return convertFromStringTo((String) identifier, classIdTypeFrom(resultSet));
|
||||
@Nullable Serializable identifierFrom(Serializable identifier, ResultSet resultSet) throws SQLException {
|
||||
Class<? extends Serializable> classIdType = classIdTypeFrom(resultSet);
|
||||
if (isString(identifier) && classIdType != null && canConvertFromStringTo(classIdType)) {
|
||||
return convertFromStringTo((String) identifier, classIdType);
|
||||
}
|
||||
// Assume it should be a Long type
|
||||
return convertToLong(identifier);
|
||||
@@ -86,11 +87,17 @@ class AclClassIdUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Serializable> Class<T> classIdTypeFrom(ResultSet resultSet) throws SQLException {
|
||||
return classIdTypeFrom(resultSet.getString(DEFAULT_CLASS_ID_TYPE_COLUMN_NAME));
|
||||
private <T extends Serializable> @Nullable Class<T> classIdTypeFrom(ResultSet resultSet) throws SQLException {
|
||||
try {
|
||||
return classIdTypeFrom(resultSet.getString(DEFAULT_CLASS_ID_TYPE_COLUMN_NAME));
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
log.debug("Unable to obtain the class id type", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Serializable> Class<T> classIdTypeFrom(String className) {
|
||||
private <T extends Serializable> @Nullable Class<T> classIdTypeFrom(String className) {
|
||||
if (className == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -107,7 +114,7 @@ class AclClassIdUtils {
|
||||
return this.conversionService.canConvert(String.class, targetType);
|
||||
}
|
||||
|
||||
private <T extends Serializable> T convertFromStringTo(String identifier, Class<T> targetType) {
|
||||
private <T extends Serializable> @Nullable T convertFromStringTo(String identifier, Class<T> targetType) {
|
||||
return this.conversionService.convert(identifier, targetType);
|
||||
}
|
||||
|
||||
@@ -121,7 +128,7 @@ class AclClassIdUtils {
|
||||
* exception occurred
|
||||
* @throws IllegalArgumentException if targetType is null
|
||||
*/
|
||||
private Long convertToLong(Serializable identifier) {
|
||||
private @Nullable Long convertToLong(Serializable identifier) {
|
||||
if (this.conversionService.canConvert(identifier.getClass(), Long.class)) {
|
||||
return this.conversionService.convert(identifier, Long.class);
|
||||
}
|
||||
@@ -140,10 +147,10 @@ class AclClassIdUtils {
|
||||
private static class StringToLongConverter implements Converter<String, Long> {
|
||||
|
||||
@Override
|
||||
public Long convert(String identifierAsString) {
|
||||
public Long convert(@Nullable String identifierAsString) {
|
||||
if (identifierAsString == null) {
|
||||
throw new ConversionFailedException(TypeDescriptor.valueOf(String.class),
|
||||
TypeDescriptor.valueOf(Long.class), null, null);
|
||||
TypeDescriptor.valueOf(Long.class), identifierAsString, new NullPointerException());
|
||||
|
||||
}
|
||||
return Long.parseLong(identifierAsString);
|
||||
@@ -154,10 +161,10 @@ class AclClassIdUtils {
|
||||
private static class StringToUUIDConverter implements Converter<String, UUID> {
|
||||
|
||||
@Override
|
||||
public UUID convert(String identifierAsString) {
|
||||
public UUID convert(@Nullable String identifierAsString) {
|
||||
if (identifierAsString == null) {
|
||||
throw new ConversionFailedException(TypeDescriptor.valueOf(String.class),
|
||||
TypeDescriptor.valueOf(UUID.class), null, null);
|
||||
TypeDescriptor.valueOf(UUID.class), identifierAsString, new NullPointerException());
|
||||
|
||||
}
|
||||
return UUID.fromString(identifierAsString);
|
||||
|
||||
@@ -31,6 +31,8 @@ import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -224,7 +226,8 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
* @param findNow Long-based primary keys to retrieve
|
||||
* @param sids
|
||||
*/
|
||||
private void lookupPrimaryKeys(final Map<Serializable, Acl> acls, final Set<Long> findNow, final List<Sid> sids) {
|
||||
private void lookupPrimaryKeys(final Map<Serializable, Acl> acls, final Set<Long> findNow,
|
||||
final @Nullable List<Sid> sids) {
|
||||
Assert.notNull(acls, "ACLs are required");
|
||||
Assert.notEmpty(findNow, "Items to find now required");
|
||||
String sql = computeRepeatingSql(this.lookupPrimaryKeysWhereClause, findNow.size());
|
||||
@@ -264,7 +267,7 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
* automatically create entries if required)
|
||||
*/
|
||||
@Override
|
||||
public final Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids) {
|
||||
public final Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids) {
|
||||
Assert.isTrue(this.batchSize >= 1, "BatchSize must be >= 1");
|
||||
Assert.notEmpty(objects, "Objects to lookup required");
|
||||
// Map<ObjectIdentity,Acl>
|
||||
@@ -323,7 +326,7 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
* properly-configured parent ACLs.
|
||||
*/
|
||||
private Map<ObjectIdentity, Acl> lookupObjectIdentities(final Collection<ObjectIdentity> objectIdentities,
|
||||
List<Sid> sids) {
|
||||
@Nullable List<Sid> sids) {
|
||||
Assert.notEmpty(objectIdentities, "Must provide identities to lookup");
|
||||
|
||||
// contains Acls with StubAclParents
|
||||
@@ -399,8 +402,10 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
}
|
||||
|
||||
// Now we have the parent (if there is one), create the true AclImpl
|
||||
Sid owner = inputAcl.getOwner();
|
||||
Assert.isTrue(owner != null, "Owner is required");
|
||||
AclImpl result = new AclImpl(inputAcl.getObjectIdentity(), inputAcl.getId(), this.aclAuthorizationStrategy,
|
||||
this.grantingStrategy, parent, null, inputAcl.isEntriesInheriting(), inputAcl.getOwner());
|
||||
this.grantingStrategy, parent, null, inputAcl.isEntriesInheriting(), owner);
|
||||
|
||||
// Copy the "aces" from the input to the destination
|
||||
|
||||
@@ -506,9 +511,9 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
|
||||
private final Map<Serializable, Acl> acls;
|
||||
|
||||
private final List<Sid> sids;
|
||||
private final @Nullable List<Sid> sids;
|
||||
|
||||
ProcessResultSet(Map<Serializable, Acl> acls, List<Sid> sids) {
|
||||
ProcessResultSet(Map<Serializable, Acl> acls, @Nullable List<Sid> sids) {
|
||||
Assert.notNull(acls, "ACLs cannot be null");
|
||||
this.acls = acls;
|
||||
this.sids = sids; // can be null
|
||||
@@ -579,6 +584,9 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
// target id type, e.g. UUID.
|
||||
Serializable identifier = (Serializable) rs.getObject("object_id_identity");
|
||||
identifier = BasicLookupStrategy.this.aclClassIdUtils.identifierFrom(identifier, rs);
|
||||
if (identifier == null) {
|
||||
throw new IllegalStateException("Identifier cannot be null");
|
||||
}
|
||||
ObjectIdentity objectIdentity = BasicLookupStrategy.this.objectIdentityGenerator
|
||||
.createObjectIdentity(identifier, rs.getString("class"));
|
||||
|
||||
@@ -670,7 +678,7 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSidLoaded(List<Sid> sids) {
|
||||
public boolean isSidLoaded(@Nullable List<Sid> sids) {
|
||||
throw new UnsupportedOperationException("Stub only");
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
@@ -98,7 +99,7 @@ public class JdbcAclService implements AclService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
|
||||
public @Nullable List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
|
||||
Object[] args = { parentIdentity.getIdentifier().toString(), parentIdentity.getType() };
|
||||
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql,
|
||||
(rs, rowNum) -> mapObjectIdentityRow(rs), args);
|
||||
@@ -109,11 +110,14 @@ public class JdbcAclService implements AclService {
|
||||
String javaType = rs.getString("class");
|
||||
Serializable identifier = (Serializable) rs.getObject("obj_id");
|
||||
identifier = this.aclClassIdUtils.identifierFrom(identifier, rs);
|
||||
if (identifier == null) {
|
||||
throw new IllegalStateException("Identifier cannot be null");
|
||||
}
|
||||
return this.objectIdentityGenerator.createObjectIdentity(identifier, javaType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException {
|
||||
public Acl readAclById(ObjectIdentity object, @Nullable List<Sid> sids) throws NotFoundException {
|
||||
Map<ObjectIdentity, Acl> map = readAclsById(Collections.singletonList(object), sids);
|
||||
Assert.isTrue(map.containsKey(object),
|
||||
() -> "There should have been an Acl entry for ObjectIdentity " + object);
|
||||
@@ -131,7 +135,7 @@ public class JdbcAclService implements AclService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids)
|
||||
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids)
|
||||
throws NotFoundException {
|
||||
Map<ObjectIdentity, Acl> result = this.lookupStrategy.readAclsById(objects, sids);
|
||||
// Check every requested object identity was found (throw NotFoundException if
|
||||
|
||||
+40
-15
@@ -22,6 +22,8 @@ import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
|
||||
import org.springframework.security.acls.domain.AccessControlEntryImpl;
|
||||
@@ -120,6 +122,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
// Need to retrieve the current principal, in order to know who "owns" this ACL
|
||||
// (can be changed later on)
|
||||
Authentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();
|
||||
Assert.isTrue(auth != null, "Authentication required");
|
||||
PrincipalSid sid = new PrincipalSid(auth);
|
||||
|
||||
// Create the acl_object_identity row
|
||||
@@ -155,9 +158,12 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
Assert.isTrue(entry_ instanceof AccessControlEntryImpl, "Unknown ACE class");
|
||||
AccessControlEntryImpl entry = (AccessControlEntryImpl) entry_;
|
||||
|
||||
Assert.state(acl.getId() != null, "ACL ID cannot be null");
|
||||
stmt.setLong(1, (Long) acl.getId());
|
||||
stmt.setInt(2, i);
|
||||
stmt.setLong(3, createOrRetrieveSidPrimaryKey(entry.getSid(), true));
|
||||
Long sidPrimaryKey = createOrRetrieveSidPrimaryKey(entry.getSid(), true);
|
||||
Assert.state(sidPrimaryKey != null, "SID primary key cannot be null");
|
||||
stmt.setLong(3, sidPrimaryKey);
|
||||
stmt.setInt(4, entry.getPermission().getMask());
|
||||
stmt.setBoolean(5, entry.isGranting());
|
||||
stmt.setBoolean(6, entry.isAuditSuccess());
|
||||
@@ -189,11 +195,14 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
* @param allowCreate true if creation is permitted if not found
|
||||
* @return the primary key or null if not found
|
||||
*/
|
||||
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
|
||||
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);
|
||||
protected @Nullable Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
|
||||
List<@Nullable Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);
|
||||
|
||||
if (!classIds.isEmpty()) {
|
||||
return classIds.get(0);
|
||||
Long result = classIds.get(0);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (allowCreate) {
|
||||
@@ -204,7 +213,9 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
this.jdbcOperations.update(this.insertClass, type, idType.getCanonicalName());
|
||||
}
|
||||
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running");
|
||||
return this.jdbcOperations.queryForObject(this.classIdentityQuery, Long.class);
|
||||
Long result = this.jdbcOperations.queryForObject(this.classIdentityQuery, Long.class);
|
||||
Assert.state(result != null, "Failed to retrieve class primary key");
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -219,7 +230,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
* @throws IllegalArgumentException if the <tt>Sid</tt> is not a recognized
|
||||
* implementation.
|
||||
*/
|
||||
protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
|
||||
protected @Nullable Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
|
||||
Assert.notNull(sid, "Sid required");
|
||||
if (sid instanceof PrincipalSid) {
|
||||
String sidName = ((PrincipalSid) sid).getPrincipal();
|
||||
@@ -240,16 +251,22 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
* @param allowCreate true if creation is permitted if not found
|
||||
* @return the primary key or null if not found
|
||||
*/
|
||||
protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal, boolean allowCreate) {
|
||||
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, Long.class, sidIsPrincipal,
|
||||
sidName);
|
||||
protected @Nullable Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal,
|
||||
boolean allowCreate) {
|
||||
List<@Nullable Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, Long.class,
|
||||
sidIsPrincipal, sidName);
|
||||
if (!sidIds.isEmpty()) {
|
||||
return sidIds.get(0);
|
||||
Long result = sidIds.get(0);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (allowCreate) {
|
||||
this.jdbcOperations.update(this.insertSid, sidIsPrincipal, sidName);
|
||||
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running");
|
||||
return this.jdbcOperations.queryForObject(this.sidIdentityQuery, Long.class);
|
||||
Long result = this.jdbcOperations.queryForObject(this.sidIdentityQuery, Long.class);
|
||||
Assert.state(result != null, "Failed to retrieve sid primary key");
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -279,6 +296,9 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
}
|
||||
|
||||
Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);
|
||||
if (oidPrimaryKey == null) {
|
||||
throw new NotFoundException("Object identity not found: " + objectIdentity);
|
||||
}
|
||||
|
||||
// Delete this ACL's ACEs in the acl_entry table
|
||||
deleteEntries(oidPrimaryKey);
|
||||
@@ -319,10 +339,11 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
* @param oid to find
|
||||
* @return the object identity or null if not found
|
||||
*/
|
||||
protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
|
||||
protected @Nullable Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
|
||||
try {
|
||||
return this.jdbcOperations.queryForObject(this.selectObjectIdentityPrimaryKey, Long.class, oid.getType(),
|
||||
oid.getIdentifier().toString());
|
||||
Long result = this.jdbcOperations.queryForObject(this.selectObjectIdentityPrimaryKey, Long.class,
|
||||
oid.getType(), oid.getIdentifier().toString());
|
||||
return result;
|
||||
}
|
||||
catch (DataAccessException notFound) {
|
||||
return null;
|
||||
@@ -340,7 +361,11 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier");
|
||||
|
||||
// Delete this ACL's ACEs in the acl_entry table
|
||||
deleteEntries(retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity()));
|
||||
Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity());
|
||||
if (oidPrimaryKey == null) {
|
||||
throw new NotFoundException("Object identity not found for ACL: " + acl.getObjectIdentity());
|
||||
}
|
||||
deleteEntries(oidPrimaryKey);
|
||||
|
||||
// Create this ACL's ACEs in the acl_entry table
|
||||
createEntries(acl);
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.security.acls.jdbc;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
@@ -42,6 +44,6 @@ public interface LookupStrategy {
|
||||
* {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used to
|
||||
* automatically create entries if required)
|
||||
*/
|
||||
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids);
|
||||
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids);
|
||||
|
||||
}
|
||||
|
||||
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* JDBC-based persistence of ACL information
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.acls.jdbc;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.security.acls.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an individual permission assignment within an {@link Acl}.
|
||||
*
|
||||
@@ -36,7 +38,7 @@ public interface AccessControlEntry extends Serializable {
|
||||
* Obtains an identifier that represents this ACE.
|
||||
* @return the identifier, or <code>null</code> if unsaved
|
||||
*/
|
||||
Serializable getId();
|
||||
@Nullable Serializable getId();
|
||||
|
||||
Permission getPermission();
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.security.acls.model;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an access control list (ACL) for a domain object.
|
||||
*
|
||||
@@ -82,7 +84,7 @@ public interface Acl extends Serializable {
|
||||
* @return the owner (may be <tt>null</tt> if the implementation does not use
|
||||
* ownership concepts)
|
||||
*/
|
||||
Sid getOwner();
|
||||
@Nullable Sid getOwner();
|
||||
|
||||
/**
|
||||
* A domain object may have a parent for the purpose of ACL inheritance. If there is a
|
||||
@@ -103,7 +105,7 @@ public interface Acl extends Serializable {
|
||||
* @return the parent <tt>Acl</tt> (may be <tt>null</tt> if this <tt>Acl</tt> does not
|
||||
* have a parent)
|
||||
*/
|
||||
Acl getParentAcl();
|
||||
@Nullable Acl getParentAcl();
|
||||
|
||||
/**
|
||||
* Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down
|
||||
@@ -189,6 +191,6 @@ public interface Acl extends Serializable {
|
||||
* @return <tt>true</tt> if every passed <tt>Sid</tt> is represented by this
|
||||
* <tt>Acl</tt> instance
|
||||
*/
|
||||
boolean isSidLoaded(List<Sid> sids);
|
||||
boolean isSidLoaded(@Nullable List<Sid> sids);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.security.acls.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.acls.jdbc.JdbcAclService;
|
||||
|
||||
/**
|
||||
@@ -31,9 +33,9 @@ public interface AclCache {
|
||||
|
||||
void evictFromCache(ObjectIdentity objectIdentity);
|
||||
|
||||
MutableAcl getFromCache(ObjectIdentity objectIdentity);
|
||||
@Nullable MutableAcl getFromCache(ObjectIdentity objectIdentity);
|
||||
|
||||
MutableAcl getFromCache(Serializable pk);
|
||||
@Nullable MutableAcl getFromCache(Serializable pk);
|
||||
|
||||
void putInCache(MutableAcl acl);
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.security.acls.model;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Provides retrieval of {@link Acl} instances.
|
||||
*
|
||||
@@ -32,7 +34,7 @@ public interface AclService {
|
||||
* @param parentIdentity to locate children of
|
||||
* @return the children (or <tt>null</tt> if none were found)
|
||||
*/
|
||||
List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity);
|
||||
@Nullable List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity);
|
||||
|
||||
/**
|
||||
* Same as {@link #readAclsById(List)} except it returns only a single Acl.
|
||||
@@ -59,7 +61,7 @@ public interface AclService {
|
||||
* @throws NotFoundException if an {@link Acl} was not found for the requested
|
||||
* {@link ObjectIdentity}
|
||||
*/
|
||||
Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException;
|
||||
Acl readAclById(ObjectIdentity object, @Nullable List<Sid> sids) throws NotFoundException;
|
||||
|
||||
/**
|
||||
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s.
|
||||
@@ -98,6 +100,7 @@ public interface AclService {
|
||||
* @throws NotFoundException if an {@link Acl} was not found for each requested
|
||||
* {@link ObjectIdentity}
|
||||
*/
|
||||
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids) throws NotFoundException;
|
||||
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, @Nullable List<Sid> sids)
|
||||
throws NotFoundException;
|
||||
|
||||
}
|
||||
|
||||
@@ -18,4 +18,7 @@
|
||||
* Interfaces and shared classes to manage access control lists (ACLs) for domain object
|
||||
* instances.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.acls.model;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -24,4 +24,7 @@
|
||||
* older and more verbose attribute/voter/after-invocation approach from versions before
|
||||
* Spring Security 3.0.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.acls;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
Reference in New Issue
Block a user