Skip to content

Commit

Permalink
Add anonymous ID translation method to core auth lib
Browse files Browse the repository at this point in the history
  • Loading branch information
MrCreosote committed Jul 21, 2023
1 parent f593cda commit 3192672
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 9 deletions.
1 change: 1 addition & 0 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@
<test name="us.kbase.test.auth2.cryptutils.CryptUtilsTest"/>
<test name="us.kbase.test.auth2.cryptutils.SHA1RandomDataGeneratorTest"/>
<test name="us.kbase.test.auth2.kbase.KBaseAuthConfigTest"/>
<test name="us.kbase.test.auth2.lib.AuthenticationAnonymousIDsTest"/>
<test name="us.kbase.test.auth2.lib.AuthenticationConfigTest"/>
<test name="us.kbase.test.auth2.lib.AuthenticationConstructorTest"/>
<test name="us.kbase.test.auth2.lib.AuthenticationCreateLocalUserTest"/>
Expand Down
33 changes: 32 additions & 1 deletion src/us/kbase/auth2/lib/Authentication.java
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,37 @@ public AuthUser getUserAsAdmin(
return user;
}

/** Translate user anonymous IDs to the user name. The requesting user must have administration
* permissions. A maximum of 10000 IDs may be submitted. UUIDs not found are omitted from
* the results.
* Never returns disabled users or the root user.
* @param token the user's token.
* @param anonymousIDs the IDs to translate.
* @return A mapping of the input IDs to the corresponding user names.
* @throws IllegalParameterException if too many IDs are submitted.
* @throws InvalidTokenException if the provided token is invalid.
* @throws UnauthorizedException if the calling user is not an admin.
* @throws AuthStorageException if an error occurs connecting to the storage system.
*/
public Map<UUID, UserName> getUserNamesFromAnonymousIDs(
final IncomingToken token,
final Set<UUID> anonymousIDs)
throws IllegalParameterException, InvalidTokenException, UnauthorizedException,
AuthStorageException {
noNulls(requireNonNull(anonymousIDs, "anonymousIDs"), "Null ID in anonymousIDs");
getUser(token, new OpReqs("translate anonymous IDs").roles(Role.ADMIN)); // check perms
if (anonymousIDs.isEmpty()) {
return Collections.emptyMap();
}
if (anonymousIDs.size() > MAX_RETURNED_USERS) {
throw new IllegalParameterException(
"Anonymous ID count exceeds maximum of " + MAX_RETURNED_USERS);
}
final Map<UUID, UserName> ret = storage.getUserNamesFromAnonymousIDs(anonymousIDs);
ret.values().remove(UserName.ROOT);
return ret;
}

