aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorValerij Fredriksen <valerijf@yahooinc.com>2022-08-12 13:26:26 +0200
committerValerij Fredriksen <valerijf@yahooinc.com>2022-08-12 13:35:32 +0200
commite8313cda153f5a4f85f12673b3d1da588940eb38 (patch)
tree27527b1eda37b9d8a5354539c79aa9721dc62ff1
parent4524b4678e7b5486ba6ff02685a73322893c0b5f (diff)
Create UserSessionManager
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserSessionManager.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManager.java50
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManagerTest.java64
3 files changed, 127 insertions, 0 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserSessionManager.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserSessionManager.java
new file mode 100644
index 00000000000..eae62c66b35
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserSessionManager.java
@@ -0,0 +1,13 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.user;
+
+import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
+
+/**
+ * @author freva
+ */
+public interface UserSessionManager {
+
+ /** Returns whether the existing session for the given SecurityContext should be expired */
+ boolean shouldExpireSessionFor(SecurityContext context);
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManager.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManager.java
new file mode 100644
index 00000000000..e2b5083abae
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManager.java
@@ -0,0 +1,50 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.security;
+
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.vespa.flags.LongFlag;
+import com.yahoo.vespa.flags.PermanentFlags;
+import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.TenantController;
+import com.yahoo.vespa.hosted.controller.api.integration.user.UserSessionManager;
+import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
+import com.yahoo.vespa.hosted.controller.api.role.TenantRole;
+import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
+
+import java.time.Instant;
+
+/**
+ * @author freva
+ */
+public class CloudUserSessionManager implements UserSessionManager {
+
+ private final TenantController tenantController;
+ private final LongFlag invalidateConsoleSessions;
+
+ public CloudUserSessionManager(Controller controller) {
+ this.tenantController = controller.tenants();
+ this.invalidateConsoleSessions = PermanentFlags.INVALIDATE_CONSOLE_SESSIONS.bindTo(controller.flagSource());
+ }
+
+ @Override
+ public boolean shouldExpireSessionFor(SecurityContext context) {
+ if (context.issuedAt().isBefore(Instant.ofEpochSecond(invalidateConsoleSessions.value())))
+ return true;
+
+ return context.roles().stream()
+ .filter(TenantRole.class::isInstance)
+ .map(TenantRole.class::cast)
+ .map(TenantRole::tenant)
+ .distinct()
+ .anyMatch(tenantName -> shouldExpireSessionFor(tenantName, context.issuedAt()));
+ }
+
+ private boolean shouldExpireSessionFor(TenantName tenantName, Instant contextIssuedAt) {
+ return tenantController.get(tenantName)
+ .filter(CloudTenant.class::isInstance)
+ .map(CloudTenant.class::cast)
+ .flatMap(CloudTenant::invalidateUserSessionsBefore)
+ .map(contextIssuedAt::isBefore)
+ .orElse(false);
+ }
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManagerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManagerTest.java
new file mode 100644
index 00000000000..710e75fb235
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/security/CloudUserSessionManagerTest.java
@@ -0,0 +1,64 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.security;
+
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.vespa.flags.InMemoryFlagSource;
+import com.yahoo.vespa.flags.PermanentFlags;
+import com.yahoo.vespa.hosted.controller.ControllerTester;
+import com.yahoo.vespa.hosted.controller.LockedTenant;
+import com.yahoo.vespa.hosted.controller.api.role.Role;
+import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
+import com.yahoo.vespa.hosted.controller.api.role.SimplePrincipal;
+import com.yahoo.vespa.hosted.controller.api.role.TenantRole;
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author freva
+ */
+class CloudUserSessionManagerTest {
+
+ private final ControllerTester tester = new ControllerTester(SystemName.Public);
+ private final CloudUserSessionManager userSessionManager = new CloudUserSessionManager(tester.controller());
+
+ @Test
+ void test() {
+ createTenant("tenant1", null);
+ createTenant("tenant2", 1234);
+ createTenant("tenant3", 1543);
+ createTenant("tenant4", 2313);
+
+ assertShouldExpire(false, 123);
+ assertShouldExpire(false, 123, "tenant1");
+ assertShouldExpire(true, 123, "tenant2");
+ assertShouldExpire(false, 2123, "tenant2");
+ assertShouldExpire(true, 123, "tenant1", "tenant2");
+
+ ((InMemoryFlagSource) tester.controller().flagSource()).withLongFlag(PermanentFlags.INVALIDATE_CONSOLE_SESSIONS.id(), 150);
+ assertShouldExpire(true, 123);
+ assertShouldExpire(true, 123, "tenant1");
+ }
+
+ private void assertShouldExpire(boolean expected, long issuedAtSeconds, String... tenantNames) {
+ Set<Role> roles = Stream.of(tenantNames).map(name -> TenantRole.developer(TenantName.from(name))).collect(Collectors.toSet());
+ SecurityContext context = new SecurityContext(new SimplePrincipal("dev"), roles, Instant.ofEpochSecond(issuedAtSeconds));
+ assertEquals(expected, userSessionManager.shouldExpireSessionFor(context));
+ }
+
+ private void createTenant(String tenantName, Integer invalidateAfterSeconds) {
+ tester.createTenant(tenantName);
+ Optional.ofNullable(invalidateAfterSeconds)
+ .map(Instant::ofEpochSecond)
+ .ifPresent(instant ->
+ tester.controller().tenants().lockOrThrow(TenantName.from(tenantName), LockedTenant.Cloud.class, tenant ->
+ tester.controller().tenants().store(tenant.withInvalidateUserSessionsBefore(instant))));
+ }
+}