diff options
author | Valerij Fredriksen <valerijf@yahooinc.com> | 2022-08-12 13:26:26 +0200 |
---|---|---|
committer | Valerij Fredriksen <valerijf@yahooinc.com> | 2022-08-12 13:35:32 +0200 |
commit | e8313cda153f5a4f85f12673b3d1da588940eb38 (patch) | |
tree | 27527b1eda37b9d8a5354539c79aa9721dc62ff1 | |
parent | 4524b4678e7b5486ba6ff02685a73322893c0b5f (diff) |
Create UserSessionManager
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)))); + } +} |