Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add admin mode to PermissionsChecker #701

Merged
merged 1 commit into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/us/kbase/workspace/database/ObjectResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ private ObjectResolver(
ReferenceSearchMaximumSizeExceededException {
this.db = db;
this.user = user;
this.permissionsFactory = new PermissionsCheckerFactory(db, user);
this.permissionsFactory = PermissionsCheckerFactory.getBuilder(db)
.withUser(user).withAsAdmin(asAdmin).build();
this.objects = Collections.unmodifiableList(objects);
this.nullIfInaccessible = nullIfInaccessible;
this.asAdmin = asAdmin;
Expand Down Expand Up @@ -197,9 +198,9 @@ private void resolve()
//handle the faster cases first, fail before the searches
Map<ObjectIdentifier, ObjectIDResolvedWS> ws = new HashMap<>();
if (!nolookup.isEmpty()) {
ws = permissionsFactory.getObjectChecker(
nolookup, asAdmin ? Permission.NONE : Permission.READ)
.withSuppressErrors(nullIfInaccessible).check();
ws = permissionsFactory.getObjectChecker(nolookup, Permission.READ)
.withSuppressErrors(nullIfInaccessible)
.check();
}
nolookup = null; //gc

Expand Down Expand Up @@ -634,7 +635,7 @@ public Builder withMaximumObjectsSearched(final int count) {

/** Run the resolution as an admin - e.g. all workspaces are accessible.
* @param asAdmin
* @return
* @return this builder.
*/
public Builder withAsAdmin(final boolean asAdmin) {
this.asAdmin = asAdmin;
Expand Down
142 changes: 109 additions & 33 deletions src/us/kbase/workspace/database/PermissionsCheckerFactory.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package us.kbase.workspace.database;

import static us.kbase.workspace.database.Util.nonNull;
import static java.util.Objects.requireNonNull;
import static us.kbase.workspace.database.Util.noNulls;
import static us.kbase.common.utils.StringUtils.checkString;

Expand All @@ -13,6 +13,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

import com.google.common.collect.ImmutableMap;

Expand All @@ -39,31 +40,39 @@

private final WorkspaceDatabase db;
private final WorkspaceUser user;
private final boolean asAdmin;

/** Create a new factory.
* @param db the workspace database that will be queried for permissions.
* @param user the user for whom permissions will be checked, or null for an anonymous user.
*/
public PermissionsCheckerFactory(final WorkspaceDatabase db, final WorkspaceUser user) {
nonNull(db, "db");
private PermissionsCheckerFactory(
final WorkspaceDatabase db,
final WorkspaceUser user,
final boolean asAdmin) {
this.db = db;
this.user = user; //TODO CODE make an AnonymousUser class or something, or use Optional
this.user = user;
this.asAdmin = asAdmin;
}

/** Returns the user associated with this permissions checker factory, or null for an anonymous
* user.
/** Returns the user associated with this permissions checker factory or an empty optional
* for an anonymous user.
* @return the user.
*/
public WorkspaceUser getUser() {
return user;
public Optional<WorkspaceUser> getUser() {
return Optional.ofNullable(user);
}

/** Returns whether the checker is running as an admin, which skips permission checks (but
* not deletion, locked, etc. checks).
* @return true if permissions checks will be skipped.
*/
public boolean isAsAdmin() {
return asAdmin;
}

private abstract class AbstractPermissionsChecker<T> {
final Permission perm;
String operation;

private AbstractPermissionsChecker(final Permission perm) {
nonNull(perm, "perm");
requireNonNull(perm, "perm");
this.perm = perm;
this.operation = ops.get(perm);
}
Expand Down Expand Up @@ -91,7 +100,8 @@

/** Get a permissions checker for multiple workspaces.
* @param workspaces the workspaces for which permissions will be checked.
* @param perm the required permission.
* @param perm the required permission. Even with the checker in admin mode, it's important
* to set the permission correctly so that the locked workspace check functions properly.
* @return a new permissions checker.
*/
public WorkspacePermissionsChecker getWorkspaceChecker(
Expand All @@ -113,7 +123,7 @@
final List<WorkspaceIdentifier> workspaces,
final Permission perm) {
super(perm);
nonNull(workspaces, "workspaces");
requireNonNull(workspaces, "workspaces");
if (workspaces.isEmpty()) {
throw new IllegalArgumentException("No workspace identifiers provided");
}
Expand Down Expand Up @@ -143,10 +153,17 @@
CorruptWorkspaceDBException, WorkspaceAuthorizationException {
final Map<WorkspaceIdentifier, ResolvedWorkspaceID> rwsis =
db.resolveWorkspaces(new HashSet<>(workspaces));
final PermissionSet perms = db.getPermissions(user, new HashSet<>(rwsis.values()));
final PermissionSet perms;
if (asAdmin) {
perms = null;
} else {
perms = db.getPermissions(user, new HashSet<>(rwsis.values()));
}
for (final Entry<WorkspaceIdentifier, ResolvedWorkspaceID> e: rwsis.entrySet()) {
comparePermission(user, perm, perms.getPermission(e.getValue()),
e.getKey(), operation);
if (!asAdmin) {
comparePermission(user, perm, perms.getPermission(e.getValue()),
e.getKey(), operation);
}
checkLocked(perm, e.getValue());
}
return rwsis;
Expand All @@ -155,7 +172,8 @@

/** Get a permissions checker for a single workspace.
* @param workspace the workspace for which permissions will be checked.
* @param perm the required permission.
* @param perm the required permission. Even with the checker in admin mode, it's important
* to set the permission correctly so that the locked workspace check functions properly.
* @return a new permissions checker.
*/
public SingleWorkspacePermissionsChecker getWorkspaceChecker(
Expand All @@ -178,7 +196,7 @@
final WorkspaceIdentifier workspace,
final Permission perm) {
super(perm);
nonNull(workspace, "Workspace identifier cannot be null");
requireNonNull(workspace, "Workspace identifier cannot be null");
checker = getWorkspaceChecker(Arrays.asList(workspace), perm);
wsi = workspace;
}
Expand Down Expand Up @@ -214,7 +232,8 @@

/** Get a permissions checker for multiple objects.
* @param objects the objects for which permissions will be checked.
* @param perm the required permission.
* @param perm the required permission. Even with the checker in admin mode, it's important
* to set the permission correctly so that the locked workspace check functions properly.
* @return a new permissions checker.
*/
public ObjectPermissionsChecker getObjectChecker(
Expand Down Expand Up @@ -272,7 +291,7 @@
final Collection<ObjectIdentifier> objects,
final Permission perm) {
super(perm);
nonNull(objects, "objects");
requireNonNull(objects, "objects");
if (objects.isEmpty()) {
throw new IllegalArgumentException("No object identifiers provided");
}
Expand Down Expand Up @@ -316,21 +335,28 @@
if (suppressErrors && !includeDeletedWorkspaces) {
removeDeletedWorkspaces(rwsis);
}
final PermissionSet perms = db.getPermissions(user, new HashSet<>(rwsis.values()));
final PermissionSet perms;
if (asAdmin) {
perms = null;
} else {
perms = db.getPermissions(user, new HashSet<>(rwsis.values()));
}
final Map<ObjectIdentifier, ObjectIDResolvedWS> ret = new HashMap<>();
for (final ObjectIdentifier o: objects) {
if (!rwsis.containsKey(o.getWorkspaceIdentifier())) {
continue; //missing workspace
}
final ResolvedWorkspaceID r = rwsis.get(o.getWorkspaceIdentifier());
try {
comparePermission(user, perm, perms.getPermission(r), o, operation);
} catch (WorkspaceAuthorizationException wae) {
if (suppressErrors) {
continue;
} else {
// contrary to ECLEmma's output, this path is in fact tested
throwInaccessibleObjectException(o, wae);
if (!asAdmin) {
try {
comparePermission(user, perm, perms.getPermission(r), o, operation);
} catch (WorkspaceAuthorizationException wae) {
if (suppressErrors) {
continue;
} else {
// contrary to ECLEmma's output, this path is in fact tested
throwInaccessibleObjectException(o, wae);

Check warning on line 358 in src/us/kbase/workspace/database/PermissionsCheckerFactory.java

View check run for this annotation

Codecov / codecov/patch

src/us/kbase/workspace/database/PermissionsCheckerFactory.java#L358

Added line #L358 was not covered by tests
}
}
}
try {
Expand All @@ -346,7 +372,8 @@

/** Get a permissions checker for a single object.
* @param object the object for which permissions will be checked.
* @param perm the required permission.
* @param perm the required permission. Even with the checker in admin mode, it's important
* to set the permission correctly so that the locked workspace check functions properly.
* @return a new permissions checker.
*/
public SingleObjectPermissionsChecker getObjectChecker(
Expand All @@ -369,7 +396,7 @@
final ObjectIdentifier object,
final Permission perm) {
super(perm);
nonNull(object, "Object identifier cannot be null");
requireNonNull(object, "Object identifier cannot be null");
checker = getObjectChecker(Arrays.asList(object), perm);
this.object = object;
}
Expand Down Expand Up @@ -501,4 +528,53 @@
throw new InaccessibleObjectException(String.format("Object %s cannot be accessed: %s",
o.getIdentifierString(), wae.getLocalizedMessage()), o, wae);
}

/** Get a builder for a {@link PermissionsCheckerFactory}.
* @param db the workspace database that will be queried for permissions.
* @return the builder.
*/
public static Builder getBuilder(final WorkspaceDatabase db) {
return new Builder(db);
}

/**
* A builder for a {@link PermissionsCheckerFactory}.
*/
public static class Builder {

private final WorkspaceDatabase db;
private WorkspaceUser user = null;
private boolean asAdmin = false;

private Builder(final WorkspaceDatabase db) {
this.db = requireNonNull(db, "db");
}

/** Add the user for whom permissions will be checked, or null for an anonymous user.
* @param user the user.
* @return this builder.
*/
public Builder withUser(final WorkspaceUser user) {
this.user = user;
return this;
}

/** Run the checker in admin mode, skipping permission checks, but not existence or
* locked workspace checks. It's important to still include the required permission for
* the operation when running the check so the lock check works properly.
* @param asAdmin true to run the checker as an admin.
* @return this builder.
*/
public Builder withAsAdmin(final boolean asAdmin) {
this.asAdmin = asAdmin;
return this;
}

/** Build the permissions checker.
* @return the permissions checker.
*/
public PermissionsCheckerFactory build() {
return new PermissionsCheckerFactory(db, user, asAdmin);
}
}
}
Loading
Loading