mirror of
https://github.com/google/nomulus.git
synced 2025-07-25 03:58:34 +02:00
We want to be able to view / test / debug how the registrar console looks for our clients. However, we don't want to accidentally change the data for registrars, especially in a "non-accountable" way (where we later don't know who did that change) So we do 2 things here: - Add a "mode" (read-only and read-write) to the getRegistrarForUser function. We set it according to what we want to do with the registrar. Currently, read-write is only requested for the "update" RegistrarSetting action. Admins will have read-only access to all registrars, but read-write access only to the "admin registrar" (or whatever registrar they are contacts for). - Support an undocumented "clientId=XXX" query param that replaces the "guessClientIdForUser" function in the original page load. We can then set it when we want to view a different account. We also change the navigation links on the HTML page to preserve the query. ------------------------- This might be used also for a better user experience for our clients, especially those with multiple "clientId"s (some registrar entities have multiple "registrar" objects) Currently, they have to have a separate user for each clientId, and only have one user allowed which has both read and write permissions. Using this change, we can give them the possibility to add users on their own, some with read-only access (to view billing information without being able to change anything), and use a single user for all their clientIds. ------------------------- ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=215480610
316 lines
12 KiB
Java
316 lines
12 KiB
Java
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
|
//
|
|
// 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 google.registry.ui.server.registrar;
|
|
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
import static google.registry.testing.AppEngineRule.THE_REGISTRAR_GAE_USER_ID;
|
|
import static google.registry.testing.DatastoreHelper.loadRegistrar;
|
|
import static google.registry.testing.DatastoreHelper.persistResource;
|
|
import static google.registry.testing.JUnitBackports.assertThrows;
|
|
import static google.registry.testing.LogsSubject.assertAboutLogs;
|
|
import static google.registry.ui.server.registrar.SessionUtils.AccessType.READ_ONLY;
|
|
import static google.registry.ui.server.registrar.SessionUtils.AccessType.READ_WRITE;
|
|
import static org.mockito.Mockito.mock;
|
|
|
|
import com.google.appengine.api.users.User;
|
|
import com.google.common.flogger.LoggerConfig;
|
|
import com.google.common.testing.NullPointerTester;
|
|
import com.google.common.testing.TestLogHandler;
|
|
import google.registry.request.HttpException.ForbiddenException;
|
|
import google.registry.request.auth.AuthLevel;
|
|
import google.registry.request.auth.AuthResult;
|
|
import google.registry.request.auth.UserAuthInfo;
|
|
import google.registry.testing.AppEngineRule;
|
|
import google.registry.testing.InjectRule;
|
|
import google.registry.ui.server.registrar.SessionUtils.AccessType;
|
|
import java.util.logging.Level;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
import org.junit.After;
|
|
import org.junit.Before;
|
|
import org.junit.Rule;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.junit.runners.JUnit4;
|
|
|
|
/** Unit tests for {@link SessionUtils}. */
|
|
@RunWith(JUnit4.class)
|
|
public class SessionUtilsTest {
|
|
|
|
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
|
|
@Rule public final InjectRule inject = new InjectRule();
|
|
|
|
private final HttpServletRequest req = mock(HttpServletRequest.class);
|
|
private final HttpServletResponse rsp = mock(HttpServletResponse.class);
|
|
private final TestLogHandler testLogHandler = new TestLogHandler();
|
|
|
|
private SessionUtils sessionUtils;
|
|
|
|
private static final AuthResult AUTHORIZED_USER = createAuthResult(true, false);
|
|
private static final AuthResult UNAUTHORIZED_USER = createAuthResult(false, false);
|
|
private static final AuthResult AUTHORIZED_ADMIN = createAuthResult(true, true);
|
|
private static final AuthResult UNAUTHORIZED_ADMIN = createAuthResult(false, true);
|
|
private static final AuthResult NO_USER = AuthResult.create(AuthLevel.NONE);
|
|
private static final String DEFAULT_CLIENT_ID = "TheRegistrar";
|
|
private static final String ADMIN_CLIENT_ID = "NewRegistrar";
|
|
|
|
private static AuthResult createAuthResult(boolean isAuthorized, boolean isAdmin) {
|
|
return AuthResult.create(
|
|
AuthLevel.USER,
|
|
UserAuthInfo.create(
|
|
new User(
|
|
String.format(
|
|
"%s_%s@gmail.com", isAuthorized ? "good" : "evil", isAdmin ? "admin" : "user"),
|
|
"gmail.com",
|
|
isAuthorized ? THE_REGISTRAR_GAE_USER_ID : "badGaeUserId"),
|
|
isAdmin));
|
|
}
|
|
|
|
@Before
|
|
public void before() {
|
|
LoggerConfig.getConfig(SessionUtils.class).addHandler(testLogHandler);
|
|
sessionUtils = new SessionUtils();
|
|
sessionUtils.registryAdminClientId = ADMIN_CLIENT_ID;
|
|
persistResource(loadRegistrar(ADMIN_CLIENT_ID));
|
|
}
|
|
|
|
@After
|
|
public void after() {
|
|
LoggerConfig.getConfig(SessionUtils.class).removeHandler(testLogHandler);
|
|
}
|
|
|
|
private String formatMessage(String message, AuthResult authResult, String clientId) {
|
|
return message
|
|
.replace("{user}", authResult.userIdForLogging())
|
|
.replace("{clientId}", String.valueOf(clientId));
|
|
}
|
|
|
|
/** Fail loading registrar if user doesn't have access to it. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readOnly_noAccess_isNotAdmin() {
|
|
expectGetRegistrarFailure(
|
|
DEFAULT_CLIENT_ID,
|
|
READ_ONLY,
|
|
UNAUTHORIZED_USER,
|
|
"User {user} doesn't have READ_ONLY access to registrar {clientId}");
|
|
}
|
|
|
|
/** Fail loading registrar if user doesn't have access to it. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readWrite_noAccess_isNotAdmin() {
|
|
expectGetRegistrarFailure(
|
|
DEFAULT_CLIENT_ID,
|
|
READ_WRITE,
|
|
UNAUTHORIZED_USER,
|
|
"User {user} doesn't have READ_WRITE access to registrar {clientId}");
|
|
}
|
|
|
|
/** Fail loading registrar if there's no user associated with the request. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readOnly_noUser() {
|
|
expectGetRegistrarFailure(DEFAULT_CLIENT_ID, READ_ONLY, NO_USER, "Not logged in");
|
|
}
|
|
|
|
/** Fail loading registrar if there's no user associated with the request. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readWrite_noUser() {
|
|
expectGetRegistrarFailure(DEFAULT_CLIENT_ID, READ_WRITE, NO_USER, "Not logged in");
|
|
|
|
assertAboutLogs().that(testLogHandler).hasNoLogsAtLevel(Level.INFO);
|
|
}
|
|
|
|
/** Succeed loading registrar in read-only mode if user has access to it. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readOnly_hasAccess_isNotAdmin() {
|
|
expectGetRegistrarSuccess(
|
|
AUTHORIZED_USER, READ_ONLY, "User {user} has access to registrar {clientId}");
|
|
}
|
|
|
|
/** Succeed loading registrar in read-write mode if user has access to it. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readWrite_hasAccess_isNotAdmin() {
|
|
expectGetRegistrarSuccess(
|
|
AUTHORIZED_USER, READ_WRITE, "User {user} has access to registrar {clientId}");
|
|
}
|
|
|
|
/** Succeed loading registrar in read-only mode if admin with access. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readOnly_hasAccess_isAdmin() {
|
|
expectGetRegistrarSuccess(
|
|
AUTHORIZED_ADMIN, READ_ONLY, "User {user} has access to registrar {clientId}");
|
|
}
|
|
|
|
/** Succeed loading registrar in read-write mode if admin with access. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readWrite_hasAccess_isAdmin() {
|
|
expectGetRegistrarSuccess(
|
|
AUTHORIZED_ADMIN, READ_WRITE, "User {user} has access to registrar {clientId}");
|
|
}
|
|
|
|
/** Succeed loading registrar for read-only when admin isn't on the approved contacts list. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readOnly_noAccess_isAdmin() {
|
|
expectGetRegistrarSuccess(
|
|
UNAUTHORIZED_ADMIN,
|
|
READ_ONLY,
|
|
"Allowing admin {user} read-only access to registrar {clientId}.");
|
|
}
|
|
|
|
/** Fail loading registrar for read-write when admin isn't on the approved contacts list. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readWrite_noAccess_isAdmin() {
|
|
expectGetRegistrarFailure(
|
|
DEFAULT_CLIENT_ID,
|
|
READ_WRITE,
|
|
UNAUTHORIZED_ADMIN,
|
|
"User {user} doesn't have READ_WRITE access to registrar {clientId}");
|
|
}
|
|
|
|
/** Fail loading registrar even if admin, if registrar doesn't exist. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readOnly_doesntExist_isAdmin() {
|
|
expectGetRegistrarFailure(
|
|
"BadClientId",
|
|
READ_ONLY,
|
|
AUTHORIZED_ADMIN,
|
|
"Registrar {clientId} not found");
|
|
}
|
|
|
|
/** Fail loading registrar even if admin, if registrar doesn't exist. */
|
|
@Test
|
|
public void testGetRegistrarForUser_readWrite_doesntExist_isAdmin() {
|
|
expectGetRegistrarFailure(
|
|
"BadClientId",
|
|
READ_WRITE,
|
|
AUTHORIZED_ADMIN,
|
|
"Registrar {clientId} not found");
|
|
}
|
|
|
|
private void expectGetRegistrarSuccess(
|
|
AuthResult authResult, AccessType accessType, String message) {
|
|
assertThat(sessionUtils.getRegistrarForUser(DEFAULT_CLIENT_ID, accessType, authResult))
|
|
.isNotNull();
|
|
assertAboutLogs()
|
|
.that(testLogHandler)
|
|
.hasLogAtLevelWithMessage(
|
|
Level.INFO, formatMessage(message, authResult, DEFAULT_CLIENT_ID));
|
|
}
|
|
|
|
private void expectGetRegistrarFailure(
|
|
String clientId, AccessType accessType, AuthResult authResult, String message) {
|
|
ForbiddenException exception =
|
|
assertThrows(
|
|
ForbiddenException.class,
|
|
() -> sessionUtils.getRegistrarForUser(clientId, accessType, authResult));
|
|
|
|
assertThat(exception).hasMessageThat().contains(formatMessage(message, authResult, clientId));
|
|
assertAboutLogs().that(testLogHandler).hasNoLogsAtLevel(Level.INFO);
|
|
}
|
|
|
|
/** If a user has access to a registrar, we should guess that registrar. */
|
|
@Test
|
|
public void testGuessClientIdForUser_hasAccess_isNotAdmin() {
|
|
expectGuessRegistrarSuccess(
|
|
AUTHORIZED_USER,
|
|
DEFAULT_CLIENT_ID,
|
|
"Associating user {user} with found registrar {clientId}.");
|
|
}
|
|
|
|
/** If a user doesn't have access to any registrars, guess returns nothing. */
|
|
@Test
|
|
public void testGuessClientIdForUser_noAccess_isNotAdmin() {
|
|
expectGuessRegistrarFailure(
|
|
UNAUTHORIZED_USER, "User {user} isn't associated with any registrar");
|
|
}
|
|
|
|
/**
|
|
* If an admin has access to a registrar, we should guess that registrar (rather than the
|
|
* ADMIN_CLIENT_ID).
|
|
*/
|
|
@Test
|
|
public void testGuessClientIdForUser_hasAccess_isAdmin() {
|
|
expectGuessRegistrarSuccess(
|
|
AUTHORIZED_ADMIN,
|
|
DEFAULT_CLIENT_ID,
|
|
"Associating user {user} with found registrar {clientId}.");
|
|
}
|
|
|
|
/** If an admin doesn't have access to a registrar, we should guess the ADMIN_CLIENT_ID. */
|
|
@Test
|
|
public void testGuessClientIdForUser_noAccess_isAdmin() {
|
|
expectGuessRegistrarSuccess(
|
|
UNAUTHORIZED_ADMIN,
|
|
ADMIN_CLIENT_ID,
|
|
"User {user} is an admin with no associated registrar."
|
|
+ " Automatically associating the user with configured client Id {clientId}.");
|
|
}
|
|
|
|
/**
|
|
* If an admin is not associated with a registrar and there is no configured adminClientId, we
|
|
* can't guess the clientId.
|
|
*/
|
|
@Test
|
|
public void testGuessClientIdForUser_noAccess_isAdmin_adminClientIdEmpty() {
|
|
sessionUtils.registryAdminClientId = "";
|
|
expectGuessRegistrarFailure(
|
|
UNAUTHORIZED_ADMIN, "User {user} isn't associated with any registrar");
|
|
assertAboutLogs()
|
|
.that(testLogHandler)
|
|
.hasLogAtLevelWithMessage(
|
|
Level.INFO,
|
|
formatMessage(
|
|
"Cannot associate admin user {user} with configured client Id."
|
|
+ " ClientId is null or empty.",
|
|
UNAUTHORIZED_ADMIN,
|
|
null));
|
|
}
|
|
|
|
/**
|
|
* If an admin is not associated with a registrar and the configured adminClientId points to a
|
|
* non-existent registrar, we still guess it (we will later fail loading the registrar).
|
|
*/
|
|
@Test
|
|
public void testGuessClientIdForUser_noAccess_isAdmin_adminClientIdInvalid() {
|
|
sessionUtils.registryAdminClientId = "NonexistentRegistrar";
|
|
expectGuessRegistrarSuccess(
|
|
UNAUTHORIZED_ADMIN,
|
|
"NonexistentRegistrar",
|
|
"User {user} is an admin with no associated registrar."
|
|
+ " Automatically associating the user with configured client Id {clientId}.");
|
|
}
|
|
|
|
private void expectGuessRegistrarSuccess(AuthResult authResult, String clientId, String message) {
|
|
assertThat(sessionUtils.guessClientIdForUser(authResult)).isEqualTo(clientId);
|
|
assertAboutLogs()
|
|
.that(testLogHandler)
|
|
.hasLogAtLevelWithMessage(Level.INFO, formatMessage(message, authResult, clientId));
|
|
}
|
|
|
|
private void expectGuessRegistrarFailure(AuthResult authResult, String message) {
|
|
ForbiddenException exception =
|
|
assertThrows(ForbiddenException.class, () -> sessionUtils.guessClientIdForUser(authResult));
|
|
assertThat(exception)
|
|
.hasMessageThat()
|
|
.contains(formatMessage(message, authResult, null));
|
|
}
|
|
|
|
@Test
|
|
public void testNullness() {
|
|
new NullPointerTester()
|
|
.setDefault(HttpServletRequest.class, req)
|
|
.setDefault(HttpServletResponse.class, rsp)
|
|
.testAllPublicStaticMethods(SessionUtils.class);
|
|
}
|
|
}
|