use of com.yahoo.vespa.hosted.controller.api.identifiers.UserId in project vespa by vespa-engine.
the class ApplicationApiTest method testAuthorization.
@Test
public void testAuthorization() throws Exception {
ContainerTester tester = new ContainerTester(container, responseFiles);
UserId authorizedUser = USER_ID;
UserId unauthorizedUser = new UserId("othertenant");
// Mutation without an user is disallowed
tester.assertResponse(request("/application/v4/tenant/tenant1", POST).data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), "{\n \"message\" : \"Not authenticated\"\n}", 401);
// ... but read methods are allowed for authenticated user
tester.assertResponse(request("/application/v4/tenant/", GET).userIdentity(USER_ID).data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), "[]", 200);
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
// Creating a tenant for an Athens domain the user is not admin for is disallowed
tester.assertResponse(request("/application/v4/tenant/tenant1", POST).data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}").userIdentity(unauthorizedUser), "{\"error-code\":\"FORBIDDEN\",\"message\":\"The user 'user.othertenant' is not admin in Athenz domain 'domain1'\"}", 403);
// (Create it with the right tenant id)
tester.assertResponse(request("/application/v4/tenant/tenant1", POST).data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}").userIdentity(authorizedUser).nToken(N_TOKEN), new File("tenant-without-applications.json"), 200);
// Creating an application for an Athens domain the user is not admin for is disallowed
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", POST).userIdentity(unauthorizedUser).nToken(N_TOKEN), "{\n \"message\" : \"Tenant admin or Vespa operator role required\"\n}", 403);
// (Create it with the right tenant id)
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", POST).userIdentity(authorizedUser).nToken(N_TOKEN), new File("application-reference.json"), 200);
// Deploy to an authorized zone by a user tenant is disallowed
HttpEntity entity = createApplicationDeployData(applicationPackage, Optional.empty());
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/default/deploy", POST).data(entity).userIdentity(USER_ID), "{\n \"message\" : \"'user.myuser' is not a Screwdriver identity. Only Screwdriver is allowed to deploy to this environment.\"\n}", 403);
// Deleting an application for an Athens domain the user is not admin for is disallowed
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE).userIdentity(unauthorizedUser), "{\n \"message\" : \"Tenant admin or Vespa operator role required\"\n}", 403);
// (Deleting it with the right tenant id)
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE).userIdentity(authorizedUser).nToken(N_TOKEN), "", 200);
// Updating a tenant for an Athens domain the user is not admin for is disallowed
tester.assertResponse(request("/application/v4/tenant/tenant1", PUT).data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}").userIdentity(unauthorizedUser), "{\n \"message\" : \"Tenant admin or Vespa operator role required\"\n}", 403);
// Change Athens domain
createAthenzDomainWithAdmin(new AthenzDomain("domain2"), USER_ID);
tester.assertResponse(request("/application/v4/tenant/tenant1", PUT).data("{\"athensDomain\":\"domain2\", \"property\":\"property1\"}").userIdentity(authorizedUser).nToken(N_TOKEN), "{\"tenant\":\"tenant1\",\"type\":\"ATHENS\",\"athensDomain\":\"domain2\",\"property\":\"property1\",\"applications\":[]}", 200);
// Deleting a tenant for an Athens domain the user is not admin for is disallowed
tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).userIdentity(unauthorizedUser), "{\n \"message\" : \"Tenant admin or Vespa operator role required\"\n}", 403);
}
use of com.yahoo.vespa.hosted.controller.api.identifiers.UserId in project vespa by vespa-engine.
the class ApplicationApiHandler method authenticatedUser.
private HttpResponse authenticatedUser(HttpRequest request) {
String userIdString = request.getProperty("userOverride");
if (userIdString == null)
userIdString = getUserId(request).map(UserId::id).orElseThrow(() -> new ForbiddenException("You must be authenticated or specify userOverride"));
UserId userId = new UserId(userIdString);
List<Tenant> tenants = controller.tenants().asList(userId);
Slime slime = new Slime();
Cursor response = slime.setObject();
response.setString("user", userId.id());
Cursor tenantsArray = response.setArray("tenants");
for (Tenant tenant : tenants) tenantInTenantsListToSlime(tenant, request.getUri(), tenantsArray.addObject());
response.setBool("tenantExists", tenants.stream().map(Tenant::getId).anyMatch(id -> id.isTenantFor(userId)));
return new SlimeJsonResponse(slime);
}
use of com.yahoo.vespa.hosted.controller.api.identifiers.UserId in project vespa by vespa-engine.
the class UserAuthWithAthenzPrincipalFilter method rewriteUserPrincipalToAthenz.
private void rewriteUserPrincipalToAthenz(DiscFilterRequest request) {
Principal userPrincipal = request.getUserPrincipal();
log.log(LogLevel.DEBUG, () -> "Original user principal: " + userPrincipal.toString());
UserId userId = new UserId(userPrincipal.getName());
AthenzUser athenzIdentity = AthenzUser.fromUserId(userId.id());
request.setRemoteUser(athenzIdentity.getFullName());
NToken nToken = Optional.ofNullable(request.getHeader(principalHeaderName)).map(NToken::new).orElse(null);
request.setUserPrincipal(new AthenzPrincipal(athenzIdentity, nToken));
}
use of com.yahoo.vespa.hosted.controller.api.identifiers.UserId in project vespa by vespa-engine.
the class TenantController method createUserTenant.
public Tenant createUserTenant(String userName) {
TenantId userTenantId = new UserId(userName).toTenantId();
try (Lock lock = lock(userTenantId)) {
Tenant tenant = Tenant.createUserTenant(userTenantId);
internalCreateTenant(tenant, Optional.empty());
return tenant;
}
}
use of com.yahoo.vespa.hosted.controller.api.identifiers.UserId in project vespa by vespa-engine.
the class ApplicationApiTest method testApplicationApi.
@Test
public void testApplicationApi() throws Exception {
ContainerControllerTester controllerTester = new ContainerControllerTester(container, responseFiles);
ContainerTester tester = controllerTester.containerTester();
tester.updateSystemVersion();
// (Necessary but not provided in this API)
createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
// GET API root
tester.assertResponse(request("/application/v4/", GET).userIdentity(USER_ID), new File("root.json"));
// GET athens domains
tester.assertResponse(request("/application/v4/athensDomain/", GET).userIdentity(USER_ID), new File("athensDomain-list.json"));
// GET OpsDB properties
tester.assertResponse(request("/application/v4/property/", GET).userIdentity(USER_ID), new File("property-list.json"));
// GET cookie freshness
tester.assertResponse(request("/application/v4/cookiefreshness/", GET).userIdentity(USER_ID), new File("cookiefreshness.json"));
// POST (add) a tenant without property ID
tester.assertResponse(request("/application/v4/tenant/tenant1", POST).userIdentity(USER_ID).data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}").nToken(N_TOKEN), new File("tenant-without-applications.json"));
// PUT (modify) a tenant
tester.assertResponse(request("/application/v4/tenant/tenant1", PUT).userIdentity(USER_ID).nToken(N_TOKEN).data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), new File("tenant-without-applications.json"));
// GET the authenticated user (with associated tenants)
tester.assertResponse(request("/application/v4/user", GET).userIdentity(USER_ID), new File("user.json"));
// GET all tenants
tester.assertResponse(request("/application/v4/tenant/", GET).userIdentity(USER_ID), new File("tenant-list.json"));
// Add another Athens domain, so we can try to create more tenants
// New domain to test tenant w/property ID
createAthenzDomainWithAdmin(new AthenzDomain("domain2"), USER_ID);
// Add property info for that property id, as well, in the mock organization.
addPropertyData((MockOrganization) controllerTester.controller().organization(), "1234");
// POST (add) a tenant with property ID
tester.assertResponse(request("/application/v4/tenant/tenant2", POST).userIdentity(USER_ID).nToken(N_TOKEN).data("{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}"), new File("tenant-without-applications-with-id.json"));
// PUT (modify) a tenant with property ID
tester.assertResponse(request("/application/v4/tenant/tenant2", PUT).userIdentity(USER_ID).nToken(N_TOKEN).data("{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}"), new File("tenant-without-applications-with-id.json"));
// GET a tenant with property ID
tester.assertResponse(request("/application/v4/tenant/tenant2", GET).userIdentity(USER_ID), new File("tenant-without-applications-with-id.json"));
// POST (create) an application
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", POST).userIdentity(USER_ID).nToken(N_TOKEN), new File("application-reference.json"));
// GET a tenant
tester.assertResponse(request("/application/v4/tenant/tenant1", GET).userIdentity(USER_ID), new File("tenant-with-application.json"));
// GET tenant applications
tester.assertResponse(request("/application/v4/tenant/tenant1/application/", GET).userIdentity(USER_ID), new File("application-list.json"));
addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR));
// POST (deploy) an application to a zone - manual user deployment
HttpEntity entity = createApplicationDeployData(applicationPackage, Optional.empty());
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/dev/region/us-west-1/instance/default/deploy", POST).data(entity).userIdentity(USER_ID), new File("deploy-result.json"));
// POST (deploy) an application to a zone. This simulates calls done by our tenant pipeline.
ApplicationId id = ApplicationId.from("tenant1", "application1", "default");
long screwdriverProjectId = 123;
addScrewdriverUserToDeployRole(SCREWDRIVER_ID, ATHENZ_TENANT_DOMAIN, // (Necessary but not provided in this API)
new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(id.application().value()));
// Trigger deployment from completion of component job
controllerTester.jobCompletion(DeploymentJobs.JobType.component).application(id).projectId(screwdriverProjectId).uploadArtifact(applicationPackage).submit();
// ... systemtest
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/test/region/us-east-1/instance/default/", POST).data(createApplicationDeployData(applicationPackage, Optional.of(screwdriverProjectId))).screwdriverIdentity(SCREWDRIVER_ID), new File("deploy-result.json"));
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/test/region/us-east-1/instance/default", DELETE).screwdriverIdentity(SCREWDRIVER_ID), "Deactivated tenant/tenant1/application/application1/environment/test/region/us-east-1/instance/default");
// Called through the separate screwdriver/v1 API
controllerTester.jobCompletion(DeploymentJobs.JobType.systemTest).application(id).projectId(screwdriverProjectId).submit();
// ... staging
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/staging/region/us-east-3/instance/default/", POST).data(createApplicationDeployData(applicationPackage, Optional.of(screwdriverProjectId))).screwdriverIdentity(SCREWDRIVER_ID), new File("deploy-result.json"));
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/staging/region/us-east-3/instance/default", DELETE).screwdriverIdentity(SCREWDRIVER_ID), "Deactivated tenant/tenant1/application/application1/environment/staging/region/us-east-3/instance/default");
controllerTester.jobCompletion(DeploymentJobs.JobType.stagingTest).application(id).projectId(screwdriverProjectId).submit();
// ... prod zone
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default/", POST).data(createApplicationDeployData(applicationPackage, Optional.of(screwdriverProjectId))).screwdriverIdentity(SCREWDRIVER_ID), new File("deploy-result.json"));
controllerTester.jobCompletion(DeploymentJobs.JobType.productionCorpUsEast1).application(id).projectId(screwdriverProjectId).unsuccessful().submit();
// GET tenant screwdriver projects
tester.assertResponse(request("/application/v4/tenant-pipeline/", GET).userIdentity(USER_ID), new File("tenant-pipelines.json"));
setDeploymentMaintainedInfo(controllerTester);
// GET tenant application deployments
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", GET).userIdentity(USER_ID), new File("application.json"));
// GET an application deployment
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default", GET).userIdentity(USER_ID), new File("deployment.json"));
addIssues(controllerTester, ApplicationId.from("tenant1", "application1", "default"));
// GET at root, with "&recursive=deployment", returns info about all tenants, their applications and their deployments
tester.assertResponse(request("/application/v4/", GET).userIdentity(USER_ID).recursive("deployment"), new File("recursive-root.json"));
// GET at root, with "&recursive=tenant", returns info about all tenants, with limited info about their applications.
tester.assertResponse(request("/application/v4/", GET).userIdentity(USER_ID).recursive("tenant"), new File("recursive-until-tenant-root.json"));
// GET at a tenant, with "&recursive=true", returns full info about their applications and their deployments
tester.assertResponse(request("/application/v4/tenant/tenant1/", GET).userIdentity(USER_ID).recursive("true"), new File("tenant1-recursive.json"));
// GET at an application, with "&recursive=true", returns full info about its deployments
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/", GET).userIdentity(USER_ID).recursive("true"), new File("application1-recursive.json"));
// DELETE (cancel) ongoing change
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/deploying", DELETE).userIdentity(HOSTED_VESPA_OPERATOR), new File("application-deployment-cancelled.json"));
// DELETE (cancel) again is a no-op
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/deploying", DELETE).userIdentity(HOSTED_VESPA_OPERATOR), new File("application-deployment-cancelled-no-op.json"));
// POST triggering of a full deployment to an application (if version is omitted, current system version is used)
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/deploying", POST).userIdentity(HOSTED_VESPA_OPERATOR).data("6.1.0"), new File("application-deployment.json"));
// POST a 'restart application' command
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default/restart", POST).screwdriverIdentity(SCREWDRIVER_ID), "Requested restart of tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default");
// POST a 'restart application' command with a host filter (other filters not supported yet)
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default/restart?hostname=host1", POST).screwdriverIdentity(SCREWDRIVER_ID), "Requested restart of tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default");
// POST a 'log' command
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default/log", POST).screwdriverIdentity(SCREWDRIVER_ID), // Proxied to config server, not sure about the expected return format
new File("log-response.json"));
// GET (wait for) convergence
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default/converge", GET).userIdentity(USER_ID), new File("convergence.json"));
// GET services
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default/service", GET).userIdentity(USER_ID), new File("services.json"));
// GET service
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default/service/storagenode-awe3slno6mmq2fye191y324jl/state/v1/", GET).userIdentity(USER_ID), new File("service.json"));
// DELETE application with active deployments fails
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE).userIdentity(USER_ID), new File("delete-with-active-deployments.json"), 400);
// DELETE (deactivate) a deployment - dev
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/dev/region/us-west-1/instance/default", DELETE).userIdentity(USER_ID), "Deactivated tenant/tenant1/application/application1/environment/dev/region/us-west-1/instance/default");
// DELETE (deactivate) a deployment - prod
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default", DELETE).screwdriverIdentity(SCREWDRIVER_ID), "Deactivated tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default");
// DELETE (deactivate) a deployment is idempotent
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default", DELETE).screwdriverIdentity(SCREWDRIVER_ID), "Deactivated tenant/tenant1/application/application1/environment/prod/region/corp-us-east-1/instance/default");
// PUT (create) the authenticated user
byte[] data = new byte[0];
tester.assertResponse(request("/application/v4/user?user=newuser&domain=by", PUT).data(data).userIdentity(new UserId("newuser")), new File("create-user-response.json"));
// OPTIONS return 200 OK
tester.assertResponse(request("/application/v4/", Request.Method.OPTIONS), "");
// GET global rotation status
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/default/global-rotation", GET).userIdentity(USER_ID), new File("global-rotation.json"));
// GET global rotation override status
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/default/global-rotation/override", GET).userIdentity(USER_ID), new File("global-rotation-get.json"));
// SET global rotation override status
tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/environment/prod/region/us-west-1/instance/default/global-rotation/override", PUT).userIdentity(USER_ID).data("{\"reason\":\"because i can\"}"), new File("global-rotation-put.json"));
// DELETE global rotation override status
tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/environment/prod/region/us-west-1/instance/default/global-rotation/override", DELETE).userIdentity(USER_ID).data("{\"reason\":\"because i can\"}"), new File("global-rotation-delete.json"));
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/promote", POST).screwdriverIdentity(SCREWDRIVER_ID), "{\"message\":\"Successfully copied environment hosted-verified-prod to hosted-instance_tenant1_application1_placeholder_component_default\"}");
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/default/promote", POST).screwdriverIdentity(SCREWDRIVER_ID), "{\"message\":\"Successfully copied environment hosted-instance_tenant1_application1_placeholder_component_default to hosted-instance_tenant1_application1_us-west-1_prod_default\"}");
// DELETE an application
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE).userIdentity(USER_ID).nToken(N_TOKEN), "");
// DELETE a tenant
tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).userIdentity(USER_ID).nToken(N_TOKEN), new File("tenant-without-applications.json"));
controllerTester.controller().deconstruct();
}
Aggregations