/** Look up display names for a set of user names. A maximum of 10000 users may be looked up
* at once. Never returns the root user name or disabled users.
* @param token a token for the user requesting the lookup.
Expand All @@ -1063,7 +1094,7 @@ public Map<UserName, DisplayName> getUserDisplayNames(
// just check the token is valid
getTokenSuppressUnauthorized(token, "get user display names");
if (userNames.isEmpty()) {
return new HashMap<>();
return Collections.emptyMap();
}
if (userNames.size() > MAX_RETURNED_USERS) {
throw new IllegalParameterException(
Expand Down
2 changes: 1 addition & 1 deletion src/us/kbase/auth2/lib/user/AuthUser.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class AuthUser {
private final EmailAddress email;
private final UserName userName;
// TODO ANONID add admin method to translate anon IDs to IDs
// TODO ANONID release notes
// TODO ANONID docs and release notes
private final UUID anonymousID;
private final Set<Role> roles;
private final Set<Role> canGrantRoles;
Expand Down
193 changes: 193 additions & 0 deletions src/us/kbase/test/auth2/lib/AuthenticationAnonymousIDsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package us.kbase.test.auth2.lib;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.when;
import static us.kbase.test.auth2.TestCommon.set;
import static us.kbase.test.auth2.lib.AuthenticationTester.initTestMocks;

import java.time.Instant;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import ch.qos.logback.classic.spi.ILoggingEvent;
import jersey.repackaged.com.google.common.collect.ImmutableMap;
import us.kbase.auth2.lib.Authentication;
import us.kbase.auth2.lib.DisplayName;
import us.kbase.auth2.lib.Role;
import us.kbase.auth2.lib.UserName;
import us.kbase.auth2.lib.exceptions.IllegalParameterException;
import us.kbase.auth2.lib.storage.AuthStorage;
import us.kbase.auth2.lib.token.IncomingToken;
import us.kbase.auth2.lib.token.StoredToken;
import us.kbase.auth2.lib.token.TokenType;
import us.kbase.auth2.lib.user.AuthUser;
import us.kbase.test.auth2.TestCommon;
import us.kbase.test.auth2.lib.AuthenticationTester.AbstractAuthOperation;
import us.kbase.test.auth2.lib.AuthenticationTester.TestMocks;

public class AuthenticationAnonymousIDsTest {

private static final UUID UID1 = UUID.randomUUID();
private static final UUID UID2 = UUID.randomUUID();
private static final Instant NOW = Instant.now();

private static List<ILoggingEvent> logEvents;

@BeforeClass
public static void beforeClass() {
logEvents = AuthenticationTester.setUpSLF4JTestLoggerAppender();
}

@Before
public void before() {
logEvents.clear();
}

@Test
public void getUserNamesFromAnonymousIDsEmptyInput() throws Exception {
final TestMocks testauth = initTestMocks();
final AuthStorage storage = testauth.storageMock;
final Authentication auth = testauth.auth;

final IncomingToken token = new IncomingToken("foobar");

when(storage.getToken(token.getHashedToken())).thenReturn(
StoredToken.getBuilder(TokenType.LOGIN, UUID.randomUUID(), new UserName("admin"))
.withLifeTime(Instant.now(), Instant.now()).build());
when(storage.getUser(new UserName("admin"))).thenReturn(AuthUser.getBuilder(
new UserName("admin"), UUID.randomUUID(), new DisplayName("d"), NOW)
.withRole(Role.ADMIN)
.build());

final Map<UUID, UserName> ret = auth.getUserNamesFromAnonymousIDs(token, set());

assertThat("incorrect user map", ret, is(Collections.emptyMap()));
}

@Test
public void getUserNamesFromAnonymousIDs() throws Exception {
// includes test of removing root user
// includes test that dev tokens work
final TestMocks testauth = initTestMocks();
final AuthStorage storage = testauth.storageMock;
final Authentication auth = testauth.auth;

final IncomingToken token = new IncomingToken("foobar");

final Map<UUID, UserName> expected = ImmutableMap.of(
UID1, new UserName("foo"), UID2, new UserName("bar"));

when(storage.getToken(token.getHashedToken())).thenReturn(
StoredToken.getBuilder(TokenType.DEV, UUID.randomUUID(), new UserName("admin"))
.withLifeTime(Instant.now(), Instant.now()).build());
when(storage.getUser(new UserName("admin"))).thenReturn(AuthUser.getBuilder(
new UserName("admin"), UUID.randomUUID(), new DisplayName("d"), NOW)
.withRole(Role.ADMIN)
.build());
final Map<UUID, UserName> mockret = new HashMap<>(); // needs to be mutable
final UUID uid3 = UUID.randomUUID();
mockret.put(UID1, new UserName("foo"));
mockret.put(UID2, new UserName("bar"));
mockret.put(uid3, new UserName("***ROOT***"));
when(storage.getUserNamesFromAnonymousIDs(set(UID2, UID1, uid3))).thenReturn(mockret);

final Map<UUID, UserName> ret = auth.getUserNamesFromAnonymousIDs(token, set(
UID1, UID2, uid3));

assertThat("incorrect user map", ret, is(expected));
}

@Test
public void getUserNamesFromAnonymousIDsFailNulls() throws Exception {
final Authentication auth = initTestMocks().auth;

final IncomingToken t = new IncomingToken("foobar");
final Set<UUID> u = set(UID1);

getUserNamesFromAnonymousIDsFail(auth, null, u, new NullPointerException("token"));
getUserNamesFromAnonymousIDsFail(auth, t, null, new NullPointerException("anonymousIDs"));
getUserNamesFromAnonymousIDsFail(auth, t, set(UID1, null), new NullPointerException(
"Null ID in anonymousIDs"));
}

@Test
public void getUserNamesFromAnonymousIDsExecuteStandardUserCheckingTests() throws Exception {
final IncomingToken token = new IncomingToken("foo");
AuthenticationTester.executeStandardUserCheckingTests(
new AbstractAuthOperation() {

@Override
public IncomingToken getIncomingToken() {
return token;
}

@Override
public void execute(final Authentication auth) throws Exception {
auth.getUserNamesFromAnonymousIDs(token, set(UID1));
}

@Override
public List<ILoggingEvent> getLogAccumulator() {
return logEvents;
}

@Override
public String getOperationString() {
return "translate anonymous IDs";
}
},
set(Role.DEV_TOKEN, Role.SERV_TOKEN, Role.CREATE_ADMIN, Role.ROOT),
set()
);
}

@Test
public void getUserNameFromAnonymousIDsFailTooManyInputs() throws Exception {
// includes test that serv tokens work
final TestMocks testauth = initTestMocks();
final AuthStorage storage = testauth.storageMock;
final Authentication auth = testauth.auth;

final IncomingToken token = new IncomingToken("foobar");

final Set<UUID> inputs = Stream.generate(UUID::randomUUID)
.limit(10001).collect(Collectors.toSet());

when(storage.getToken(token.getHashedToken())).thenReturn(
StoredToken.getBuilder(TokenType.SERV, UUID.randomUUID(), new UserName("admin"))
.withLifeTime(Instant.now(), Instant.now()).build());
when(storage.getUser(new UserName("admin"))).thenReturn(AuthUser.getBuilder(
new UserName("admin"), UUID.randomUUID(), new DisplayName("d"), NOW)
.withRole(Role.ADMIN)
.build());

getUserNamesFromAnonymousIDsFail(auth, token, inputs, new IllegalParameterException(
"Anonymous ID count exceeds maximum of 10000"));
}

private void getUserNamesFromAnonymousIDsFail(
final Authentication auth,
final IncomingToken token,
final Set<UUID> anonymoutIDs,
final Exception expected) {
try {
auth.getUserNamesFromAnonymousIDs(token, anonymoutIDs);
fail("expected exception");
} catch (Exception got) {
TestCommon.assertExceptionCorrect(got, expected);
}
}

}
9 changes: 2 additions & 7 deletions src/us/kbase/test/auth2/lib/AuthenticationTester.java
Original file line number Diff line number Diff line change
Expand Up @@ -487,13 +487,8 @@ private static void testUnauthorizedRole(

ao.getLogAccumulator().clear();

final UserName un;
if (Role.ROOT.equals(r)) {
un = UserName.ROOT;
} else {
un = new UserName("foo");
}

final UserName un = Role.ROOT.equals(r) ? UserName.ROOT : new UserName("foo");

when(storage.getToken(ao.getIncomingToken().getHashedToken())).thenReturn(
StoredToken.getBuilder(TokenType.LOGIN, UUID.randomUUID(), un)
.withLifeTime(Instant.now(), 0).build())
Expand Down

0 comments on commit 3192672

Please sign in to comment.