From 8efd7efe46890108ad9c6beda6050c480d107a28 Mon Sep 17 00:00:00 2001 From: Gowtam Lal Date: Mon, 16 Oct 2017 17:32:26 -0400 Subject: [PATCH 01/79] Make the leniency of OPTIMISTIC tunable. --- .../singularity/config/SingularityConfiguration.java | 10 ++++++++++ .../mesos/SingularitySlaveAndRackManager.java | 8 +++++--- .../singularity/scheduler/SingularityLeaderCache.java | 6 ++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java index 1bd201a686..70c08dca99 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java @@ -114,6 +114,8 @@ public class SingularityConfiguration extends Configuration { @NotNull private SlavePlacement defaultSlavePlacement = SlavePlacement.GREEDY; + private double placementLeniency = 0.0d; + private boolean defaultValueForKillTasksOfPausedRequests = true; private int defaultDeployStepWaitTimeMs = 0; @@ -528,6 +530,10 @@ public SlavePlacement getDefaultSlavePlacement() { return defaultSlavePlacement; } + public double getPlacementLeniency() { + return placementLeniency; + } + public int getDefaultDeployStepWaitTimeMs() { return defaultDeployStepWaitTimeMs; } @@ -958,6 +964,10 @@ public void setDefaultSlavePlacement(SlavePlacement defaultSlavePlacement) { this.defaultSlavePlacement = defaultSlavePlacement; } + public void setPlacementLeniency(double placementLeniency) { + this.placementLeniency = placementLeniency; + } + public void setDefaultValueForKillTasksOfPausedRequests(boolean defaultValueForKillTasksOfPausedRequests) { this.defaultValueForKillTasksOfPausedRequests = defaultValueForKillTasksOfPausedRequests; } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java index 797002b89f..34198a6ae4 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java @@ -11,8 +11,8 @@ import javax.inject.Singleton; -import org.apache.mesos.v1.Protos.Offer; import org.apache.mesos.v1.Protos.AgentID; +import org.apache.mesos.v1.Protos.Offer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +30,7 @@ import com.hubspot.singularity.SingularityMachineAbstraction; import com.hubspot.singularity.SingularityPendingRequest.PendingType; import com.hubspot.singularity.SingularityPendingTask; +import com.hubspot.singularity.SingularityPendingTaskId; import com.hubspot.singularity.SingularityRack; import com.hubspot.singularity.SingularityRequest; import com.hubspot.singularity.SingularitySlave; @@ -217,14 +218,15 @@ public SlaveMatchState doesOfferMatch(SingularityOfferHolder offerHolder, Singul // If no tasks are active for this request yet, we can fall back to greedy. if (currentlyActiveTasksForRequestClusterwide.size() > 0) { + Collection pendingTasksForRequestClusterwide = leaderCache.getPendingTaskIdsForRequest(taskRequest.getRequest().getId()); Set currentHostsForRequest = currentlyActiveTasksForRequestClusterwide.stream() .map(SingularityTaskId::getSanitizedHost) .collect(Collectors.toSet()); final double numPerSlave = currentlyActiveTasksForRequestClusterwide.size() / (double) currentHostsForRequest.size(); - - final boolean isSlaveOk = numOnSlave <= numPerSlave; + final double leniencyCoefficient = configuration.getPlacementLeniency(); + final boolean isSlaveOk = numOnSlave <= (numPerSlave * (1 + (pendingTasksForRequestClusterwide.size() * leniencyCoefficient))); if (!isSlaveOk) { LOG.trace( diff --git a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityLeaderCache.java b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityLeaderCache.java index 7cd17ef1aa..c186596529 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityLeaderCache.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/scheduler/SingularityLeaderCache.java @@ -120,6 +120,12 @@ public List getPendingTaskIds() { return new ArrayList<>(pendingTaskIdToPendingTask.keySet()); } + public List getPendingTaskIdsForRequest(String requestId) { + return pendingTaskIdToPendingTask.keySet().stream() + .filter(t -> t.getRequestId().equals(requestId)) + .collect(Collectors.toList()); + } + public void deletePendingTask(SingularityPendingTaskId pendingTaskId) { if (!active) { LOG.warn("deletePendingTask {}, but not active", pendingTaskId); From 329b8da3e11e077576fe0666bb6fc3897f912540 Mon Sep 17 00:00:00 2001 From: Gowtam Lal Date: Tue, 17 Oct 2017 14:26:53 -0400 Subject: [PATCH 02/79] Improve logging. --- .../singularity/mesos/SingularitySlaveAndRackManager.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java index 34198a6ae4..9179b66ba2 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/mesos/SingularitySlaveAndRackManager.java @@ -226,12 +226,13 @@ public SlaveMatchState doesOfferMatch(SingularityOfferHolder offerHolder, Singul final double numPerSlave = currentlyActiveTasksForRequestClusterwide.size() / (double) currentHostsForRequest.size(); final double leniencyCoefficient = configuration.getPlacementLeniency(); - final boolean isSlaveOk = numOnSlave <= (numPerSlave * (1 + (pendingTasksForRequestClusterwide.size() * leniencyCoefficient))); + final double threshold = numPerSlave * (1 + (pendingTasksForRequestClusterwide.size() * leniencyCoefficient)); + final boolean isSlaveOk = numOnSlave <= threshold; if (!isSlaveOk) { LOG.trace( - "Rejecting OPTIMISTIC task {} from slave {} ({}) due to numOnSlave {} and numPerSlave {} (based on currentlyActiveTasksForRequest {} and currentHostsForRequest {})", - taskRequest.getRequest().getId(), slaveId, host, numOnSlave, numPerSlave, currentlyActiveTasksForRequestClusterwide.size(), currentHostsForRequest.size() + "Rejecting OPTIMISTIC task {} from slave {} ({}) because numOnSlave {} violates threshold {} (based on active tasks for request {}, current hosts for request {}, pending tasks for request {})", + taskRequest.getRequest().getId(), slaveId, host, numOnSlave, threshold, currentlyActiveTasksForRequestClusterwide.size(), currentHostsForRequest.size(), pendingTasksForRequestClusterwide.size() ); return SlaveMatchState.SLAVE_SATURATED; } From ec4b804bf833da358cf2c9f24947142105b78e70 Mon Sep 17 00:00:00 2001 From: Gowtam Lal Date: Tue, 17 Oct 2017 14:27:05 -0400 Subject: [PATCH 03/79] Set default leniency based on experimentation. --- .../hubspot/singularity/config/SingularityConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java index 70c08dca99..c5bd9068b8 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java @@ -114,7 +114,7 @@ public class SingularityConfiguration extends Configuration { @NotNull private SlavePlacement defaultSlavePlacement = SlavePlacement.GREEDY; - private double placementLeniency = 0.0d; + private double placementLeniency = 0.09d; private boolean defaultValueForKillTasksOfPausedRequests = true; From 61f7e602dae6311cc027ddce5c2637d790723c9c Mon Sep 17 00:00:00 2001 From: Gowtam Lal Date: Tue, 17 Oct 2017 14:28:01 -0400 Subject: [PATCH 04/79] Fix test. --- .../SingularitySlavePlacementTest.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java index e409afdd41..87d511ccbf 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java @@ -104,32 +104,33 @@ public void testSlavePlacementOptimistic() { initRequest(); initFirstDeploy(); - saveAndSchedule(request.toBuilder().setInstances(Optional.of(7)).setSlavePlacement(Optional.of(SlavePlacement.OPTIMISTIC))); + saveAndSchedule(request.toBuilder().setInstances(Optional.of(20)).setSlavePlacement(Optional.of(SlavePlacement.OPTIMISTIC))); // Default behavior if we don't have info about other hosts that can run this task: be greedy. sms.resourceOffers(Arrays.asList(createOffer(2, 128 * 2, "slave1", "host1"))); Assert.assertEquals(2, taskManager.getActiveTaskIds().size()); - // Now that at least one other host is running tasks for this request, we expect an even spread. + // Now that at least one other host is running tasks for this request, we expect an even-ish spread, + // but because we have many tasks pending, we allow quite a bit of unevenness. sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2"))); - Assert.assertEquals(5, taskManager.getActiveTaskIds().size()); + Assert.assertEquals(13, taskManager.getActiveTaskIds().size()); - // ...and we don't allow a violation of this even spread by refusing to schedule more tasks on host2 (because it's hosting 3/5 tasks). + // ...but now we won't schedule more tasks on host2, because it's hosting a disproportionate number of tasks. sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave2", "host2"))); - Assert.assertEquals(5, taskManager.getActiveTaskIds().size()); + Assert.assertEquals(13, taskManager.getActiveTaskIds().size()); - // ...but since host1 is only hosting 2/5 tasks, we will schedule more tasks on it when an offer is received. + // ...but since host1 is only hosting 2 tasks, we will schedule more tasks on it when an offer is received. sms.resourceOffers(Arrays.asList(createOffer(20, 20000, "slave1", "host1"))); - Assert.assertEquals(7, taskManager.getActiveTaskIds().size()); + Assert.assertEquals(20, taskManager.getActiveTaskIds().size()); Map> tasksByHost = taskManager.getActiveTaskIdsForRequest(request.getId()).stream() .collect(Collectors.groupingBy(SingularityTaskId::getSanitizedHost)); Assert.assertNotNull(tasksByHost.get("host1")); - Assert.assertEquals(4, tasksByHost.get("host1").size()); + Assert.assertEquals(9, tasksByHost.get("host1").size()); Assert.assertNotNull(tasksByHost.get("host2")); - Assert.assertEquals(3, tasksByHost.get("host2").size()); + Assert.assertEquals(11, tasksByHost.get("host2").size()); } From f794f2be5337500d2b4be4fc9db795b50341a4ba Mon Sep 17 00:00:00 2001 From: Gowtam Lal Date: Tue, 17 Oct 2017 14:31:04 -0400 Subject: [PATCH 05/79] Enforce a minimum for the placement leninency coefficient. --- .../com/hubspot/singularity/config/SingularityConfiguration.java | 1 + 1 file changed, 1 insertion(+) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java index c5bd9068b8..276624ce12 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java @@ -114,6 +114,7 @@ public class SingularityConfiguration extends Configuration { @NotNull private SlavePlacement defaultSlavePlacement = SlavePlacement.GREEDY; + @Min(value = 0, message = "Must be non-negative") private double placementLeniency = 0.09d; private boolean defaultValueForKillTasksOfPausedRequests = true; From 27fae78b66c6424bcea8c000b4772e5a84443413 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Thu, 19 Oct 2017 11:41:53 -0400 Subject: [PATCH 06/79] Catch errors in FetchRequestArgHistory --- SingularityUI/app/actions/api/history.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SingularityUI/app/actions/api/history.es6 b/SingularityUI/app/actions/api/history.es6 index 75bfb5283f..1a3c8e65fd 100644 --- a/SingularityUI/app/actions/api/history.es6 +++ b/SingularityUI/app/actions/api/history.es6 @@ -101,7 +101,7 @@ export const FetchRequestArgHistory = buildApiAction( 'FETCH_REQUEST_ARG_HISTORY', (requestId) => ({ url: `/history/request/${requestId}/command-line-args`, - catchStatusCodes: [400, 404] + catchStatusCodes: [400, 404, 500] }), (requestId) => requestId ); From 85d1ce994437cf5122e4ba32d676c4e1516ff7c6 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 20 Oct 2017 10:38:38 -0400 Subject: [PATCH 07/79] progress on external jwt auth --- .../SingularityExternalJWTAuthenticator.java | 75 +++++++++++++++++++ .../singularity/config/AuthConfiguration.java | 26 +++++++ 2 files changed, 101 insertions(+) create mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java new file mode 100644 index 0000000000..e1890b869c --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java @@ -0,0 +1,75 @@ +package com.hubspot.singularity.auth.authenticator; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +import com.google.common.base.Optional; +import com.google.common.base.Strings; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.hubspot.singularity.SingularityAsyncHttpClient; +import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; +import com.hubspot.singularity.config.AuthConfiguration; +import com.hubspot.singularity.config.SingularityConfiguration; + +@Singleton +public class SingularityExternalJWTAuthenticator implements SingularityAuthenticator { + private final SingularityAsyncHttpClient asyncHttpClient; + private final AuthConfiguration authConfiguration; + private final Provider requestProvider; + private final Cache permissionsCache; + + @Inject + public SingularityExternalJWTAuthenticator(SingularityAsyncHttpClient asyncHttpClient, SingularityConfiguration configuration, Provider requestProvider) { + this.asyncHttpClient = asyncHttpClient; + this.authConfiguration = configuration.getAuthConfiguration(); + this.requestProvider = requestProvider; + this.permissionsCache = CacheBuilder.newBuilder() + .expireAfterWrite("") + .build(new CacheLoader() { + @Override + public UserPermissions load(String key) throws Exception { + return null; + } + }) + } + + @Override + public Optional get() { + String jwt = extractJWT(requestProvider.get()); + + UserPermissions permissions = verifyToken(jwt); + + return Optional.of(new SingularityUser("", "", "", "")); + } + + private String extractJWT(HttpServletRequest request) { + String cookieValue = null; + for (Cookie cookie : request.getCookies()) { + if (cookie.getName().equals(authConfiguration.getJwtAuthCookieName())) { + cookieValue = cookie.getValue(); + break; + } + } + + if (Strings.isNullOrEmpty(cookieValue)) { + throw WebExceptions.unauthorized(String.format("No %s cookie present, please log in first", authConfiguration.getJwtAuthCookieName())); + } else { + return cookieValue; + } + } + + private UserPermissions verifyToken(String jwt) { + // http request to verify, returns user permissions/email/id + } + + + private class UserPermissions { + // mimic janus Permissions class + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java index ed33fd3e00..45d7ec0fa8 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java @@ -45,6 +45,14 @@ public class AuthConfiguration { @NotNull private String requestUserHeaderName = "X-Username"; // used by SingularityHeaderPassthroughAuthenticator + @JsonProperty + @NotNull + private String jwtAuthVerificationUrl = ""; // used by SingularityExternalJWTAuthenticator + + @JsonProperty + @NotNull + private String jwtAuthCookieName = ""; // used by SingularityExternalJWTAuthenticator + public boolean isEnabled() { return enabled; } @@ -116,4 +124,22 @@ public String getRequestUserHeaderName() { public void setRequestUserHeaderName(String requestUserHeaderName) { this.requestUserHeaderName = requestUserHeaderName; } + + public String getJwtAuthVerificationUrl() { + return jwtAuthVerificationUrl; + } + + public AuthConfiguration setJwtAuthVerificationUrl(String jwtAuthVerificationUrl) { + this.jwtAuthVerificationUrl = jwtAuthVerificationUrl; + return this; + } + + public String getJwtAuthCookieName() { + return jwtAuthCookieName; + } + + public AuthConfiguration setJwtAuthCookieName(String jwtAuthCookieName) { + this.jwtAuthCookieName = jwtAuthCookieName; + return this; + } } From 1b99c243960177951dee364badba46c5a13d0ae4 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 20 Oct 2017 11:38:48 -0400 Subject: [PATCH 08/79] first draft of jwt auth --- .../auth/SingularityAuthenticatorClass.java | 4 +- .../SingularityExternalJWTAuthenticator.java | 109 ++++++++++++++---- .../singularity/config/AuthConfiguration.java | 26 ----- .../config/SingularityConfiguration.java | 13 +++ 4 files changed, 105 insertions(+), 47 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java index 69a84e631a..2a440b1f70 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java @@ -2,13 +2,15 @@ import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; import com.hubspot.singularity.auth.authenticator.SingularityDisabledAuthenticator; +import com.hubspot.singularity.auth.authenticator.SingularityExternalJWTAuthenticator; import com.hubspot.singularity.auth.authenticator.SingularityHeaderPassthroughAuthenticator; import com.hubspot.singularity.auth.authenticator.SingularityQueryParamAuthenticator; public enum SingularityAuthenticatorClass { DISABLED(SingularityDisabledAuthenticator.class), HEADER_PASSTHROUGH(SingularityHeaderPassthroughAuthenticator.class), - QUERYPARAM_PASSTHROUGH(SingularityQueryParamAuthenticator.class); + QUERYPARAM_PASSTHROUGH(SingularityQueryParamAuthenticator.class), + JWT(SingularityExternalJWTAuthenticator.class); private final Class authenticatorClass; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java index e1890b869c..78b418b162 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java @@ -1,42 +1,57 @@ package com.hubspot.singularity.auth.authenticator; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; -import com.hubspot.singularity.SingularityAsyncHttpClient; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.WebExceptions; -import com.hubspot.singularity.config.AuthConfiguration; +import com.hubspot.singularity.config.JWTConfiguration; import com.hubspot.singularity.config.SingularityConfiguration; +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.Response; @Singleton public class SingularityExternalJWTAuthenticator implements SingularityAuthenticator { - private final SingularityAsyncHttpClient asyncHttpClient; - private final AuthConfiguration authConfiguration; + private static final Logger LOG = LoggerFactory.getLogger(SingularityExternalJWTAuthenticator.class); + + private final AsyncHttpClient asyncHttpClient; + private final JWTConfiguration jwtConfiguration; private final Provider requestProvider; + private final ObjectMapper objectMapper; private final Cache permissionsCache; @Inject - public SingularityExternalJWTAuthenticator(SingularityAsyncHttpClient asyncHttpClient, SingularityConfiguration configuration, Provider requestProvider) { + public SingularityExternalJWTAuthenticator(AsyncHttpClient asyncHttpClient, + SingularityConfiguration configuration, + Provider requestProvider, + ObjectMapper objectMapper) { this.asyncHttpClient = asyncHttpClient; - this.authConfiguration = configuration.getAuthConfiguration(); + this.jwtConfiguration = configuration.getJwtConfiguration(); this.requestProvider = requestProvider; + this.objectMapper = objectMapper; this.permissionsCache = CacheBuilder.newBuilder() - .expireAfterWrite("") - .build(new CacheLoader() { - @Override - public UserPermissions load(String key) throws Exception { - return null; - } - }) + .expireAfterWrite(jwtConfiguration.getCacheValidationMs(), TimeUnit.MILLISECONDS) + .build(); } @Override @@ -45,31 +60,85 @@ public Optional get() { UserPermissions permissions = verifyToken(jwt); - return Optional.of(new SingularityUser("", "", "", "")); + Set groups = new HashSet<>(); + groups.addAll(permissions.getGroups()); + groups.addAll(permissions.getScopes()); + return Optional.of(new SingularityUser(permissions.getUid(), Optional.of(permissions.getUid()), Optional.of(permissions.getUid()), groups)); } private String extractJWT(HttpServletRequest request) { String cookieValue = null; for (Cookie cookie : request.getCookies()) { - if (cookie.getName().equals(authConfiguration.getJwtAuthCookieName())) { + if (cookie.getName().equals(jwtConfiguration.getAuthCookieName())) { cookieValue = cookie.getValue(); break; } } if (Strings.isNullOrEmpty(cookieValue)) { - throw WebExceptions.unauthorized(String.format("No %s cookie present, please log in first", authConfiguration.getJwtAuthCookieName())); + throw WebExceptions.unauthorized(String.format("No %s cookie present, please log in first", jwtConfiguration.getAuthCookieName())); } else { return cookieValue; } } private UserPermissions verifyToken(String jwt) { - // http request to verify, returns user permissions/email/id + UserPermissions maybeCachedPermissions = permissionsCache.getIfPresent(jwt); + if (maybeCachedPermissions != null) { + return maybeCachedPermissions; + } else { + try { + Response response = asyncHttpClient.prepareGet(jwtConfiguration.getAuthVerificationUrl()) + .addHeader("Authorization", String.format("Bearer: %s", jwt)) + .execute() + .get(); + if (response.getStatusCode() > 299) { + throw WebExceptions.unauthorized(String.format("Got status code %d when verifying jwt", response.getStatusCode())); + } else { + String responseBody = response.getResponseBody(); + UserPermissions permissions = objectMapper.readValue(responseBody, UserPermissions.class); + permissionsCache.put(jwt, permissions); + return permissions; + } + } catch (IOException|ExecutionException|InterruptedException e) { + LOG.error("Exception while verifying jwt", e); + throw WebExceptions.unauthorized(String.format("Exception while verifying jwt: %s", e.getMessage())); + } + } } - + @JsonIgnoreProperties(ignoreUnknown = true) private class UserPermissions { - // mimic janus Permissions class + private final String uid; + private final Set groups; + private final Set scopes; + + @JsonCreator + public UserPermissions(@JsonProperty("uid") String uid, @JsonProperty("groups") Set groups, @JsonProperty("scopes") Set scopes) { + this.uid = uid; + this.groups = groups; + this.scopes = scopes; + } + + public String getUid() { + return uid; + } + + public Set getGroups() { + return groups; + } + + public Set getScopes() { + return scopes; + } + + @Override + public String toString() { + return "UserPermissions{" + + "uid='" + uid + '\'' + + ", groups=" + groups + + ", scopes=" + scopes + + '}'; + } } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java index 45d7ec0fa8..ed33fd3e00 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java @@ -45,14 +45,6 @@ public class AuthConfiguration { @NotNull private String requestUserHeaderName = "X-Username"; // used by SingularityHeaderPassthroughAuthenticator - @JsonProperty - @NotNull - private String jwtAuthVerificationUrl = ""; // used by SingularityExternalJWTAuthenticator - - @JsonProperty - @NotNull - private String jwtAuthCookieName = ""; // used by SingularityExternalJWTAuthenticator - public boolean isEnabled() { return enabled; } @@ -124,22 +116,4 @@ public String getRequestUserHeaderName() { public void setRequestUserHeaderName(String requestUserHeaderName) { this.requestUserHeaderName = requestUserHeaderName; } - - public String getJwtAuthVerificationUrl() { - return jwtAuthVerificationUrl; - } - - public AuthConfiguration setJwtAuthVerificationUrl(String jwtAuthVerificationUrl) { - this.jwtAuthVerificationUrl = jwtAuthVerificationUrl; - return this; - } - - public String getJwtAuthCookieName() { - return jwtAuthCookieName; - } - - public AuthConfiguration setJwtAuthCookieName(String jwtAuthCookieName) { - this.jwtAuthCookieName = jwtAuthCookieName; - return this; - } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java index 1bd201a686..9110d937f7 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java @@ -306,6 +306,10 @@ public class SingularityConfiguration extends Configuration { @Valid private LDAPConfiguration ldapConfiguration; + @JsonProperty("jwt") + @Valid + private JWTConfiguration jwtConfiguration; + @JsonProperty("auth") @NotNull @Valid @@ -1256,6 +1260,15 @@ public Optional getLdapConfigurationOptional() { return Optional.fromNullable(ldapConfiguration); } + public JWTConfiguration getJwtConfiguration() { + return jwtConfiguration; + } + + public SingularityConfiguration setJwtConfiguration(JWTConfiguration jwtConfiguration) { + this.jwtConfiguration = jwtConfiguration; + return this; + } + public void setLdapConfiguration(LDAPConfiguration ldapConfiguration) { this.ldapConfiguration = ldapConfiguration; } From 0ffdee5edaabccbffa3fb7c598858866b97ed2cb Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 20 Oct 2017 11:39:55 -0400 Subject: [PATCH 09/79] config for auth --- .../singularity/config/JWTConfiguration.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 SingularityService/src/main/java/com/hubspot/singularity/config/JWTConfiguration.java diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/JWTConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/JWTConfiguration.java new file mode 100644 index 0000000000..09a6eb6512 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/JWTConfiguration.java @@ -0,0 +1,47 @@ +package com.hubspot.singularity.config; + +import javax.annotation.Nonnegative; +import javax.validation.constraints.NotNull; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class JWTConfiguration { + @JsonProperty + @NotNull + private String authVerificationUrl = ""; + + @JsonProperty + @NotNull + private String authCookieName = ""; + + @JsonProperty + @Nonnegative + private long cacheValidationMs = 60000; + + public String getAuthVerificationUrl() { + return authVerificationUrl; + } + + public JWTConfiguration setAuthVerificationUrl(String authVerificationUrl) { + this.authVerificationUrl = authVerificationUrl; + return this; + } + + public String getAuthCookieName() { + return authCookieName; + } + + public JWTConfiguration setAuthCookieName(String authCookieName) { + this.authCookieName = authCookieName; + return this; + } + + public long getCacheValidationMs() { + return cacheValidationMs; + } + + public JWTConfiguration setCacheValidationMs(long cacheValidationMs) { + this.cacheValidationMs = cacheValidationMs; + return this; + } +} From 7b9143905bd2995fb657ffa6be01945fe8af9a2e Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 20 Oct 2017 16:28:03 -0400 Subject: [PATCH 10/79] backend uses auth header, add default emial domain if response doesn't have one --- .../auth/SingularityAuthenticatorClass.java | 4 +- ...a => SingularityWebhookAuthenticator.java} | 60 +++++++++---------- .../config/SingularityConfiguration.java | 10 ++-- ...ion.java => WebhookAuthConfiguration.java} | 28 ++++----- .../singularity/config/UIConfiguration.java | 25 ++++++++ .../hubspot/singularity/views/IndexView.java | 16 +++++ 6 files changed, 92 insertions(+), 51 deletions(-) rename SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/{SingularityExternalJWTAuthenticator.java => SingularityWebhookAuthenticator.java} (65%) rename SingularityService/src/main/java/com/hubspot/singularity/config/{JWTConfiguration.java => WebhookAuthConfiguration.java} (59%) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java index 2a440b1f70..1edf311861 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java @@ -2,7 +2,7 @@ import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; import com.hubspot.singularity.auth.authenticator.SingularityDisabledAuthenticator; -import com.hubspot.singularity.auth.authenticator.SingularityExternalJWTAuthenticator; +import com.hubspot.singularity.auth.authenticator.SingularityWebhookAuthenticator; import com.hubspot.singularity.auth.authenticator.SingularityHeaderPassthroughAuthenticator; import com.hubspot.singularity.auth.authenticator.SingularityQueryParamAuthenticator; @@ -10,7 +10,7 @@ public enum SingularityAuthenticatorClass { DISABLED(SingularityDisabledAuthenticator.class), HEADER_PASSTHROUGH(SingularityHeaderPassthroughAuthenticator.class), QUERYPARAM_PASSTHROUGH(SingularityQueryParamAuthenticator.class), - JWT(SingularityExternalJWTAuthenticator.class); + JWT(SingularityWebhookAuthenticator.class); private final Class authenticatorClass; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java similarity index 65% rename from SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java rename to SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java index 78b418b162..7e4560a86a 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityExternalJWTAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -6,7 +6,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; @@ -25,71 +24,72 @@ import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.WebExceptions; -import com.hubspot.singularity.config.JWTConfiguration; import com.hubspot.singularity.config.SingularityConfiguration; +import com.hubspot.singularity.config.WebhookAuthConfiguration; import com.ning.http.client.AsyncHttpClient; import com.ning.http.client.Response; @Singleton -public class SingularityExternalJWTAuthenticator implements SingularityAuthenticator { - private static final Logger LOG = LoggerFactory.getLogger(SingularityExternalJWTAuthenticator.class); +public class SingularityWebhookAuthenticator implements SingularityAuthenticator { + private static final Logger LOG = LoggerFactory.getLogger(SingularityWebhookAuthenticator.class); private final AsyncHttpClient asyncHttpClient; - private final JWTConfiguration jwtConfiguration; + private final WebhookAuthConfiguration webhookAuthConfiguration; private final Provider requestProvider; private final ObjectMapper objectMapper; private final Cache permissionsCache; @Inject - public SingularityExternalJWTAuthenticator(AsyncHttpClient asyncHttpClient, - SingularityConfiguration configuration, - Provider requestProvider, - ObjectMapper objectMapper) { + public SingularityWebhookAuthenticator(AsyncHttpClient asyncHttpClient, + SingularityConfiguration configuration, + Provider requestProvider, + ObjectMapper objectMapper) { this.asyncHttpClient = asyncHttpClient; - this.jwtConfiguration = configuration.getJwtConfiguration(); + this.webhookAuthConfiguration = configuration.getWebhookAuthConfiguration(); this.requestProvider = requestProvider; this.objectMapper = objectMapper; this.permissionsCache = CacheBuilder.newBuilder() - .expireAfterWrite(jwtConfiguration.getCacheValidationMs(), TimeUnit.MILLISECONDS) + .expireAfterWrite(webhookAuthConfiguration.getCacheValidationMs(), TimeUnit.MILLISECONDS) .build(); } @Override public Optional get() { - String jwt = extractJWT(requestProvider.get()); + String authHeaderValue = extractAuthHeader(requestProvider.get()); - UserPermissions permissions = verifyToken(jwt); + UserPermissions permissions = verify(authHeaderValue); Set groups = new HashSet<>(); groups.addAll(permissions.getGroups()); groups.addAll(permissions.getScopes()); - return Optional.of(new SingularityUser(permissions.getUid(), Optional.of(permissions.getUid()), Optional.of(permissions.getUid()), groups)); + + String email = permissions.getUid().contains("@") ? permissions.getUid() : String.format("%s@%s", permissions.getUid(), webhookAuthConfiguration.getDefaultEmailDomain()); + + return Optional.of(new SingularityUser( + permissions.getUid(), + Optional.of(permissions.getUid()), + Optional.of(email), + groups)); } - private String extractJWT(HttpServletRequest request) { - String cookieValue = null; - for (Cookie cookie : request.getCookies()) { - if (cookie.getName().equals(jwtConfiguration.getAuthCookieName())) { - cookieValue = cookie.getValue(); - break; - } - } + private String extractAuthHeader(HttpServletRequest request) { + String authHeaderValue = request.getHeader("Authorization"); - if (Strings.isNullOrEmpty(cookieValue)) { - throw WebExceptions.unauthorized(String.format("No %s cookie present, please log in first", jwtConfiguration.getAuthCookieName())); + if (Strings.isNullOrEmpty(authHeaderValue)) { + throw WebExceptions.unauthorized("No Authorization header present, please log in first"); } else { - return cookieValue; + return authHeaderValue; } } - private UserPermissions verifyToken(String jwt) { - UserPermissions maybeCachedPermissions = permissionsCache.getIfPresent(jwt); + private UserPermissions verify(String authHeaderValue) { + UserPermissions maybeCachedPermissions = permissionsCache.getIfPresent(authHeaderValue); if (maybeCachedPermissions != null) { return maybeCachedPermissions; } else { try { - Response response = asyncHttpClient.prepareGet(jwtConfiguration.getAuthVerificationUrl()) - .addHeader("Authorization", String.format("Bearer: %s", jwt)) + Response response = asyncHttpClient.prepareGet(webhookAuthConfiguration.getAuthVerificationUrl()) + .addHeader("Authorization", authHeaderValue) .execute() .get(); if (response.getStatusCode() > 299) { @@ -97,7 +97,7 @@ private UserPermissions verifyToken(String jwt) { } else { String responseBody = response.getResponseBody(); UserPermissions permissions = objectMapper.readValue(responseBody, UserPermissions.class); - permissionsCache.put(jwt, permissions); + permissionsCache.put(authHeaderValue, permissions); return permissions; } } catch (IOException|ExecutionException|InterruptedException e) { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java index 9110d937f7..871121d2ee 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java @@ -308,7 +308,7 @@ public class SingularityConfiguration extends Configuration { @JsonProperty("jwt") @Valid - private JWTConfiguration jwtConfiguration; + private WebhookAuthConfiguration webhookAuthConfiguration; @JsonProperty("auth") @NotNull @@ -1260,12 +1260,12 @@ public Optional getLdapConfigurationOptional() { return Optional.fromNullable(ldapConfiguration); } - public JWTConfiguration getJwtConfiguration() { - return jwtConfiguration; + public WebhookAuthConfiguration getWebhookAuthConfiguration() { + return webhookAuthConfiguration; } - public SingularityConfiguration setJwtConfiguration(JWTConfiguration jwtConfiguration) { - this.jwtConfiguration = jwtConfiguration; + public SingularityConfiguration setWebhookAuthConfiguration(WebhookAuthConfiguration webhookAuthConfiguration) { + this.webhookAuthConfiguration = webhookAuthConfiguration; return this; } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/JWTConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java similarity index 59% rename from SingularityService/src/main/java/com/hubspot/singularity/config/JWTConfiguration.java rename to SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java index 09a6eb6512..937696795e 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/JWTConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java @@ -5,43 +5,43 @@ import com.fasterxml.jackson.annotation.JsonProperty; -public class JWTConfiguration { +public class WebhookAuthConfiguration { @JsonProperty @NotNull private String authVerificationUrl = ""; - @JsonProperty - @NotNull - private String authCookieName = ""; - @JsonProperty @Nonnegative private long cacheValidationMs = 60000; + @JsonProperty + @NotNull + private String defaultEmailDomain = ""; + public String getAuthVerificationUrl() { return authVerificationUrl; } - public JWTConfiguration setAuthVerificationUrl(String authVerificationUrl) { + public WebhookAuthConfiguration setAuthVerificationUrl(String authVerificationUrl) { this.authVerificationUrl = authVerificationUrl; return this; } - public String getAuthCookieName() { - return authCookieName; + public long getCacheValidationMs() { + return cacheValidationMs; } - public JWTConfiguration setAuthCookieName(String authCookieName) { - this.authCookieName = authCookieName; + public WebhookAuthConfiguration setCacheValidationMs(long cacheValidationMs) { + this.cacheValidationMs = cacheValidationMs; return this; } - public long getCacheValidationMs() { - return cacheValidationMs; + public String getDefaultEmailDomain() { + return defaultEmailDomain; } - public JWTConfiguration setCacheValidationMs(long cacheValidationMs) { - this.cacheValidationMs = cacheValidationMs; + public WebhookAuthConfiguration setDefaultEmailDomain(String defaultEmailDomain) { + this.defaultEmailDomain = defaultEmailDomain; return this; } } diff --git a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java index c136e2bc41..206044b566 100644 --- a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java +++ b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java @@ -90,6 +90,13 @@ public static RootUrlMode parse(String value) { @JsonProperty private Optional extraScript = Optional.absent(); + @JsonProperty + private boolean generateAuthHeader = false; + + @JsonProperty + @NotNull + private String authCookieName = ""; + public boolean isHideNewDeployButton() { return hideNewDeployButton; @@ -226,4 +233,22 @@ public Optional getExtraScript() { public void setExtraScript(Optional extraScript) { this.extraScript = extraScript; } + + public boolean isGenerateAuthHeader() { + return generateAuthHeader; + } + + public UIConfiguration setGenerateAuthHeader(boolean generateAuthHeader) { + this.generateAuthHeader = generateAuthHeader; + return this; + } + + public String getAuthCookieName() { + return authCookieName; + } + + public UIConfiguration setAuthCookieName(String authCookieName) { + this.authCookieName = authCookieName; + return this; + } } diff --git a/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java b/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java index 9a8e562db3..5ec6dc4c97 100644 --- a/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java +++ b/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java @@ -61,6 +61,9 @@ public class IndexView extends View { private final String extraScript; + private final boolean generateAuthHeader; + private final String authCookieName; + public IndexView(String singularityUriBase, String appRoot, IndexViewConfiguration configuration, ObjectMapper mapper) { super("index.mustache"); @@ -122,6 +125,9 @@ public IndexView(String singularityUriBase, String appRoot, IndexViewConfigurati this.timestampWithSecondsFormat = uiConfiguration.getTimestampWithSecondsFormat(); this.extraScript = uiConfiguration.getExtraScript().orNull(); + + this.generateAuthHeader = uiConfiguration.isGenerateAuthHeader(); + this.authCookieName = uiConfiguration.getAuthCookieName(); } public String getAppRoot() { @@ -244,6 +250,14 @@ public boolean isShortenSlaveUsageHostname() { return shortenSlaveUsageHostname; } + public boolean isGenerateAuthHeader() { + return generateAuthHeader; + } + + public String getAuthCookieName() { + return authCookieName; + } + @Override public String toString() { return "IndexView{" + @@ -277,6 +291,8 @@ public String toString() { ", timestampWithSecondsFormat='" + timestampWithSecondsFormat + '\'' + ", redirectOnUnauthorizedUrl='" + redirectOnUnauthorizedUrl + '\'' + ", extraScript='" + extraScript + '\'' + + ", generateAuthHeader=" + generateAuthHeader + + ", authCookieName='" + authCookieName + '\'' + "} " + super.toString(); } } From 5a98e84ef0fa058b80a6b5d590cd3d74c01ed8a5 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 20 Oct 2017 16:47:17 -0400 Subject: [PATCH 11/79] allow multiple auth methods --- .../singularity/SingularityAuthModule.java | 11 ++++- .../auth/SingularityAuthenticatorClass.java | 2 +- .../SingularityUserProvider.java | 44 +++++++++++++++++++ .../singularity/config/AuthConfiguration.java | 16 +++++++ 4 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java index fc626197a3..e8fbd5c617 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java @@ -5,8 +5,11 @@ import com.google.inject.Module; import com.google.inject.Scopes; import com.google.inject.TypeLiteral; +import com.google.inject.multibindings.Multibinder; +import com.hubspot.singularity.auth.SingularityAuthenticatorClass; import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; +import com.hubspot.singularity.auth.authenticator.SingularityUserProvider; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; import com.hubspot.singularity.config.SingularityConfiguration; @@ -19,9 +22,13 @@ public SingularityAuthModule(SingularityConfiguration configuration) { @Override public void configure(Binder binder) { - binder.bind(SingularityAuthenticator.class).to(configuration.getAuthConfiguration().getAuthenticator().getAuthenticatorClass()); + Multibinder multibinder = Multibinder.newSetBinder(binder, SingularityAuthenticator.class); + for (SingularityAuthenticatorClass clazz : configuration.getAuthConfiguration().getAuthenticators()) { + multibinder.addBinding().to(clazz.getAuthenticatorClass()); + } + binder.bind(SingularityAuthDatastore.class).to(configuration.getAuthConfiguration().getDatastore().getAuthDatastoreClass()); - binder.bind(new TypeLiteral>() {}).toProvider(SingularityAuthenticator.class); + binder.bind(new TypeLiteral>() {}).toProvider(SingularityUserProvider.class); binder.bind(SingularityAuthorizationHelper.class).in(Scopes.SINGLETON); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java index 1edf311861..7137fb7735 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthenticatorClass.java @@ -10,7 +10,7 @@ public enum SingularityAuthenticatorClass { DISABLED(SingularityDisabledAuthenticator.class), HEADER_PASSTHROUGH(SingularityHeaderPassthroughAuthenticator.class), QUERYPARAM_PASSTHROUGH(SingularityQueryParamAuthenticator.class), - JWT(SingularityWebhookAuthenticator.class); + WEBHOOK(SingularityWebhookAuthenticator.class); private final Class authenticatorClass; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java new file mode 100644 index 0000000000..5a80627240 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java @@ -0,0 +1,44 @@ +package com.hubspot.singularity.auth.authenticator; + +import java.util.Set; + +import com.google.common.base.Optional; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; +import com.hubspot.singularity.config.SingularityConfiguration; + +@Singleton +public class SingularityUserProvider implements Provider> { + private Set authenticators; + private boolean authEnabled; + + @Inject + public SingularityUserProvider(Set authenticators, SingularityConfiguration configuration) { + this.authenticators = authenticators; + this.authEnabled = configuration.getAuthConfiguration().isEnabled(); + } + + @Override + public Optional get() { + Exception maybeException = null; + for (SingularityAuthenticator authenticator : authenticators) { + try { + Optional maybeUser = authenticator.get(); + if (maybeUser.isPresent()) { + return maybeUser; + } + } catch (Exception e) { + maybeException = e; + } + } + if (authEnabled) { + throw WebExceptions.unauthorized( + maybeException != null ? maybeException.getMessage() : String.format("Not authorized using authenticators: %s", authenticators)); + } else { + return Optional.absent(); + } + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java index ed33fd3e00..608a088d9c 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java @@ -6,6 +6,7 @@ import javax.validation.constraints.NotNull; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.Sets; import com.hubspot.singularity.auth.SingularityAuthDatastoreClass; import com.hubspot.singularity.auth.SingularityAuthenticatorClass; @@ -15,8 +16,13 @@ public class AuthConfiguration { @JsonProperty @NotNull + @Deprecated private SingularityAuthenticatorClass authenticator = SingularityAuthenticatorClass.QUERYPARAM_PASSTHROUGH; + @JsonProperty + @NotNull + private Set authenticators = Sets.newHashSet(SingularityAuthenticatorClass.QUERYPARAM_PASSTHROUGH); + @JsonProperty @NotNull private SingularityAuthDatastoreClass datastore = SingularityAuthDatastoreClass.DUMMY; @@ -53,12 +59,22 @@ public void setEnabled(boolean enabled) { this.enabled = enabled; } + @Deprecated public SingularityAuthenticatorClass getAuthenticator() { return authenticator; } public void setAuthenticator(SingularityAuthenticatorClass authenticator) { this.authenticator = authenticator; + this.authenticators.add(authenticator); + } + + public Set getAuthenticators() { + return authenticators; + } + + public void setAuthenticators(Set authenticators) { + this.authenticators = authenticators; } public SingularityAuthDatastoreClass getDatastore() { From 1d7610c0a09831522b58dc0a2f141db83806797d Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Tue, 24 Oct 2017 16:15:31 -0400 Subject: [PATCH 12/79] grab the cookie and create a header in the ui --- SingularityUI/app/actions/api/base.es6 | 23 +++++++++++++++++++++++ SingularityUI/app/assets/index.mustache | 4 +++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/SingularityUI/app/actions/api/base.es6 b/SingularityUI/app/actions/api/base.es6 index b80338a75f..317205f974 100644 --- a/SingularityUI/app/actions/api/base.es6 +++ b/SingularityUI/app/actions/api/base.es6 @@ -58,6 +58,23 @@ export function buildApiAction(actionName, opts = {}, keyFunc = undefined) { }; } + function getCookie(key) { + if (!key) { + return null; + } + const encodedKey = encodeURIComponent(key).replace(/[\-\.\+\*]/g, '\\$&'); + return decodeURIComponent(document.cookie.replace(new RegExp(`(?:(?:^|.*;)\\s*${encodedKey}\\s*\\=\\s*([^;]*).*$)|^.*$`), '$1')) || null; + } + + function getAuthHeader(headerName) { + const authCookie = getCookie(headerName); + if (!authCookie) { + return ''; + } + const authToken = JSON.parse(authCookie).token; + return `Bearer ${ authToken }`; + } + function trigger(...args) { return (dispatch) => { let key; @@ -76,6 +93,12 @@ export function buildApiAction(actionName, opts = {}, keyFunc = undefined) { userParam = `?user=${localStorage.getItem('singularityUserId')}` } } + + if (config.generateAuthHeader) { + options.headers = options.headers || {}; + options.header.Authorization = getAuthHeader(config.authCookieName) + } + return fetch(config.apiRoot + options.url + userParam, _.extend({credentials: 'include'}, _.omit(options, 'url'))) .then(response => { apiResponse = response; diff --git a/SingularityUI/app/assets/index.mustache b/SingularityUI/app/assets/index.mustache index 364b080e4f..00d52f1e2b 100644 --- a/SingularityUI/app/assets/index.mustache +++ b/SingularityUI/app/assets/index.mustache @@ -47,7 +47,9 @@ shortenSlaveUsageHostname : {{{shortenSlaveUsageHostname}}}, redirectOnUnauthorizedUrl: "{{{redirectOnUnauthorizedUrl}}}", globalRefreshInterval: 60000, - sentryDsn: "{{{sentryDsn}}}" + sentryDsn: "{{{sentryDsn}}}", + generateAuthHeader: {{{generateAuthHeader}}}, + authCookieName: "{{{ authCookieName }}}" }; From 389552bccde47596610f5f9e840871b2ccab6319 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Tue, 24 Oct 2017 16:36:05 -0400 Subject: [PATCH 13/79] fix getter/setter --- .../singularity/config/WebhookAuthConfiguration.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java index 937696795e..bed7aebaa8 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java @@ -22,26 +22,23 @@ public String getAuthVerificationUrl() { return authVerificationUrl; } - public WebhookAuthConfiguration setAuthVerificationUrl(String authVerificationUrl) { + public void setAuthVerificationUrl(String authVerificationUrl) { this.authVerificationUrl = authVerificationUrl; - return this; } public long getCacheValidationMs() { return cacheValidationMs; } - public WebhookAuthConfiguration setCacheValidationMs(long cacheValidationMs) { + public void setCacheValidationMs(long cacheValidationMs) { this.cacheValidationMs = cacheValidationMs; - return this; } public String getDefaultEmailDomain() { return defaultEmailDomain; } - public WebhookAuthConfiguration setDefaultEmailDomain(String defaultEmailDomain) { + public void setDefaultEmailDomain(String defaultEmailDomain) { this.defaultEmailDomain = defaultEmailDomain; - return this; } } From 64f80e67bdd30ef079ddf758766d431817f9e4fe Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Tue, 24 Oct 2017 16:57:05 -0400 Subject: [PATCH 14/79] onemore setter fix --- .../hubspot/singularity/config/SingularityConfiguration.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java index 871121d2ee..6a65e9099f 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java @@ -1264,9 +1264,8 @@ public WebhookAuthConfiguration getWebhookAuthConfiguration() { return webhookAuthConfiguration; } - public SingularityConfiguration setWebhookAuthConfiguration(WebhookAuthConfiguration webhookAuthConfiguration) { + public void setWebhookAuthConfiguration(WebhookAuthConfiguration webhookAuthConfiguration) { this.webhookAuthConfiguration = webhookAuthConfiguration; - return this; } public void setLdapConfiguration(LDAPConfiguration ldapConfiguration) { From 2e2f45e6e83f7af78883fa5c81def60023e8d863 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Tue, 24 Oct 2017 17:06:27 -0400 Subject: [PATCH 15/79] change conig field name --- .../hubspot/singularity/config/SingularityConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java index 6a65e9099f..8940718580 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java @@ -306,9 +306,9 @@ public class SingularityConfiguration extends Configuration { @Valid private LDAPConfiguration ldapConfiguration; - @JsonProperty("jwt") + @JsonProperty("webhookAuth") @Valid - private WebhookAuthConfiguration webhookAuthConfiguration; + private WebhookAuthConfiguration webhookAuthConfiguration = new WebhookAuthConfiguration(); @JsonProperty("auth") @NotNull From 337cd3ea8ced5be8a27d5c67f6c85f74b42a1aeb Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Tue, 24 Oct 2017 17:25:06 -0400 Subject: [PATCH 16/79] fixes in gulpfile --- SingularityUI/app/actions/api/base.es6 | 2 +- SingularityUI/gulpfile.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/SingularityUI/app/actions/api/base.es6 b/SingularityUI/app/actions/api/base.es6 index 317205f974..2bdb28fabb 100644 --- a/SingularityUI/app/actions/api/base.es6 +++ b/SingularityUI/app/actions/api/base.es6 @@ -96,7 +96,7 @@ export function buildApiAction(actionName, opts = {}, keyFunc = undefined) { if (config.generateAuthHeader) { options.headers = options.headers || {}; - options.header.Authorization = getAuthHeader(config.authCookieName) + options.headers.Authorization = getAuthHeader(config.authCookieName) } return fetch(config.apiRoot + options.url + userParam, _.extend({credentials: 'include'}, _.omit(options, 'url'))) diff --git a/SingularityUI/gulpfile.js b/SingularityUI/gulpfile.js index 2d320475cd..e475e001e3 100644 --- a/SingularityUI/gulpfile.js +++ b/SingularityUI/gulpfile.js @@ -44,7 +44,9 @@ var templateData = { timestampWithSecondsFormat: process.env.SINGULARITY_TIMESTAMP_WITH_SECONDS_FORMAT || 'lll:ss', redirectOnUnauthorizedUrl: process.env.SINGULARITY_REDIRECT_ON_UNAUTHORIZED_URL || '', extraScript: process.env.SINGULARITY_EXTRA_SCRIPT || '', - sentryDsn: process.env.SINGULARITY_SENTRY_DSN || '' + sentryDsn: process.env.SINGULARITY_SENTRY_DSN || '', + generateAuthHeader: process.env.SINGULARITY_GENERATE_AUTH_HEADER || 'true', + authCookieName: process.env.SINGULARITY_AUTH_COOKIE_NAME || 'hs_tools_auth' }; var dest = path.resolve(__dirname, 'dist'); From 40fec5028b51aedf259234e8df86bdafe4c2c92d Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Wed, 25 Oct 2017 09:12:52 -0400 Subject: [PATCH 17/79] catch nulls defaults --- .../auth/authenticator/SingularityWebhookAuthenticator.java | 5 +++-- SingularityUI/gulpfile.js | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java index 7e4560a86a..8967e879a3 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -1,6 +1,7 @@ package com.hubspot.singularity.auth.authenticator; import java.io.IOException; +import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -116,8 +117,8 @@ private class UserPermissions { @JsonCreator public UserPermissions(@JsonProperty("uid") String uid, @JsonProperty("groups") Set groups, @JsonProperty("scopes") Set scopes) { this.uid = uid; - this.groups = groups; - this.scopes = scopes; + this.groups = groups != null ? groups : Collections.emptySet(); + this.scopes = scopes != null ? scopes : Collections.emptySet(); } public String getUid() { diff --git a/SingularityUI/gulpfile.js b/SingularityUI/gulpfile.js index e475e001e3..65f0602571 100644 --- a/SingularityUI/gulpfile.js +++ b/SingularityUI/gulpfile.js @@ -45,8 +45,8 @@ var templateData = { redirectOnUnauthorizedUrl: process.env.SINGULARITY_REDIRECT_ON_UNAUTHORIZED_URL || '', extraScript: process.env.SINGULARITY_EXTRA_SCRIPT || '', sentryDsn: process.env.SINGULARITY_SENTRY_DSN || '', - generateAuthHeader: process.env.SINGULARITY_GENERATE_AUTH_HEADER || 'true', - authCookieName: process.env.SINGULARITY_AUTH_COOKIE_NAME || 'hs_tools_auth' + generateAuthHeader: process.env.SINGULARITY_GENERATE_AUTH_HEADER || 'false', + authCookieName: process.env.SINGULARITY_AUTH_COOKIE_NAME || '' }; var dest = path.resolve(__dirname, 'dist'); From 4ade51b255484cd5475d77ace486b25dc3ec205d Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Wed, 25 Oct 2017 09:49:41 -0400 Subject: [PATCH 18/79] trace logging --- .../SingularityHeaderPassthroughAuthenticator.java | 7 +++++++ .../auth/authenticator/SingularityUserProvider.java | 12 +++++++++--- .../SingularityWebhookAuthenticator.java | 1 + .../singularity/config/AuthConfiguration.java | 9 +++++---- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java index 6eebb9fe48..4e5a746d1b 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java @@ -2,6 +2,9 @@ import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.inject.Inject; @@ -14,6 +17,8 @@ @Singleton public class SingularityHeaderPassthroughAuthenticator implements SingularityAuthenticator { + private static final Logger LOG = LoggerFactory.getLogger(SingularityHeaderPassthroughAuthenticator.class); + private final SingularityAuthDatastore datastore; private final String requestUserHeaderName; private final Provider requestProvider; @@ -37,6 +42,8 @@ private Optional getUserId() { public Optional get() { final Optional maybeUsername = getUserId(); + LOG.trace("Fetched user {} from header", maybeUsername); + if (!maybeUsername.isPresent()) { return Optional.absent(); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java index 5a80627240..facb625ddd 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java @@ -1,6 +1,9 @@ package com.hubspot.singularity.auth.authenticator; -import java.util.Set; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.inject.Inject; @@ -12,11 +15,13 @@ @Singleton public class SingularityUserProvider implements Provider> { - private Set authenticators; + private static final Logger LOG = LoggerFactory.getLogger(SingularityUserProvider.class); + + private List authenticators; private boolean authEnabled; @Inject - public SingularityUserProvider(Set authenticators, SingularityConfiguration configuration) { + public SingularityUserProvider(List authenticators, SingularityConfiguration configuration) { this.authenticators = authenticators; this.authEnabled = configuration.getAuthConfiguration().isEnabled(); } @@ -26,6 +31,7 @@ public Optional get() { Exception maybeException = null; for (SingularityAuthenticator authenticator : authenticators) { try { + LOG.trace("Attempting to authenticate using {}", authenticator.getClass().getSimpleName()); Optional maybeUser = authenticator.get(); if (maybeUser.isPresent()) { return maybeUser; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java index 8967e879a3..e619d3c2de 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -59,6 +59,7 @@ public Optional get() { String authHeaderValue = extractAuthHeader(requestProvider.get()); UserPermissions permissions = verify(authHeaderValue); + LOG.trace("Verified permissions for user {}", permissions); Set groups = new HashSet<>(); groups.addAll(permissions.getGroups()); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java index 608a088d9c..577abf5fa8 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/AuthConfiguration.java @@ -1,12 +1,13 @@ package com.hubspot.singularity.config; import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.validation.constraints.NotNull; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.Sets; +import com.google.common.collect.Lists; import com.hubspot.singularity.auth.SingularityAuthDatastoreClass; import com.hubspot.singularity.auth.SingularityAuthenticatorClass; @@ -21,7 +22,7 @@ public class AuthConfiguration { @JsonProperty @NotNull - private Set authenticators = Sets.newHashSet(SingularityAuthenticatorClass.QUERYPARAM_PASSTHROUGH); + private List authenticators = Lists.newArrayList(SingularityAuthenticatorClass.QUERYPARAM_PASSTHROUGH); @JsonProperty @NotNull @@ -69,11 +70,11 @@ public void setAuthenticator(SingularityAuthenticatorClass authenticator) { this.authenticators.add(authenticator); } - public Set getAuthenticators() { + public List getAuthenticators() { return authenticators; } - public void setAuthenticators(Set authenticators) { + public void setAuthenticators(List authenticators) { this.authenticators = authenticators; } From 58ee7ba6a80cf6734a7f018508c7a87a901f94bc Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Wed, 25 Oct 2017 09:53:53 -0400 Subject: [PATCH 19/79] keep this as a set --- .../auth/authenticator/SingularityUserProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java index facb625ddd..275accab0f 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java @@ -1,6 +1,6 @@ package com.hubspot.singularity.auth.authenticator; -import java.util.List; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,11 +17,11 @@ public class SingularityUserProvider implements Provider> { private static final Logger LOG = LoggerFactory.getLogger(SingularityUserProvider.class); - private List authenticators; + private Set authenticators; private boolean authEnabled; @Inject - public SingularityUserProvider(List authenticators, SingularityConfiguration configuration) { + public SingularityUserProvider(Set authenticators, SingularityConfiguration configuration) { this.authenticators = authenticators; this.authEnabled = configuration.getAuthConfiguration().isEnabled(); } From 7ab90c201d495c4857f3c0f6fc5b99d55799a651 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Wed, 25 Oct 2017 10:41:19 -0400 Subject: [PATCH 20/79] specific SingularityUserPermissionsResponse object --- .../SingularityUserPermissionsResponse.java | 61 +++++++++++++++ .../SingularityWebhookAuthenticator.java | 74 ++++--------------- 2 files changed, 74 insertions(+), 61 deletions(-) create mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java new file mode 100644 index 0000000000..46ace6af9c --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java @@ -0,0 +1,61 @@ +package com.hubspot.singularity.auth.authenticator; + +import java.util.Objects; +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.hubspot.singularity.SingularityUser; + +public class SingularityUserPermissionsResponse { + private final SingularityUser user; + private final boolean authenticated; + private final Optional error; + + @JsonCreator + public SingularityUserPermissionsResponse(@JsonProperty("user") SingularityUser user, @JsonProperty("authenticated") boolean authenticated, @JsonProperty("error") Optional error) { + this.user = user; + this.authenticated = authenticated; + this.error = error; + } + + public SingularityUser getUser() { + return user; + } + + public boolean isAuthenticated() { + return authenticated; + } + + public Optional getError() { + return error; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof SingularityUserPermissionsResponse) { + final SingularityUserPermissionsResponse that = (SingularityUserPermissionsResponse) obj; + return Objects.equals(this.authenticated, that.authenticated) && + Objects.equals(this.user, that.user) && + Objects.equals(this.error, that.error); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(user, authenticated, error); + } + + @Override + public String toString() { + return "SingularityUserPermissionsResponse{" + + "user=" + user + + ", authenticated=" + authenticated + + ", error=" + error + + '}'; + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java index e619d3c2de..897a050669 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -1,9 +1,6 @@ package com.hubspot.singularity.auth.authenticator; import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -12,9 +9,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Optional; import com.google.common.base.Strings; @@ -38,7 +32,7 @@ public class SingularityWebhookAuthenticator implements SingularityAuthenticator private final WebhookAuthConfiguration webhookAuthConfiguration; private final Provider requestProvider; private final ObjectMapper objectMapper; - private final Cache permissionsCache; + private final Cache permissionsCache; @Inject public SingularityWebhookAuthenticator(AsyncHttpClient asyncHttpClient, @@ -49,7 +43,7 @@ public SingularityWebhookAuthenticator(AsyncHttpClient asyncHttpClient, this.webhookAuthConfiguration = configuration.getWebhookAuthConfiguration(); this.requestProvider = requestProvider; this.objectMapper = objectMapper; - this.permissionsCache = CacheBuilder.newBuilder() + this.permissionsCache = CacheBuilder.newBuilder() .expireAfterWrite(webhookAuthConfiguration.getCacheValidationMs(), TimeUnit.MILLISECONDS) .build(); } @@ -58,20 +52,10 @@ public SingularityWebhookAuthenticator(AsyncHttpClient asyncHttpClient, public Optional get() { String authHeaderValue = extractAuthHeader(requestProvider.get()); - UserPermissions permissions = verify(authHeaderValue); - LOG.trace("Verified permissions for user {}", permissions); + SingularityUserPermissionsResponse permissionsResponse = verify(authHeaderValue); + LOG.trace("Verified permissions for user {}", permissionsResponse); - Set groups = new HashSet<>(); - groups.addAll(permissions.getGroups()); - groups.addAll(permissions.getScopes()); - - String email = permissions.getUid().contains("@") ? permissions.getUid() : String.format("%s@%s", permissions.getUid(), webhookAuthConfiguration.getDefaultEmailDomain()); - - return Optional.of(new SingularityUser( - permissions.getUid(), - Optional.of(permissions.getUid()), - Optional.of(email), - groups)); + return Optional.of(permissionsResponse.getUser()); } private String extractAuthHeader(HttpServletRequest request) { @@ -84,8 +68,8 @@ private String extractAuthHeader(HttpServletRequest request) { } } - private UserPermissions verify(String authHeaderValue) { - UserPermissions maybeCachedPermissions = permissionsCache.getIfPresent(authHeaderValue); + private SingularityUserPermissionsResponse verify(String authHeaderValue) { + SingularityUserPermissionsResponse maybeCachedPermissions = permissionsCache.getIfPresent(authHeaderValue); if (maybeCachedPermissions != null) { return maybeCachedPermissions; } else { @@ -98,9 +82,12 @@ private UserPermissions verify(String authHeaderValue) { throw WebExceptions.unauthorized(String.format("Got status code %d when verifying jwt", response.getStatusCode())); } else { String responseBody = response.getResponseBody(); - UserPermissions permissions = objectMapper.readValue(responseBody, UserPermissions.class); - permissionsCache.put(authHeaderValue, permissions); - return permissions; + SingularityUserPermissionsResponse permissionsResponse = objectMapper.readValue(responseBody, SingularityUserPermissionsResponse.class); + if (!permissionsResponse.isAuthenticated()) { + throw WebExceptions.unauthorized(String.format("User %s not authenticated (error: %s)", permissionsResponse.getUser().getId(), permissionsResponse.getError())); + } + permissionsCache.put(authHeaderValue, permissionsResponse); + return permissionsResponse; } } catch (IOException|ExecutionException|InterruptedException e) { LOG.error("Exception while verifying jwt", e); @@ -108,39 +95,4 @@ private UserPermissions verify(String authHeaderValue) { } } } - - @JsonIgnoreProperties(ignoreUnknown = true) - private class UserPermissions { - private final String uid; - private final Set groups; - private final Set scopes; - - @JsonCreator - public UserPermissions(@JsonProperty("uid") String uid, @JsonProperty("groups") Set groups, @JsonProperty("scopes") Set scopes) { - this.uid = uid; - this.groups = groups != null ? groups : Collections.emptySet(); - this.scopes = scopes != null ? scopes : Collections.emptySet(); - } - - public String getUid() { - return uid; - } - - public Set getGroups() { - return groups; - } - - public Set getScopes() { - return scopes; - } - - @Override - public String toString() { - return "UserPermissions{" + - "uid='" + uid + '\'' + - ", groups=" + groups + - ", scopes=" + scopes + - '}'; - } - } } From ef40108a9c0f40af6da625ac379c9604495e6cd0 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Wed, 25 Oct 2017 12:13:14 -0400 Subject: [PATCH 21/79] update user permissions response model --- .../authenticator/SingularityUserPermissionsResponse.java | 8 ++++---- .../authenticator/SingularityWebhookAuthenticator.java | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java index 46ace6af9c..6fae2e7a49 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java @@ -1,25 +1,25 @@ package com.hubspot.singularity.auth.authenticator; import java.util.Objects; -import java.util.Optional; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Optional; import com.hubspot.singularity.SingularityUser; public class SingularityUserPermissionsResponse { - private final SingularityUser user; + private final Optional user; private final boolean authenticated; private final Optional error; @JsonCreator - public SingularityUserPermissionsResponse(@JsonProperty("user") SingularityUser user, @JsonProperty("authenticated") boolean authenticated, @JsonProperty("error") Optional error) { + public SingularityUserPermissionsResponse(@JsonProperty("user") Optional user, @JsonProperty("authenticated") boolean authenticated, @JsonProperty("error") Optional error) { this.user = user; this.authenticated = authenticated; this.error = error; } - public SingularityUser getUser() { + public Optional getUser() { return user; } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java index 897a050669..0918f3c2d9 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -55,7 +55,7 @@ public Optional get() { SingularityUserPermissionsResponse permissionsResponse = verify(authHeaderValue); LOG.trace("Verified permissions for user {}", permissionsResponse); - return Optional.of(permissionsResponse.getUser()); + return permissionsResponse.getUser(); } private String extractAuthHeader(HttpServletRequest request) { @@ -84,7 +84,10 @@ private SingularityUserPermissionsResponse verify(String authHeaderValue) { String responseBody = response.getResponseBody(); SingularityUserPermissionsResponse permissionsResponse = objectMapper.readValue(responseBody, SingularityUserPermissionsResponse.class); if (!permissionsResponse.isAuthenticated()) { - throw WebExceptions.unauthorized(String.format("User %s not authenticated (error: %s)", permissionsResponse.getUser().getId(), permissionsResponse.getError())); + throw WebExceptions.unauthorized(String.format("User not authenticated (response: %s)", permissionsResponse)); + } + if (!permissionsResponse.getUser().isPresent()) { + throw WebExceptions.unauthorized(String.format("No user present in response %s", permissionsResponse)); } permissionsCache.put(authHeaderValue, permissionsResponse); return permissionsResponse; From 16caab8d08f25d2ad344f46e56637ab8e331891d Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Wed, 25 Oct 2017 12:15:19 -0400 Subject: [PATCH 22/79] don't need this config field --- .../singularity/config/WebhookAuthConfiguration.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java b/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java index bed7aebaa8..b511e00ea5 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/config/WebhookAuthConfiguration.java @@ -14,10 +14,6 @@ public class WebhookAuthConfiguration { @Nonnegative private long cacheValidationMs = 60000; - @JsonProperty - @NotNull - private String defaultEmailDomain = ""; - public String getAuthVerificationUrl() { return authVerificationUrl; } @@ -33,12 +29,4 @@ public long getCacheValidationMs() { public void setCacheValidationMs(long cacheValidationMs) { this.cacheValidationMs = cacheValidationMs; } - - public String getDefaultEmailDomain() { - return defaultEmailDomain; - } - - public void setDefaultEmailDomain(String defaultEmailDomain) { - this.defaultEmailDomain = defaultEmailDomain; - } } From f0955d21cfab130689284fe24f2b96575a3a28fe Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Wed, 25 Oct 2017 14:16:08 -0400 Subject: [PATCH 23/79] ui overrides --- .../singularity/config/UIConfiguration.java | 38 +++++++++++++++++-- .../hubspot/singularity/views/IndexView.java | 6 +-- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java index 206044b566..625bdd9b45 100644 --- a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java +++ b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java @@ -97,6 +97,14 @@ public static RootUrlMode parse(String value) { @NotNull private String authCookieName = ""; + @JsonProperty + private Optional apiRootOverride = Optional.absent(); + + @JsonProperty + private Optional appRootOverride = Optional.absent(); + + @JsonProperty + private Optional staticRootOverride = Optional.absent(); public boolean isHideNewDeployButton() { return hideNewDeployButton; @@ -238,17 +246,39 @@ public boolean isGenerateAuthHeader() { return generateAuthHeader; } - public UIConfiguration setGenerateAuthHeader(boolean generateAuthHeader) { + public void setGenerateAuthHeader(boolean generateAuthHeader) { this.generateAuthHeader = generateAuthHeader; - return this; } public String getAuthCookieName() { return authCookieName; } - public UIConfiguration setAuthCookieName(String authCookieName) { + public void setAuthCookieName(String authCookieName) { this.authCookieName = authCookieName; - return this; + } + + public Optional getApiRootOverride() { + return apiRootOverride; + } + + public void setApiRootOverride(Optional apiRootOverride) { + this.apiRootOverride = apiRootOverride; + } + + public Optional getAppRootOverride() { + return appRootOverride; + } + + public void setAppRootOverride(Optional appRootOverride) { + this.appRootOverride = appRootOverride; + } + + public Optional getStaticRootOverride() { + return staticRootOverride; + } + + public void setStaticRootOverride(Optional staticRootOverride) { + this.staticRootOverride = staticRootOverride; } } diff --git a/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java b/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java index 5ec6dc4c97..e6b25c19a5 100644 --- a/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java +++ b/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java @@ -73,10 +73,10 @@ public IndexView(String singularityUriBase, String appRoot, IndexViewConfigurati String rawAppRoot = String.format("%s%s", singularityUriBase, appRoot); - this.appRoot = (rawAppRoot.endsWith("/")) ? rawAppRoot.substring(0, rawAppRoot.length() - 1) : rawAppRoot; - this.staticRoot = String.format("%s/static", singularityUriBase); + this.appRoot = uiConfiguration.getAppRootOverride().or((rawAppRoot.endsWith("/")) ? rawAppRoot.substring(0, rawAppRoot.length() - 1) : rawAppRoot); + this.staticRoot = uiConfiguration.getStaticRootOverride().or(String.format("%s/static", singularityUriBase)); this.apiDocs = String.format("%s/api-docs/", singularityUriBase); - this.apiRoot = String.format("%s%s", singularityUriBase, ApiPaths.API_BASE_PATH); + this.apiRoot = uiConfiguration.getApiRootOverride().or(String.format("%s%s", singularityUriBase, ApiPaths.API_BASE_PATH)); this.title = uiConfiguration.getTitle(); From 9af857ea6e66c2592dbd815bb42f9497c98a1f86 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Wed, 25 Oct 2017 14:34:18 -0400 Subject: [PATCH 24/79] catch these exceptions and return absent --- .../SingularityWebhookAuthenticator.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java index 0918f3c2d9..2885b5124d 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -5,6 +5,7 @@ import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.WebApplicationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,12 +51,17 @@ public SingularityWebhookAuthenticator(AsyncHttpClient asyncHttpClient, @Override public Optional get() { - String authHeaderValue = extractAuthHeader(requestProvider.get()); + try { + String authHeaderValue = extractAuthHeader(requestProvider.get()); - SingularityUserPermissionsResponse permissionsResponse = verify(authHeaderValue); - LOG.trace("Verified permissions for user {}", permissionsResponse); + SingularityUserPermissionsResponse permissionsResponse = verify(authHeaderValue); + LOG.trace("Verified permissions for user {}", permissionsResponse); - return permissionsResponse.getUser(); + return permissionsResponse.getUser(); + } catch (WebApplicationException wae) { + LOG.trace("Not authenticated: {}", wae.getMessage()); + return Optional.absent(); + } } private String extractAuthHeader(HttpServletRequest request) { From e9880948cfdc391d1d59d2f3ec626e746139f979 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Wed, 25 Oct 2017 14:45:29 -0400 Subject: [PATCH 25/79] catch one level up --- .../authenticator/SingularityUserProvider.java | 14 +++----------- .../SingularityWebhookAuthenticator.java | 14 ++++---------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java index 275accab0f..260bf9a1ad 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java @@ -10,20 +10,16 @@ import com.google.inject.Provider; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; -import com.hubspot.singularity.WebExceptions; -import com.hubspot.singularity.config.SingularityConfiguration; @Singleton public class SingularityUserProvider implements Provider> { private static final Logger LOG = LoggerFactory.getLogger(SingularityUserProvider.class); private Set authenticators; - private boolean authEnabled; @Inject - public SingularityUserProvider(Set authenticators, SingularityConfiguration configuration) { + public SingularityUserProvider(Set authenticators) { this.authenticators = authenticators; - this.authEnabled = configuration.getAuthConfiguration().isEnabled(); } @Override @@ -40,11 +36,7 @@ public Optional get() { maybeException = e; } } - if (authEnabled) { - throw WebExceptions.unauthorized( - maybeException != null ? maybeException.getMessage() : String.format("Not authorized using authenticators: %s", authenticators)); - } else { - return Optional.absent(); - } + LOG.trace("Not authenticated {}", maybeException != null ? maybeException.getMessage() : ""); + return Optional.absent(); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java index 2885b5124d..0918f3c2d9 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -5,7 +5,6 @@ import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.WebApplicationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,17 +50,12 @@ public SingularityWebhookAuthenticator(AsyncHttpClient asyncHttpClient, @Override public Optional get() { - try { - String authHeaderValue = extractAuthHeader(requestProvider.get()); + String authHeaderValue = extractAuthHeader(requestProvider.get()); - SingularityUserPermissionsResponse permissionsResponse = verify(authHeaderValue); - LOG.trace("Verified permissions for user {}", permissionsResponse); + SingularityUserPermissionsResponse permissionsResponse = verify(authHeaderValue); + LOG.trace("Verified permissions for user {}", permissionsResponse); - return permissionsResponse.getUser(); - } catch (WebApplicationException wae) { - LOG.trace("Not authenticated: {}", wae.getMessage()); - return Optional.absent(); - } + return permissionsResponse.getUser(); } private String extractAuthHeader(HttpServletRequest request) { From 5a742fdaf8ee787c9e9c5ab1b24d8a273e2cee24 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Wed, 25 Oct 2017 15:02:12 -0400 Subject: [PATCH 26/79] redirect to login earlier --- SingularityUI/app/initialize.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SingularityUI/app/initialize.jsx b/SingularityUI/app/initialize.jsx index 34b7803d6d..872b1627aa 100644 --- a/SingularityUI/app/initialize.jsx +++ b/SingularityUI/app/initialize.jsx @@ -67,6 +67,9 @@ document.addEventListener('DOMContentLoaded', () => { window.app = {}; window.app.setupUser = () => store.dispatch(FetchUser.trigger()); window.app.setupUser().then(() => { + if (store.getState().api.user.data.authEnabled && !store.getState().api.user.data.authenticated) { // redirect to login + window.location.href = config.redirectOnUnauthorizedUrl.replace('{URL}', encodeURIComponent(window.location.href)); + } if (!store.getState().api.user.data.user) { return renderUserIdForm(); } else { From b778e64183812f2c26dc51734ab24653dad6e657 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Thu, 26 Oct 2017 11:32:05 -0400 Subject: [PATCH 27/79] wip, dropwizard auth --- .../java/com/hubspot/mesos/JavaUtils.java | 8 - .../hubspot/singularity/SingularityUser.java | 66 +++-- SingularityService/pom.xml | 10 + .../singularity/SingularityAuthModule.java | 9 +- .../singularity/SingularityService.java | 2 +- .../auth/SingularityAuthorizationHelper.java | 62 +++-- .../SingularityAuthenticator.java | 10 +- .../SingularityDisabledAuthenticator.java | 9 +- ...ularityHeaderPassthroughAuthenticator.java | 31 +-- .../SingularityMultiLevelAuthenticator.java | 58 +++++ .../SingularityQueryParamAuthenticator.java | 31 +-- .../SingularityUserPermissionsResponse.java | 2 +- .../SingularityUserProvider.java | 42 --- .../SingularityWebhookAuthenticator.java | 34 +-- .../datastore/SingularityAuthDatastore.java | 5 +- .../SingularityDisabledAuthDatastore.java | 9 +- .../datastore/SingularityDummyDatastore.java | 4 +- .../datastore/SingularityLDAPDatastore.java | 18 +- .../resources/AbstractHistoryResource.java | 12 +- .../resources/AbstractMachineResource.java | 32 ++- .../resources/AbstractRequestResource.java | 10 +- .../singularity/resources/AuthResource.java | 21 +- .../singularity/resources/DeployResource.java | 34 +-- .../resources/DisastersResource.java | 36 ++- .../resources/HistoryResource.java | 43 +++- .../resources/InactiveSlaveResource.java | 14 +- .../resources/PriorityResource.java | 13 +- .../singularity/resources/RackResource.java | 31 +-- .../resources/RequestResource.java | 240 ++++++++++-------- .../singularity/resources/S3LogResource.java | 46 ++-- .../resources/SandboxResource.java | 29 ++- .../singularity/resources/SlaveResource.java | 31 +-- .../singularity/resources/TaskResource.java | 78 +++--- .../singularity/resources/UsageResource.java | 23 +- .../singularity/resources/UserResource.java | 26 +- .../resources/WebhookResource.java | 29 +-- .../sentry/SingularityExceptionNotifier.java | 12 +- .../SingularityAuthorizationHelperTest.java | 10 +- .../singularity/SingularityHistoryTest.java | 16 +- .../SingularityTestAuthenticator.java | 9 +- .../singularity/data/StateManagerTest.java | 2 +- .../mesos/SingularityStartupTest.java | 2 +- .../SingularityTaskShellCommandTest.java | 12 +- .../scheduler/SingularityDeploysTest.java | 67 +++-- .../SingularityExpiringActionsTest.java | 19 +- .../SingularityHealthchecksTest.java | 13 +- .../SingularityMachineStatesTest.java | 20 +- .../scheduler/SingularitySchedulerTest.java | 174 ++++++------- .../SingularitySchedulerTestBase.java | 11 +- .../SingularitySlavePlacementTest.java | 4 +- pom.xml | 6 + 51 files changed, 803 insertions(+), 732 deletions(-) create mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java delete mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java diff --git a/SingularityBase/src/main/java/com/hubspot/mesos/JavaUtils.java b/SingularityBase/src/main/java/com/hubspot/mesos/JavaUtils.java index c2315afad0..aae04c2aaf 100644 --- a/SingularityBase/src/main/java/com/hubspot/mesos/JavaUtils.java +++ b/SingularityBase/src/main/java/com/hubspot/mesos/JavaUtils.java @@ -202,12 +202,4 @@ public static ThreadPoolExecutor newFixedTimingOutThreadPool(int maxThreads, lon public static String getReplaceHyphensWithUnderscores(String string) { return string.replace("-", "_"); } - - public static final Optional getUserEmail(Optional user) { - if (user.isPresent()) { - return user.get().getEmail(); - } else { - return Optional.absent(); - } - } } diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java index 4089e7c282..65005e8224 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java @@ -2,6 +2,8 @@ import static com.google.common.collect.ImmutableSet.copyOf; +import java.security.Principal; +import java.util.Collections; import java.util.Objects; import java.util.Set; @@ -9,26 +11,40 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Optional; -public class SingularityUser { +public class SingularityUser implements Principal { private final String id; private final Optional name; private final Optional email; private final Set groups; + private final boolean authenticated; + + public static SingularityUser defaultUser() { + return new SingularityUser("singularity", Optional.absent(), Optional.absent(), Collections.emptySet(), false); + } + + public SingularityUser(String id, Optional name, Optional email, Set groups) { + this(id, name, email, groups, true); + } @JsonCreator - public SingularityUser(@JsonProperty("id") String id, @JsonProperty("name") Optional name, @JsonProperty("email") Optional email, @JsonProperty("groups") Set groups) { + public SingularityUser(@JsonProperty("id") String id, + @JsonProperty("name") Optional name, + @JsonProperty("email") Optional email, + @JsonProperty("groups") Set groups, + @JsonProperty("authenticated") boolean authenticated) { this.id = id; this.name = name; this.email = email; this.groups = copyOf(groups); + this.authenticated = authenticated; } public String getId() { return id; } - public Optional getName() { - return name; + public String getName() { + return name.or(id); } public Optional getEmail() { @@ -39,33 +55,39 @@ public Set getGroups() { return groups; } - @Override - public String toString() { - return "SingularityUser{" + - "id='" + id + '\'' + - ", name=" + name + - ", email=" + email + - ", groups=" + groups + - '}'; + public boolean isAuthenticated() { + return authenticated; } @Override - public boolean equals(Object o) { - if (this == o) { + public boolean equals(Object obj) { + if (this == obj) { return true; } - if (o == null || getClass() != o.getClass()) { - return false; + if (obj instanceof SingularityUser) { + final SingularityUser that = (SingularityUser) obj; + return Objects.equals(this.authenticated, that.authenticated) && + Objects.equals(this.id, that.id) && + Objects.equals(this.name, that.name) && + Objects.equals(this.email, that.email) && + Objects.equals(this.groups, that.groups); } - SingularityUser that = (SingularityUser) o; - return Objects.equals(id, that.id) && - Objects.equals(name, that.name) && - Objects.equals(email, that.email) && - Objects.equals(groups, that.groups); + return false; } @Override public int hashCode() { - return Objects.hash(id, name, email, groups); + return Objects.hash(id, name, email, groups, authenticated); + } + + @Override + public String toString() { + return "SingularityUser{" + + "id='" + id + '\'' + + ", name=" + name + + ", email=" + email + + ", groups=" + groups + + ", authenticated=" + authenticated + + '}'; } } diff --git a/SingularityService/pom.xml b/SingularityService/pom.xml index 7ecdc44ff0..27b01abdd9 100644 --- a/SingularityService/pom.xml +++ b/SingularityService/pom.xml @@ -280,6 +280,16 @@ metrics-guice + + io.dropwizard + dropwizard-auth + + + + com.sun.jersey + jersey-server + + com.fasterxml.jackson.core jackson-annotations diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java index e8fbd5c617..e0ab550ff7 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java @@ -1,6 +1,7 @@ package com.hubspot.singularity; -import com.google.common.base.Optional; +import javax.ws.rs.container.ContainerRequestContext; + import com.google.inject.Binder; import com.google.inject.Module; import com.google.inject.Scopes; @@ -9,10 +10,12 @@ import com.hubspot.singularity.auth.SingularityAuthenticatorClass; import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; -import com.hubspot.singularity.auth.authenticator.SingularityUserProvider; +import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; import com.hubspot.singularity.config.SingularityConfiguration; +import io.dropwizard.auth.Authenticator; + public class SingularityAuthModule implements Module { private final SingularityConfiguration configuration; @@ -26,9 +29,9 @@ public void configure(Binder binder) { for (SingularityAuthenticatorClass clazz : configuration.getAuthConfiguration().getAuthenticators()) { multibinder.addBinding().to(clazz.getAuthenticatorClass()); } + binder.bind(new TypeLiteral>() {}).to(SingularityMultiLevelAuthenticator.class); binder.bind(SingularityAuthDatastore.class).to(configuration.getAuthConfiguration().getDatastore().getAuthDatastoreClass()); - binder.bind(new TypeLiteral>() {}).toProvider(SingularityUserProvider.class); binder.bind(SingularityAuthorizationHelper.class).in(Scopes.SINGLETON); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java index 009c1915a6..27aa0f1823 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java @@ -100,7 +100,7 @@ public Iterable> getDropwizardConfiguredBundles(Bo public static void main(final String[] args) throws Exception { try { - new SingularityService().run(args); + new SingularityService<>().run(args); } catch (final Throwable t) { t.printStackTrace(); System.exit(1); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthorizationHelper.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthorizationHelper.java index 772f74a148..d27b8d5ee3 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthorizationHelper.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthorizationHelper.java @@ -3,7 +3,6 @@ import static com.google.common.collect.ImmutableSet.copyOf; import static com.hubspot.singularity.WebExceptions.badRequest; import static com.hubspot.singularity.WebExceptions.checkForbidden; -import static com.hubspot.singularity.WebExceptions.checkNotFound; import static com.hubspot.singularity.WebExceptions.checkUnauthorized; import java.util.Collections; @@ -57,31 +56,36 @@ public static boolean groupsIntersect(Set a, Set b) { return !Sets.intersection(a, b).isEmpty(); } - public boolean hasAdminAuthorization(Optional user) { + public boolean hasAdminAuthorization(SingularityUser user) { // disabled auth == no rules! if (!authEnabled) { return true; } + if (!user.isAuthenticated()) { + return false; + } + // not authenticated, or no groups, or no admin groups == can't possibly be admin - if (!user.isPresent() || user.get().getGroups().isEmpty() || adminGroups.isEmpty()) { + if (user.getGroups().isEmpty() || adminGroups.isEmpty()) { return false; } - return groupsIntersect(user.get().getGroups(), adminGroups); + return groupsIntersect(user.getGroups(), adminGroups); } - public void checkAdminAuthorization(Optional user) { + public void checkAdminAuthorization(SingularityUser user) { if (authEnabled) { - checkUnauthorized(user.isPresent(), "Please log in to perform this action."); + checkForbidden(user.isAuthenticated(), "Not Authenticated!"); if (!adminGroups.isEmpty()) { - checkForbidden(groupsIntersect(user.get().getGroups(), adminGroups), "%s must be part of one or more admin groups: %s", user.get().getId(), JavaUtils.COMMA_JOINER.join(adminGroups)); + checkForbidden(groupsIntersect(user.getGroups(), adminGroups), "%s must be part of one or more admin groups: %s", user.getId(), JavaUtils.COMMA_JOINER.join(adminGroups)); } } } - public void checkForAuthorizationByTaskId(String taskId, Optional user, SingularityAuthorizationScope scope) { + public void checkForAuthorizationByTaskId(String taskId, SingularityUser user, SingularityAuthorizationScope scope) { if (authEnabled) { + checkForbidden(user.isAuthenticated(), "Not Authenticated!"); try { final SingularityTaskId taskIdObj = SingularityTaskId.valueOf(taskId); @@ -96,8 +100,9 @@ public void checkForAuthorizationByTaskId(String taskId, Optional user, SingularityAuthorizationScope scope) { + public void checkForAuthorizationByRequestId(String requestId, SingularityUser user, SingularityAuthorizationScope scope) { if (authEnabled) { + final Optional maybeRequest = requestManager.getRequest(requestId); if (maybeRequest.isPresent()) { @@ -106,16 +111,16 @@ public void checkForAuthorizationByRequestId(String requestId, Optional user, SingularityAuthorizationScope scope) { + public boolean isAuthorizedForRequest(SingularityRequest request, SingularityUser user, SingularityAuthorizationScope scope) { if (!authEnabled) { return true; // no auth == no rules! } - if (!user.isPresent()) { + if (!user.isAuthenticated()) { return false; } - final Set userGroups = user.get().getGroups(); + final Set userGroups = user.getGroups(); final Set readWriteGroups = Sets.union(request.getGroup().asSet(), request.getReadWriteGroups().or(Collections.emptySet())); final Set readOnlyGroups = request.getReadOnlyGroups().or(defaultReadOnlyGroups); @@ -136,14 +141,14 @@ public boolean isAuthorizedForRequest(SingularityRequest request, Optional user, SingularityAuthorizationScope scope) { + public void checkForAuthorization(SingularityRequest request, SingularityUser user, SingularityAuthorizationScope scope) { if (!authEnabled) { return; } - checkUnauthorized(user.isPresent(), "user must be present"); + checkForbidden(user.isAuthenticated(), "Not authenticated!"); - final Set userGroups = user.get().getGroups(); + final Set userGroups = user.getGroups(); final Set readWriteGroups = Sets.union(request.getGroup().asSet(), request.getReadWriteGroups().or(Collections.emptySet())); final Set readOnlyGroups = request.getReadOnlyGroups().or(defaultReadOnlyGroups); @@ -157,26 +162,26 @@ public void checkForAuthorization(SingularityRequest request, Optional user) { + public void checkForAuthorizedChanges(SingularityRequest request, SingularityRequest oldRequest, SingularityUser user) { if (!authEnabled) { return; } - checkUnauthorized(user.isPresent(), "user must be present"); + checkForbidden(user.isAuthenticated(), "Not Authenticated!"); if (oldRequest.getGroup().isPresent() && !oldRequest.getReadWriteGroups().equals(request.getReadWriteGroups())) { - final Set userGroups = user.get().getGroups(); + final Set userGroups = user.getGroups(); final boolean userIsAdmin = !adminGroups.isEmpty() && groupsIntersect(userGroups, adminGroups); final boolean userIsJITA = !jitaGroups.isEmpty() && groupsIntersect(userGroups, jitaGroups); final boolean userIsRequestOwner = userGroups.contains(oldRequest.getGroup().get()); @@ -186,7 +191,7 @@ public void checkForAuthorizedChanges(SingularityRequest request, SingularityReq } } - public Iterable filterByAuthorizedRequests(final Optional user, List objects, final Function requestIdFunction, final SingularityAuthorizationScope scope) { + public Iterable filterByAuthorizedRequests(final SingularityUser user, List objects, final Function requestIdFunction, final SingularityAuthorizationScope scope) { if (hasAdminAuthorization(user)) { return objects; } @@ -214,7 +219,7 @@ public boolean apply(@Nonnull T input) { }); } - public Iterable filterAuthorizedRequestIds(final Optional user, List requestIds, final SingularityAuthorizationScope scope, boolean useWebCache) { + public Iterable filterAuthorizedRequestIds(final SingularityUser user, List requestIds, final SingularityAuthorizationScope scope, boolean useWebCache) { if (hasAdminAuthorization(user)) { return requestIds; } @@ -233,13 +238,4 @@ public boolean apply(@Nonnull String input) { } }); } - - public String getAuthUserId(Optional user) { - if (authEnabled) { - checkUnauthorized(user.isPresent(), "Please log in to perform this action."); - } else { - checkNotFound(user.isPresent(), "Please set a user id to perform this action."); - } - return user.get().getId(); - } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityAuthenticator.java index 5f4d0905d8..6866ece6eb 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityAuthenticator.java @@ -1,9 +1,11 @@ package com.hubspot.singularity.auth.authenticator; -import com.google.common.base.Optional; -import com.google.inject.Provider; -import com.hubspot.singularity.SingularityUser; +import java.util.Optional; + +import javax.ws.rs.container.ContainerRequestContext; -public interface SingularityAuthenticator extends Provider> { +import com.hubspot.singularity.SingularityUser; +public interface SingularityAuthenticator { + Optional getUser(ContainerRequestContext context); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityDisabledAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityDisabledAuthenticator.java index c4e102677c..f89ed1887a 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityDisabledAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityDisabledAuthenticator.java @@ -1,6 +1,9 @@ package com.hubspot.singularity.auth.authenticator; -import com.google.common.base.Optional; +import java.util.Optional; + +import javax.ws.rs.container.ContainerRequestContext; + import com.google.inject.Inject; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; @@ -11,7 +14,7 @@ public class SingularityDisabledAuthenticator implements SingularityAuthenticato public SingularityDisabledAuthenticator() {} @Override - public Optional get() { - return Optional.absent(); + public Optional getUser(ContainerRequestContext context) { + return Optional.empty(); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java index 4e5a746d1b..1ff41369b9 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityHeaderPassthroughAuthenticator.java @@ -1,17 +1,18 @@ package com.hubspot.singularity.auth.authenticator; -import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +import javax.ws.rs.container.ContainerRequestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.inject.Inject; -import com.google.inject.Provider; import com.google.inject.ProvisionException; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; import com.hubspot.singularity.config.SingularityConfiguration; @@ -19,35 +20,31 @@ public class SingularityHeaderPassthroughAuthenticator implements SingularityAuthenticator { private static final Logger LOG = LoggerFactory.getLogger(SingularityHeaderPassthroughAuthenticator.class); - private final SingularityAuthDatastore datastore; + private final SingularityAuthDatastore authDatastore; private final String requestUserHeaderName; - private final Provider requestProvider; @Inject - public SingularityHeaderPassthroughAuthenticator(SingularityAuthDatastore datastore, SingularityConfiguration configuration, Provider requestProvider) { - this.datastore = datastore; + public SingularityHeaderPassthroughAuthenticator(SingularityAuthDatastore authDatastore, SingularityConfiguration configuration) { + this.authDatastore = authDatastore; this.requestUserHeaderName = configuration.getAuthConfiguration().getRequestUserHeaderName(); - this.requestProvider = requestProvider; } - private Optional getUserId() { + private Optional getUserId(ContainerRequestContext context) { try { - return Optional.fromNullable(Strings.emptyToNull(requestProvider.get().getHeader(requestUserHeaderName))); + return Optional.ofNullable(Strings.emptyToNull(context.getHeaderString(requestUserHeaderName))); } catch (ProvisionException pe) { - return Optional.absent(); + return Optional.empty(); } } @Override - public Optional get() { - final Optional maybeUsername = getUserId(); - - LOG.trace("Fetched user {} from header", maybeUsername); + public Optional getUser(ContainerRequestContext context) { + final Optional maybeUsername = getUserId(context); if (!maybeUsername.isPresent()) { - return Optional.absent(); + throw WebExceptions.unauthorized("(HeaderPassthrough) Could not determine username from header"); } - return datastore.getUser(maybeUsername.get()); + return authDatastore.getUser(maybeUsername.get()); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java new file mode 100644 index 0000000000..a8d0054748 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java @@ -0,0 +1,58 @@ +package com.hubspot.singularity.auth.authenticator; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.container.ContainerRequestContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.inject.Inject; +import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; +import com.hubspot.singularity.config.SingularityConfiguration; + +import io.dropwizard.auth.Authenticator; + +public class SingularityMultiLevelAuthenticator implements Authenticator { + private static final Logger LOG = LoggerFactory.getLogger(SingularityMultiLevelAuthenticator.class); + + private final Set authenticators; + private final SingularityConfiguration configuration; + + @Inject + public SingularityMultiLevelAuthenticator(Set authenticators, SingularityConfiguration configuration) { + this.authenticators = authenticators; + this.configuration = configuration; + } + + public Optional authenticate(ContainerRequestContext context) { + WebApplicationException unauthorizedException = null; + for (SingularityAuthenticator authenticator : authenticators) { + try { + Optional maybeUser = authenticator.getUser(context); + if (maybeUser.isPresent()) { + return maybeUser; + } + } catch (WebApplicationException e) { + LOG.trace("Unauthenticated: {}", e.getMessage()); + unauthorizedException = e; + } + } + + // No user found if we got here + if (configuration.getAuthConfiguration().isEnabled()) { + if (unauthorizedException != null) { + throw unauthorizedException; + } else { + throw WebExceptions.unauthorized(String.format("Unable to authenticate user using methods: %s", authenticators.stream().map(SingularityAuthenticator::getClass).collect(Collectors.toList()))); + } + } + + // Auth is disabled, return a dummy/default user + return Optional.of(SingularityUser.defaultUser()); + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityQueryParamAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityQueryParamAuthenticator.java index 49d575822d..db98bfc100 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityQueryParamAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityQueryParamAuthenticator.java @@ -1,43 +1,32 @@ package com.hubspot.singularity.auth.authenticator; -import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +import javax.ws.rs.container.ContainerRequestContext; -import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.ProvisionException; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; @Singleton public class SingularityQueryParamAuthenticator implements SingularityAuthenticator { - private final Provider requestProvider; private final SingularityAuthDatastore authDatastore; @Inject - public SingularityQueryParamAuthenticator(Provider requestProvider, SingularityAuthDatastore authDatastore) { - this.requestProvider = requestProvider; + public SingularityQueryParamAuthenticator(SingularityAuthDatastore authDatastore) { this.authDatastore = authDatastore; } - private Optional getUserId() { - try { - return Optional.fromNullable(Strings.emptyToNull(requestProvider.get().getParameter("user"))); - } catch (ProvisionException pe) { - return Optional.absent(); - } - } - @Override - public Optional get() { - final Optional maybeUser = getUserId(); + public Optional getUser(ContainerRequestContext context) { + final Optional maybeUserId = Optional.ofNullable(Strings.emptyToNull(context.getUriInfo().getQueryParameters().getFirst("user"))); - if (maybeUser.isPresent()) { - return authDatastore.getUser(maybeUser.get()); - } else { - return Optional.absent(); + if (!maybeUserId.isPresent()) { + throw WebExceptions.unauthorized("(QueryParam) No user specified"); } + return authDatastore.getUser(maybeUserId.get()); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java index 6fae2e7a49..0da3261995 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java @@ -1,10 +1,10 @@ package com.hubspot.singularity.auth.authenticator; import java.util.Objects; +import java.util.Optional; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.Optional; import com.hubspot.singularity.SingularityUser; public class SingularityUserPermissionsResponse { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java deleted file mode 100644 index 260bf9a1ad..0000000000 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserProvider.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.hubspot.singularity.auth.authenticator; - -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.Singleton; -import com.hubspot.singularity.SingularityUser; - -@Singleton -public class SingularityUserProvider implements Provider> { - private static final Logger LOG = LoggerFactory.getLogger(SingularityUserProvider.class); - - private Set authenticators; - - @Inject - public SingularityUserProvider(Set authenticators) { - this.authenticators = authenticators; - } - - @Override - public Optional get() { - Exception maybeException = null; - for (SingularityAuthenticator authenticator : authenticators) { - try { - LOG.trace("Attempting to authenticate using {}", authenticator.getClass().getSimpleName()); - Optional maybeUser = authenticator.get(); - if (maybeUser.isPresent()) { - return maybeUser; - } - } catch (Exception e) { - maybeException = e; - } - } - LOG.trace("Not authenticated {}", maybeException != null ? maybeException.getMessage() : ""); - return Optional.absent(); - } -} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java index 0918f3c2d9..cda3292840 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -1,21 +1,18 @@ package com.hubspot.singularity.auth.authenticator; import java.io.IOException; +import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import javax.servlet.http.HttpServletRequest; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import javax.ws.rs.container.ContainerRequestContext; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import com.google.common.net.HttpHeaders; import com.google.inject.Inject; -import com.google.inject.Provider; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.WebExceptions; @@ -26,22 +23,17 @@ @Singleton public class SingularityWebhookAuthenticator implements SingularityAuthenticator { - private static final Logger LOG = LoggerFactory.getLogger(SingularityWebhookAuthenticator.class); - private final AsyncHttpClient asyncHttpClient; private final WebhookAuthConfiguration webhookAuthConfiguration; - private final Provider requestProvider; private final ObjectMapper objectMapper; private final Cache permissionsCache; @Inject public SingularityWebhookAuthenticator(AsyncHttpClient asyncHttpClient, SingularityConfiguration configuration, - Provider requestProvider, ObjectMapper objectMapper) { this.asyncHttpClient = asyncHttpClient; this.webhookAuthConfiguration = configuration.getWebhookAuthConfiguration(); - this.requestProvider = requestProvider; this.objectMapper = objectMapper; this.permissionsCache = CacheBuilder.newBuilder() .expireAfterWrite(webhookAuthConfiguration.getCacheValidationMs(), TimeUnit.MILLISECONDS) @@ -49,20 +41,19 @@ public SingularityWebhookAuthenticator(AsyncHttpClient asyncHttpClient, } @Override - public Optional get() { - String authHeaderValue = extractAuthHeader(requestProvider.get()); + public Optional getUser(ContainerRequestContext context) { + String authHeaderValue = extractAuthHeader(context); SingularityUserPermissionsResponse permissionsResponse = verify(authHeaderValue); - LOG.trace("Verified permissions for user {}", permissionsResponse); return permissionsResponse.getUser(); } - private String extractAuthHeader(HttpServletRequest request) { - String authHeaderValue = request.getHeader("Authorization"); + private String extractAuthHeader(ContainerRequestContext context) { + final String authHeaderValue = context.getHeaderString(HttpHeaders.AUTHORIZATION); if (Strings.isNullOrEmpty(authHeaderValue)) { - throw WebExceptions.unauthorized("No Authorization header present, please log in first"); + throw WebExceptions.unauthorized("(Webhook) No Authorization header present, please log in first"); } else { return authHeaderValue; } @@ -79,22 +70,21 @@ private SingularityUserPermissionsResponse verify(String authHeaderValue) { .execute() .get(); if (response.getStatusCode() > 299) { - throw WebExceptions.unauthorized(String.format("Got status code %d when verifying jwt", response.getStatusCode())); + throw WebExceptions.unauthorized(String.format("(Webhook) Got status code %d when verifying jwt", response.getStatusCode())); } else { String responseBody = response.getResponseBody(); SingularityUserPermissionsResponse permissionsResponse = objectMapper.readValue(responseBody, SingularityUserPermissionsResponse.class); if (!permissionsResponse.isAuthenticated()) { - throw WebExceptions.unauthorized(String.format("User not authenticated (response: %s)", permissionsResponse)); + throw WebExceptions.unauthorized(String.format("(Webhook) User not authenticated (response: %s)", permissionsResponse)); } if (!permissionsResponse.getUser().isPresent()) { - throw WebExceptions.unauthorized(String.format("No user present in response %s", permissionsResponse)); + throw WebExceptions.unauthorized(String.format("(Webhook) No user present in response %s", permissionsResponse)); } permissionsCache.put(authHeaderValue, permissionsResponse); return permissionsResponse; } } catch (IOException|ExecutionException|InterruptedException e) { - LOG.error("Exception while verifying jwt", e); - throw WebExceptions.unauthorized(String.format("Exception while verifying jwt: %s", e.getMessage())); + throw WebExceptions.unauthorized(String.format("(Webhook) Exception while verifying token: %s", e.getMessage())); } } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityAuthDatastore.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityAuthDatastore.java index d40d462805..b7f164a22d 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityAuthDatastore.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityAuthDatastore.java @@ -1,9 +1,10 @@ package com.hubspot.singularity.auth.datastore; -import com.google.common.base.Optional; +import java.util.Optional; + import com.hubspot.singularity.SingularityUser; public interface SingularityAuthDatastore { Optional getUser(String username); - Optional isHealthy(); + com.google.common.base.Optional isHealthy(); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDisabledAuthDatastore.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDisabledAuthDatastore.java index 23b6750990..9b69c7ae5c 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDisabledAuthDatastore.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDisabledAuthDatastore.java @@ -1,6 +1,7 @@ package com.hubspot.singularity.auth.datastore; -import com.google.common.base.Optional; +import java.util.Optional; + import com.google.inject.Inject; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; @@ -12,11 +13,11 @@ public SingularityDisabledAuthDatastore() {} @Override public Optional getUser(String username) { - return Optional.absent(); + return Optional.empty(); } @Override - public Optional isHealthy() { - return Optional.absent(); + public com.google.common.base.Optional isHealthy() { + return com.google.common.base.Optional.absent(); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDummyDatastore.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDummyDatastore.java index 1bb4094fd4..f3dc6abd06 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDummyDatastore.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityDummyDatastore.java @@ -14,8 +14,8 @@ public SingularityDummyDatastore() { } @Override - public Optional getUser(String username) { - return Optional.of(new SingularityUser(username, Optional.of(username), Optional.of(username), Collections.emptySet())); + public java.util.Optional getUser(String username) { + return java.util.Optional.of(new SingularityUser(username, Optional.of(username), Optional.of(username), Collections.emptySet())); } @Override diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityLDAPDatastore.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityLDAPDatastore.java index ba828a0dfe..1876a749fd 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityLDAPDatastore.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/datastore/SingularityLDAPDatastore.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -19,7 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.base.Throwables; import com.google.common.cache.Cache; @@ -94,12 +94,12 @@ public SingularityLDAPDatastore(SingularityConfiguration configuration, .build(); ldapCache = Optional.of(cache); } else { - ldapCache = Optional.absent(); + ldapCache = Optional.empty(); } } @Override - public Optional isHealthy() { + public com.google.common.base.Optional isHealthy() { try { final LdapConnection connection = connectionPool.getConnection(); @@ -107,7 +107,7 @@ public Optional isHealthy() { if (connection.isConnected() && connection.isAuthenticated()) { connection.bind(); try { - return Optional.of(true); + return com.google.common.base.Optional.of(true); } finally { connection.unBind(); } @@ -119,7 +119,7 @@ public Optional isHealthy() { LOG.warn("LdapException caught when checking health", e); exceptionNotifier.notify(String.format("LdapException caught when checking health (%s)", e.getMessage()), e); } - return Optional.of(false); + return com.google.common.base.Optional.of(false); } @Override @@ -153,10 +153,10 @@ public Optional getUser(String user) { if (!userCursor.next()) { if (ldapCache.isPresent()) { - ldapCache.get().put(user, Optional. absent()); + ldapCache.get().put(user, Optional.empty()); } - return Optional.absent(); + return Optional.empty(); } final Entry userEntry = userCursor.get(); @@ -170,8 +170,8 @@ public Optional getUser(String user) { groups.add(cursor.get().get(configuration.getGroupNameAttribute()).getString()); } - Optional result = Optional.of(new SingularityUser(user, Optional.fromNullable(Strings.emptyToNull(userEntry.get(configuration.getUserNameAttribute()).getString())), - Optional.fromNullable(Strings.emptyToNull(userEntry.get(configuration.getUserEmailAttribute()).getString())), groups)); + Optional result = Optional.of(new SingularityUser(user, com.google.common.base.Optional.fromNullable(Strings.emptyToNull(userEntry.get(configuration.getUserNameAttribute()).getString())), + com.google.common.base.Optional.fromNullable(Strings.emptyToNull(userEntry.get(configuration.getUserEmailAttribute()).getString())), groups)); if (ldapCache.isPresent()) { ldapCache.get().put(user, result); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractHistoryResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractHistoryResource.java index 5ef3c36138..3d26842987 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractHistoryResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractHistoryResource.java @@ -21,14 +21,12 @@ public abstract class AbstractHistoryResource { protected final TaskManager taskManager; protected final DeployManager deployManager; protected final SingularityAuthorizationHelper authorizationHelper; - protected final Optional user; - public AbstractHistoryResource(HistoryManager historyManager, TaskManager taskManager, DeployManager deployManager, SingularityAuthorizationHelper authorizationHelper, Optional user) { + public AbstractHistoryResource(HistoryManager historyManager, TaskManager taskManager, DeployManager deployManager, SingularityAuthorizationHelper authorizationHelper) { this.historyManager = historyManager; this.taskManager = taskManager; this.deployManager = deployManager; this.authorizationHelper = authorizationHelper; - this.user = user; } protected SingularityTaskId getTaskIdObject(String taskId) { @@ -39,7 +37,7 @@ protected SingularityTaskId getTaskIdObject(String taskId) { } } - protected Optional getTaskHistory(SingularityTaskId taskId) { + protected Optional getTaskHistory(SingularityTaskId taskId, SingularityUser user) { authorizationHelper.checkForAuthorizationByRequestId(taskId.getRequestId(), user, SingularityAuthorizationScope.READ); Optional history = taskManager.getTaskHistory(taskId); @@ -51,15 +49,15 @@ protected Optional getTaskHistory(SingularityTaskId task return history; } - protected SingularityTaskHistory getTaskHistoryRequired(SingularityTaskId taskId) { - Optional history = getTaskHistory(taskId); + protected SingularityTaskHistory getTaskHistoryRequired(SingularityTaskId taskId, SingularityUser user) { + Optional history = getTaskHistory(taskId, user); checkNotFound(history.isPresent(), "No history for task %s", taskId); return history.get(); } - protected SingularityDeployHistory getDeployHistory(String requestId, String deployId) { + protected SingularityDeployHistory getDeployHistory(String requestId, String deployId, SingularityUser user) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); Optional deployHistory = deployManager.getDeployHistory(requestId, deployId, true); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractMachineResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractMachineResource.java index f6b562ef29..079d3f61a5 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractMachineResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractMachineResource.java @@ -24,29 +24,27 @@ public abstract class AbstractMachineResource> { protected final AbstractMachineManager manager; - protected final Optional user; protected final SingularityAuthorizationHelper authorizationHelper; private final SingularityValidator validator; - public AbstractMachineResource(AbstractMachineManager manager, SingularityAuthorizationHelper authorizationHelper, Optional user, SingularityValidator validator) { + public AbstractMachineResource(AbstractMachineManager manager, SingularityAuthorizationHelper authorizationHelper, SingularityValidator validator) { this.manager = manager; this.authorizationHelper = authorizationHelper; - this.user = user; this.validator = validator; } - protected void remove(String objectId) { + protected void remove(String objectId, SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); checkNotFound(manager.deleteObject(objectId) == SingularityDeleteResult.DELETED, "Couldn't find dead %s with id %s", getObjectTypeString(), objectId); } - protected void cancelExpiring(String objectId) { + protected void cancelExpiring(String objectId, SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); manager.deleteExpiringObject(objectId); } - protected List getExpiringStateChanges() { + protected List getExpiringStateChanges(SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return manager.getExpiringObjects(); } @@ -73,36 +71,36 @@ private void changeState(String objectId, MachineState newState, Optional decommissionRequest, Optional queryUser, SingularityAction action) { + protected void decommission(String objectId, Optional decommissionRequest, SingularityUser user, SingularityAction action) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(action); validator.validateExpiringMachineStateChange(decommissionRequest, MachineState.STARTING_DECOMMISSION, manager.getExpiringObject(objectId)); validator.validateDecommissioningCount(); - changeState(objectId, MachineState.STARTING_DECOMMISSION, decommissionRequest, queryUser); - saveExpiring(decommissionRequest, queryUser, objectId); + changeState(objectId, MachineState.STARTING_DECOMMISSION, decommissionRequest, user.getEmail()); + saveExpiring(decommissionRequest, user, objectId); } - protected void freeze(String objectId, Optional freezeRequest, Optional queryUser, SingularityAction action) { + protected void freeze(String objectId, Optional freezeRequest, SingularityUser user, SingularityAction action) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(action); validator.validateExpiringMachineStateChange(freezeRequest, MachineState.FROZEN, manager.getExpiringObject(objectId)); - changeState(objectId, MachineState.FROZEN, freezeRequest, queryUser); - saveExpiring(freezeRequest, queryUser, objectId); + changeState(objectId, MachineState.FROZEN, freezeRequest, user.getEmail()); + saveExpiring(freezeRequest, user, objectId); } - protected void activate(String objectId, Optional activateRequest, Optional queryUser, SingularityAction action) { + protected void activate(String objectId, Optional activateRequest, SingularityUser user, SingularityAction action) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(action); validator.validateExpiringMachineStateChange(activateRequest, MachineState.ACTIVE, manager.getExpiringObject(objectId)); - changeState(objectId, MachineState.ACTIVE, activateRequest, queryUser); - saveExpiring(activateRequest, queryUser, objectId); + changeState(objectId, MachineState.ACTIVE, activateRequest, user.getEmail()); + saveExpiring(activateRequest, user, objectId); } - private void saveExpiring(Optional changeRequest, Optional queryUser, String objectId) { + private void saveExpiring(Optional changeRequest, SingularityUser user, String objectId) { if (changeRequest.isPresent() && changeRequest.get().getDurationMillis().isPresent()) { manager.saveExpiringObject( new SingularityExpiringMachineState( - queryUser, + user.getEmail(), System.currentTimeMillis(), changeRequest.get().getActionId().or(UUID.randomUUID().toString()), changeRequest.get(), diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractRequestResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractRequestResource.java index 6fab114f00..12569c0465 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractRequestResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/AbstractRequestResource.java @@ -25,25 +25,23 @@ public class AbstractRequestResource extends AbstractLeaderAwareResource { protected final RequestManager requestManager; protected final DeployManager deployManager; - protected final Optional user; protected final SingularityValidator validator; protected final SingularityAuthorizationHelper authorizationHelper; - public AbstractRequestResource(RequestManager requestManager, DeployManager deployManager, Optional user, SingularityValidator validator, SingularityAuthorizationHelper authorizationHelper, + public AbstractRequestResource(RequestManager requestManager, DeployManager deployManager, SingularityValidator validator, SingularityAuthorizationHelper authorizationHelper, AsyncHttpClient httpClient, LeaderLatch leaderLatch, ObjectMapper objectMapper) { super(httpClient, leaderLatch, objectMapper); this.requestManager = requestManager; this.deployManager = deployManager; - this.user = user; this.validator = validator; this.authorizationHelper = authorizationHelper; } - protected SingularityRequestWithState fetchRequestWithState(String requestId) { - return fetchRequestWithState(requestId, false); + protected SingularityRequestWithState fetchRequestWithState(String requestId, SingularityUser user) { + return fetchRequestWithState(requestId, false, user); } - protected SingularityRequestWithState fetchRequestWithState(String requestId, boolean useWebCache) { + protected SingularityRequestWithState fetchRequestWithState(String requestId, boolean useWebCache, SingularityUser user) { Optional request = requestManager.getRequest(requestId, useWebCache); checkNotFound(request.isPresent(), "Couldn't find request with id %s", requestId); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java index 5bcfa778af..4cf20a85f3 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java @@ -13,7 +13,6 @@ import com.hubspot.singularity.SingularityAuthorizationScope; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.SingularityUserHolder; -import com.hubspot.singularity.SingularityUserSettings; import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; import com.hubspot.singularity.config.ApiPaths; @@ -21,22 +20,21 @@ import com.hubspot.singularity.data.UserManager; import com.wordnik.swagger.annotations.ApiOperation; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.AUTH_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) public class AuthResource { - private final Optional user; private final UserManager userManager; private final SingularityConfiguration configuration; private final SingularityAuthorizationHelper authorizationHelper; private final SingularityAuthDatastore authDatastore; @Inject - public AuthResource(Optional user, - UserManager userManager, + public AuthResource(UserManager userManager, SingularityConfiguration configuration, SingularityAuthorizationHelper authorizationHelper, SingularityAuthDatastore authDatastore) { - this.user = user; this.userManager = userManager; this.configuration = configuration; this.authorizationHelper = authorizationHelper; @@ -45,19 +43,20 @@ public AuthResource(Optional user, @GET @Path("/user") - public SingularityUserHolder getUser() { + public SingularityUserHolder getUser(@Auth SingularityUser user) { return new SingularityUserHolder( - user, - user.isPresent() ? userManager.getUserSettings(user.get().getId()) : Optional.absent(), - user.isPresent(), + Optional.of(user), + userManager.getUserSettings(user.getId()), + true, configuration.getAuthConfiguration().isEnabled()); } @GET @Path("/{requestId}/auth-check/{userId}") @ApiOperation("Check if the specified user is authorized for a request") - public Response checkReadOnlyAuth(@PathParam("requestId") String requestId, @PathParam("userId") String userId, @QueryParam("scope") Optional scope) { - authorizationHelper.checkForAuthorizationByRequestId(requestId, authDatastore.getUser(userId), scope.or(SingularityAuthorizationScope.READ)); + public Response checkReadOnlyAuth(@PathParam("requestId") String requestId, @PathParam("userId") String userId, + @QueryParam("scope") Optional scope) { + authorizationHelper.checkForAuthorizationByRequestId(requestId, authDatastore.getUser(userId).orElse(SingularityUser.defaultUser()), scope.or(SingularityAuthorizationScope.READ)); return Response.ok().build(); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/DeployResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/DeployResource.java index 6a2dcf2639..cacecc93f3 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/DeployResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/DeployResource.java @@ -23,7 +23,6 @@ import com.google.common.base.Optional; import com.google.inject.Inject; import com.hubspot.jackson.jaxrs.PropertyFiltering; -import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.DeployState; import com.hubspot.singularity.RequestState; import com.hubspot.singularity.SingularityAction; @@ -40,7 +39,6 @@ import com.hubspot.singularity.SingularityRequestDeployState; import com.hubspot.singularity.SingularityRequestParent; import com.hubspot.singularity.SingularityRequestWithState; -import com.hubspot.singularity.SingularityTaskId; import com.hubspot.singularity.SingularityTransformHelpers; import com.hubspot.singularity.SingularityUpdatePendingDeployRequest; import com.hubspot.singularity.SingularityUser; @@ -59,6 +57,8 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.DEPLOY_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity Deploys for existing requests", value=ApiPaths.DEPLOY_RESOURCE_PATH, position=2) @@ -67,10 +67,10 @@ public class DeployResource extends AbstractRequestResource { private final TaskManager taskManager; @Inject - public DeployResource(RequestManager requestManager, DeployManager deployManager, SingularityValidator validator, SingularityAuthorizationHelper authorizationHelper, Optional user, + public DeployResource(RequestManager requestManager, DeployManager deployManager, SingularityValidator validator, SingularityAuthorizationHelper authorizationHelper, SingularityConfiguration configuration, TaskManager taskManager, LeaderLatch leaderLatch, AsyncHttpClient httpClient, ObjectMapper objectMapper) { - super(requestManager, deployManager, user, validator, authorizationHelper, httpClient, leaderLatch, objectMapper); + super(requestManager, deployManager, validator, authorizationHelper, httpClient, leaderLatch, objectMapper); this.configuration = configuration; this.taskManager = taskManager; } @@ -79,7 +79,7 @@ public DeployResource(RequestManager requestManager, DeployManager deployManager @PropertyFiltering @Path("/pending") @ApiOperation(response=SingularityPendingDeploy.class, responseContainer="List", value="Retrieve the list of current pending deploys") - public Iterable getPendingDeploys() { + public Iterable getPendingDeploys(@Auth SingularityUser user) { return authorizationHelper.filterByAuthorizedRequests(user, deployManager.getPendingDeploys(), SingularityTransformHelpers.PENDING_DEPLOY_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -90,19 +90,19 @@ public Iterable getPendingDeploys() { @ApiResponse(code=400, message="Deploy object is invalid"), @ApiResponse(code=409, message="A current deploy is in progress. It may be canceled by calling DELETE"), }) - public SingularityRequestParent deploy(@Context HttpServletRequest requestContext, @ApiParam(required=true) SingularityDeployRequest deployRequest) { - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, deployRequest, () -> deploy(deployRequest)); + public SingularityRequestParent deploy(@Auth SingularityUser user, @Context HttpServletRequest requestContext, @ApiParam(required=true) SingularityDeployRequest deployRequest) { + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, deployRequest, () -> deploy(deployRequest, user)); } - public SingularityRequestParent deploy(SingularityDeployRequest deployRequest) { + public SingularityRequestParent deploy(SingularityDeployRequest deployRequest, SingularityUser user) { validator.checkActionEnabled(SingularityAction.DEPLOY); SingularityDeploy deploy = deployRequest.getDeploy(); checkNotNullBadRequest(deploy, "DeployRequest must have a deploy object"); - final Optional deployUser = JavaUtils.getUserEmail(user); + final Optional deployUser = user.getEmail(); final String requestId = checkNotNullBadRequest(deploy.getRequestId(), "DeployRequest must have a non-null requestId"); - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); @@ -140,7 +140,7 @@ public SingularityRequestParent deploy(SingularityDeployRequest deployRequest) { deploy.getDeployStepWaitTimeMs().or(configuration.getDefaultDeployStepWaitTimeMs()), false, deploy.getAutoAdvanceDeploySteps().or(true), - Collections.emptySet(), + Collections.emptySet(), System.currentTimeMillis())); } @@ -154,7 +154,7 @@ public SingularityRequestParent deploy(SingularityDeployRequest deployRequest) { boolean deployAlreadyInProgress = deployManager.createPendingDeploy(pendingDeployObj) == SingularityCreateResult.EXISTED; if (deployAlreadyInProgress && deployToUnpause) { - requestManager.pause(request, now, deployUser, Optional.absent()); + requestManager.pause(request, now, deployUser, Optional.absent()); } checkConflict(!deployAlreadyInProgress, @@ -177,9 +177,10 @@ public SingularityRequestParent deploy(SingularityDeployRequest deployRequest) { @ApiResponse(code=400, message="Deploy is not in the pending state pending or is not not present"), }) public SingularityRequestParent cancelDeploy( + @Auth SingularityUser user, @ApiParam(required=true, value="The Singularity Request Id from which the deployment is removed.") @PathParam("requestId") String requestId, @ApiParam(required=true, value="The Singularity Deploy Id that should be removed.") @PathParam("deployId") String deployId) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); validator.checkActionEnabled(SingularityAction.CANCEL_DEPLOY); @@ -189,7 +190,7 @@ public SingularityRequestParent cancelDeploy( checkBadRequest(deployState.isPresent() && deployState.get().getPendingDeploy().isPresent() && deployState.get().getPendingDeploy().get().getDeployId().equals(deployId), "Request %s does not have a pending deploy %s", requestId, deployId); - deployManager.createCancelDeployRequest(new SingularityDeployMarker(requestId, deployId, System.currentTimeMillis(), JavaUtils.getUserEmail(user), Optional. absent())); + deployManager.createCancelDeployRequest(new SingularityDeployMarker(requestId, deployId, System.currentTimeMillis(), user.getEmail(), Optional. absent())); return fillEntireRequest(requestWithState); } @@ -200,8 +201,9 @@ public SingularityRequestParent cancelDeploy( @ApiResponses({ @ApiResponse(code=400, message="Deploy is not in the pending state pending or is not not present") }) - public SingularityRequestParent updatePendingDeploy(@ApiParam(required=true) SingularityUpdatePendingDeployRequest updateRequest) { - SingularityRequestWithState requestWithState = fetchRequestWithState(updateRequest.getRequestId()); + public SingularityRequestParent updatePendingDeploy(@Auth SingularityUser user, + @ApiParam(required=true) SingularityUpdatePendingDeployRequest updateRequest) { + SingularityRequestWithState requestWithState = fetchRequestWithState(updateRequest.getRequestId(), user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/DisastersResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/DisastersResource.java index ba04e0d41a..e93cb5245c 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/DisastersResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/DisastersResource.java @@ -3,7 +3,6 @@ import java.util.Collections; import java.util.List; -import javax.servlet.http.HttpServletRequest; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; @@ -11,7 +10,6 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import com.google.common.base.Optional; @@ -29,26 +27,26 @@ import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiOperation; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.DISASTERS_RESOURCE_PATH) @Produces(MediaType.APPLICATION_JSON) @Api(description="Manages Singularity Deploys for existing requests", value=ApiPaths.DISASTERS_RESOURCE_PATH) public class DisastersResource { private final DisasterManager disasterManager; private final SingularityAuthorizationHelper authorizationHelper; - private final Optional user; + @Inject public DisastersResource(DisasterManager disasterManager, - SingularityAuthorizationHelper authorizationHelper, - Optional user) { + SingularityAuthorizationHelper authorizationHelper) { this.disasterManager = disasterManager; this.authorizationHelper = authorizationHelper; - this.user = user; } @GET @Path("/stats") @ApiOperation(value="Get current data related to disaster detection", response=SingularityDisastersData.class) - public SingularityDisastersData disasterStats() { + public SingularityDisastersData disasterStats(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return disasterManager.getDisastersData(); } @@ -56,7 +54,7 @@ public SingularityDisastersData disasterStats() { @GET @Path("/active") @ApiOperation(value="Get a list of current active disasters") - public List activeDisasters() { + public List activeDisasters(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return disasterManager.getActiveDisasters(); } @@ -64,7 +62,7 @@ public List activeDisasters() { @POST @Path("/disable") @ApiOperation(value="Do not allow the automated poller to disable actions when a disaster is detected") - public void disableAutomatedDisasterCreation() { + public void disableAutomatedDisasterCreation(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); disasterManager.disableAutomatedDisabledActions(); } @@ -72,7 +70,7 @@ public void disableAutomatedDisasterCreation() { @POST @Path("/enable") @ApiOperation(value="Allow the automated poller to disable actions when a disaster is detected") - public void enableAutomatedDisasterCreation() { + public void enableAutomatedDisasterCreation(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); disasterManager.enableAutomatedDisabledActions(); } @@ -80,7 +78,7 @@ public void enableAutomatedDisasterCreation() { @DELETE @Path("/active/{type}") @ApiOperation(value="Remove an active disaster (make it inactive)") - public void removeDisaster(@PathParam("type") SingularityDisasterType type) { + public void removeDisaster(@Auth SingularityUser user, @PathParam("type") SingularityDisasterType type) { authorizationHelper.checkAdminAuthorization(user); disasterManager.removeDisaster(type); } @@ -88,7 +86,7 @@ public void removeDisaster(@PathParam("type") SingularityDisasterType type) { @POST @Path("/active/{type}") @ApiOperation(value="Create a new active disaster") - public void newDisaster(@PathParam("type") SingularityDisasterType type) { + public void newDisaster(@Auth SingularityUser user, @PathParam("type") SingularityDisasterType type) { authorizationHelper.checkAdminAuthorization(user); disasterManager.addDisaster(type); disasterManager.addDisabledActionsForDisasters(Collections.singletonList(type)); @@ -97,7 +95,7 @@ public void newDisaster(@PathParam("type") SingularityDisasterType type) { @GET @Path("/disabled-actions") @ApiOperation(value="Get a list of actions that are currently disable") - public List disabledActions() { + public List disabledActions(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return disasterManager.getDisabledActions(); } @@ -105,17 +103,17 @@ public List disabledActions() { @POST @Path("/disabled-actions/{action}") @ApiOperation(value="Disable a specific action") - public void disableAction(@PathParam("action") SingularityAction action, SingularityDisabledActionRequest disabledActionRequest) { + public void disableAction(@Auth SingularityUser user, @PathParam("action") SingularityAction action, SingularityDisabledActionRequest disabledActionRequest) { final Optional maybeRequest = Optional.fromNullable(disabledActionRequest); authorizationHelper.checkAdminAuthorization(user); Optional message = maybeRequest.isPresent() ? maybeRequest.get().getMessage() : Optional.absent(); - disasterManager.disable(action, message, user, false, Optional.absent()); + disasterManager.disable(action, message, Optional.of(user), false, Optional.absent()); } @DELETE @Path("/disabled-actions/{action}") @ApiOperation(value="Re-enable a specific action if it has been disabled") - public void enableAction(@PathParam("action") SingularityAction action) { + public void enableAction(@Auth SingularityUser user, @PathParam("action") SingularityAction action) { authorizationHelper.checkAdminAuthorization(user); disasterManager.enable(action); } @@ -123,7 +121,7 @@ public void enableAction(@PathParam("action") SingularityAction action) { @POST @Path("/task-credits") @ApiOperation(value="Add task credits, enables task credit system if not already enabled") - public void addTaskCredits(@QueryParam("credits") Optional credits) throws Exception { + public void addTaskCredits(@Auth SingularityUser user, @QueryParam("credits") Optional credits) throws Exception { authorizationHelper.checkAdminAuthorization(user); disasterManager.enableTaskCredits(); disasterManager.enqueueCreditsChange(credits.or(0)); @@ -132,7 +130,7 @@ public void addTaskCredits(@QueryParam("credits") Optional credits) thr @DELETE @Path("/task-credits") @ApiOperation(value="Disable task credit system") - public void disableTaskCredits(@Context HttpServletRequest request) throws Exception { + public void disableTaskCredits(@Auth SingularityUser user) throws Exception { authorizationHelper.checkAdminAuthorization(user); disasterManager.disableTaskCredits(); } @@ -140,7 +138,7 @@ public void disableTaskCredits(@Context HttpServletRequest request) throws Excep @GET @Path("/task-credits") @ApiOperation(value="Get task credit data") - public SingularityTaskCredits getTaskCreditData() throws Exception { + public SingularityTaskCredits getTaskCreditData(@Auth SingularityUser user) throws Exception { authorizationHelper.checkAdminAuthorization(user); return new SingularityTaskCredits(disasterManager.isTaskCreditEnabled(), disasterManager.getTaskCredits()); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java index 4c31fb6778..a8fdd88826 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java @@ -42,6 +42,8 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.HISTORY_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description = "Manages historical data for tasks, requests, and deploys.", value = ApiPaths.HISTORY_RESOURCE_PATH) @@ -55,8 +57,8 @@ public class HistoryResource extends AbstractHistoryResource { @Inject public HistoryResource(HistoryManager historyManager, TaskManager taskManager, DeployManager deployManager, DeployHistoryHelper deployHistoryHelper, TaskHistoryHelper taskHistoryHelper, - RequestHistoryHelper requestHistoryHelper, SingularityAuthorizationHelper authorizationHelper, Optional user, DeployTaskHistoryHelper deployTaskHistoryHelper) { - super(historyManager, taskManager, deployManager, authorizationHelper, user); + RequestHistoryHelper requestHistoryHelper, SingularityAuthorizationHelper authorizationHelper, DeployTaskHistoryHelper deployTaskHistoryHelper) { + super(historyManager, taskManager, deployManager, authorizationHelper); this.requestHistoryHelper = requestHistoryHelper; this.deployHistoryHelper = deployHistoryHelper; @@ -67,10 +69,11 @@ public HistoryResource(HistoryManager historyManager, TaskManager taskManager, D @GET @Path("/task/{taskId}") @ApiOperation("Retrieve the history for a specific task.") - public SingularityTaskHistory getHistoryForTask(@ApiParam("Task ID to look up") @PathParam("taskId") String taskId) { + public SingularityTaskHistory getHistoryForTask(@Auth SingularityUser user, + @ApiParam("Task ID to look up") @PathParam("taskId") String taskId) { SingularityTaskId taskIdObj = getTaskIdObject(taskId); - return getTaskHistoryRequired(taskIdObj); + return getTaskHistoryRequired(taskIdObj, user); } private Integer getLimitCount(Integer countParam) { @@ -107,8 +110,8 @@ private Optional getPageCount(Optional dataCount, Integer coun @GET @Path("/request/{requestId}/tasks/active") @ApiOperation("Retrieve the history for all active tasks of a specific request.") - public List getTaskHistoryForRequest( - @ApiParam("Request ID to look up") @PathParam("requestId") String requestId) { + public List getTaskHistoryForRequest(@Auth SingularityUser user, + @ApiParam("Request ID to look up") @PathParam("requestId") String requestId) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); List activeTaskIds = taskManager.getActiveTaskIdsForRequest(requestId); @@ -118,15 +121,17 @@ public List getTaskHistoryForRequest( @GET @Path("/request/{requestId}/deploy/{deployId}") @ApiOperation("Retrieve the history for a specific deploy.") - public SingularityDeployHistory getDeploy(@ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, - @ApiParam("Deploy ID") @PathParam("deployId") String deployId) { - return getDeployHistory(requestId, deployId); + public SingularityDeployHistory getDeploy(@Auth SingularityUser user, + @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, + @ApiParam("Deploy ID") @PathParam("deployId") String deployId) { + return getDeployHistory(requestId, deployId, user); } @GET @Path("/request/{requestId}/deploy/{deployId}/tasks/active") @ApiOperation("Retrieve the task history for a specific deploy.") public List getActiveDeployTasks( + @Auth SingularityUser user, @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, @ApiParam("Deploy ID") @PathParam("deployId") String deployId) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); @@ -137,7 +142,7 @@ public List getActiveDeployTasks( @GET @Path("/request/{requestId}/deploy/{deployId}/tasks/inactive") @ApiOperation("Retrieve the task history for a specific deploy.") - public List getInactiveDeployTasks( + public List getInactiveDeployTasks(@Auth SingularityUser user, @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, @ApiParam("Deploy ID") @PathParam("deployId") String deployId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @@ -155,6 +160,7 @@ public List getInactiveDeployTasks( @Path("/request/{requestId}/deploy/{deployId}/tasks/inactive/withmetadata") @ApiOperation("Retrieve the task history for a specific deploy.") public SingularityPaginatedResponse getInactiveDeployTasksWithMetadata( + @Auth SingularityUser user, @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, @ApiParam("Deploy ID") @PathParam("deployId") String deployId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @@ -176,6 +182,7 @@ public SingularityPaginatedResponse getInactiveDeployT @Path("/tasks") @ApiOperation("Retrieve the history sorted by startedAt for all inactive tasks.") public List getTaskHistory( + @Auth SingularityUser user, @ApiParam("Optional Request ID to match") @QueryParam("requestId") Optional requestId, @ApiParam("Optional deploy ID to match") @QueryParam("deployId") Optional deployId, @ApiParam("Optional runId to match") @QueryParam("runId") Optional runId, @@ -205,6 +212,7 @@ public List getTaskHistory( @Path("/tasks/withmetadata") @ApiOperation("Retrieve the history sorted by startedAt for all inactive tasks.") public SingularityPaginatedResponse getTaskHistoryWithMetadata( + @Auth SingularityUser user, @ApiParam("Optional Request ID to match") @QueryParam("requestId") Optional requestId, @ApiParam("Optional deploy ID to match") @QueryParam("deployId") Optional deployId, @ApiParam("Optional runId to match") @QueryParam("runId") Optional runId, @@ -225,7 +233,7 @@ public SingularityPaginatedResponse getTaskHistoryWith final Optional dataCount = taskHistoryHelper.getBlendedHistoryCount(new SingularityTaskHistoryQuery(requestId, deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection)); final int limitCount = getLimitCount(count); - final List data = this.getTaskHistory(requestId, deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection, count, page); + final List data = this.getTaskHistory(user, requestId, deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection, count, page); final Optional pageCount = getPageCount(dataCount, limitCount); return new SingularityPaginatedResponse<>(dataCount, pageCount, Optional.fromNullable(page), data); @@ -235,6 +243,7 @@ public SingularityPaginatedResponse getTaskHistoryWith @Path("/request/{requestId}/tasks") @ApiOperation("Retrieve the history sorted by startedAt for all inactive tasks of a specific request.") public List getTaskHistoryForRequest( + @Auth SingularityUser user, @ApiParam("Request ID to match") @PathParam("requestId") String requestId, @ApiParam("Optional deploy ID to match") @QueryParam("deployId") Optional deployId, @ApiParam("Optional runId to match") @QueryParam("runId") Optional runId, @@ -260,6 +269,7 @@ public List getTaskHistoryForRequest( @Path("/request/{requestId}/tasks/withmetadata") @ApiOperation("Retrieve the history count for all inactive tasks of a specific request.") public SingularityPaginatedResponse getTaskHistoryForRequestWithMetadata( + @Auth SingularityUser user, @ApiParam("Request ID to match") @PathParam("requestId") String requestId, @ApiParam("Optional deploy ID to match") @QueryParam("deployId") Optional deployId, @ApiParam("Optional runId to match") @QueryParam("runId") Optional runId, @@ -276,7 +286,7 @@ public SingularityPaginatedResponse getTaskHistoryForR final Optional dataCount = taskHistoryHelper.getBlendedHistoryCount(new SingularityTaskHistoryQuery(Optional.of(requestId), deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection)); final int limitCount = getLimitCount(count); - final List data = this.getTaskHistoryForRequest(requestId, deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection, count, page); + final List data = this.getTaskHistoryForRequest(user, requestId, deployId, runId, host, lastTaskStatus, startedBefore, startedAfter, updatedBefore, updatedAfter, orderDirection, count, page); final Optional pageCount = getPageCount(dataCount, limitCount); return new SingularityPaginatedResponse<>(dataCount, pageCount, Optional.fromNullable(page), data); @@ -286,6 +296,7 @@ public SingularityPaginatedResponse getTaskHistoryForR @Path("/request/{requestId}/run/{runId}") @ApiOperation("Retrieve the history for a task by runId") public Optional getTaskHistoryForRequestAndRunId( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("runId to look up") @PathParam("runId") String runId) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); @@ -297,6 +308,7 @@ public Optional getTaskHistoryForRequestAndRunId( @Path("/request/{requestId}/deploys") @ApiOperation("Get deploy history for a single request") public List getDeploys( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @ApiParam("Which page of items to view") @QueryParam("page") Integer page) { @@ -312,6 +324,7 @@ public List getDeploys( @Path("/request/{requestId}/deploys/withmetadata") @ApiOperation("Get deploy history with metadata for a single request") public SingularityPaginatedResponse getDeploysWithMetadata( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @ApiParam("Which page of items to view") @QueryParam("page") Integer page) { @@ -319,7 +332,7 @@ public SingularityPaginatedResponse getDeploysWithMeta final Optional dataCount = deployHistoryHelper.getBlendedHistoryCount(requestId); final int limitCount = getLimitCount(count); - final List data = this.getDeploys(requestId, count, page); + final List data = this.getDeploys(user, requestId, count, page); final Optional pageCount = getPageCount(dataCount, limitCount); return new SingularityPaginatedResponse<>(dataCount, pageCount, Optional.fromNullable(page), data); @@ -329,6 +342,7 @@ public SingularityPaginatedResponse getDeploysWithMeta @Path("/request/{requestId}/requests") @ApiOperation("Get request history for a single request") public List getRequestHistoryForRequest( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @ApiParam("Which page of items to view") @QueryParam("page") Integer page) { @@ -344,6 +358,7 @@ public List getRequestHistoryForRequest( @Path("/request/{requestId}/requests/withmetadata") @ApiOperation("Get request history for a single request") public SingularityPaginatedResponse getRequestHistoryForRequestWithMetadata( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @ApiParam("Which page of items to view") @QueryParam("page") Integer page) { @@ -362,6 +377,7 @@ public SingularityPaginatedResponse getRequestHistory @Path("/requests/search") @ApiOperation("Search for requests.") public Iterable getRequestHistoryForRequestLike( + @Auth SingularityUser user, @ApiParam("Request ID prefix to search for") @QueryParam("requestIdLike") String requestIdLike, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, @ApiParam("Which page of items to view") @QueryParam("page") Integer page, @@ -378,6 +394,7 @@ public Iterable getRequestHistoryForRequestLike( @Path("/request/{requestId}/command-line-args") @ApiOperation("Get a list of recently used command line args for an on-demand or scheduled request") public Set> getRecentCommandLineArgs( + @Auth SingularityUser user, @ApiParam("Request ID to look up") @PathParam("requestId") String requestId, @ApiParam("Max number of recent args to return") @QueryParam("count") Optional count) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/InactiveSlaveResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/InactiveSlaveResource.java index 422e36f6d3..878a41ff69 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/InactiveSlaveResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/InactiveSlaveResource.java @@ -10,7 +10,6 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; -import com.google.common.base.Optional; import com.google.inject.Inject; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.auth.SingularityAuthorizationHelper; @@ -18,22 +17,21 @@ import com.hubspot.singularity.data.InactiveSlaveManager; import com.wordnik.swagger.annotations.Api; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.INACTIVE_SLAVES_RESOURCE_PATH) @Produces(MediaType.APPLICATION_JSON) @Api(description="Manages Singularity Deploys for existing requests", value=ApiPaths.INACTIVE_SLAVES_RESOURCE_PATH) public class InactiveSlaveResource { private final InactiveSlaveManager inactiveSlaveManager; private final SingularityAuthorizationHelper authorizationHelper; - private final Optional user; @Inject public InactiveSlaveResource(InactiveSlaveManager inactiveSlaveManager, - SingularityAuthorizationHelper authorizationHelper, - Optional user) { + SingularityAuthorizationHelper authorizationHelper) { this.inactiveSlaveManager = inactiveSlaveManager; this.authorizationHelper = authorizationHelper; - this.user = user; } @GET @@ -42,13 +40,15 @@ public Set getInactiveSlaves() { } @POST - public void deactivateSlave(@QueryParam("host") String host) { + public void deactivateSlave(@Auth SingularityUser user, + @QueryParam("host") String host) { authorizationHelper.checkAdminAuthorization(user); inactiveSlaveManager.deactivateSlave(host); } @DELETE - public void reactivateSlave(@QueryParam("host") String host) { + public void reactivateSlave(@Auth SingularityUser user, + @QueryParam("host") String host) { authorizationHelper.checkAdminAuthorization(user); inactiveSlaveManager.activateSlave(host); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java index 1280324cf6..bd7a58be34 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java @@ -11,7 +11,6 @@ import com.google.common.base.Optional; import com.google.inject.Inject; -import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.SingularityDeleteResult; import com.hubspot.singularity.SingularityPriorityFreezeParent; import com.hubspot.singularity.SingularityUser; @@ -25,18 +24,18 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.PRIORITY_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages whether or not to schedule tasks based on their priority levels.", value=ApiPaths.PRIORITY_RESOURCE_PATH ) public class PriorityResource { - private final Optional user; private final SingularityAuthorizationHelper authorizationHelper; private final SingularityValidator singularityValidator; private final PriorityManager priorityManager; @Inject - public PriorityResource(Optional user, SingularityAuthorizationHelper authorizationHelper, SingularityValidator singularityValidator, PriorityManager priorityManager) { - this.user = user; + public PriorityResource(SingularityAuthorizationHelper authorizationHelper, SingularityValidator singularityValidator, PriorityManager priorityManager) { this.authorizationHelper = authorizationHelper; this.singularityValidator = singularityValidator; this.priorityManager = priorityManager; @@ -60,7 +59,7 @@ public Optional getActivePriorityFreeze() { @ApiResponse(code=202, message="The active priority freeze was deleted."), @ApiResponse(code=400, message="There was no active priority freeze to delete.") }) - public void deleteActivePriorityFreeze() { + public void deleteActivePriorityFreeze(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); final SingularityDeleteResult deleteResult = priorityManager.deleteActivePriorityFreeze(); @@ -77,11 +76,11 @@ public void deleteActivePriorityFreeze() { @ApiResponse(code=200, message="The priority freeze request was accepted."), @ApiResponse(code=400, message="There was a validation error with the priority freeze request.") }) - public SingularityPriorityFreezeParent createPriorityFreeze(SingularityPriorityFreeze priorityFreezeRequest) { + public SingularityPriorityFreezeParent createPriorityFreeze(@Auth SingularityUser user, SingularityPriorityFreeze priorityFreezeRequest) { authorizationHelper.checkAdminAuthorization(user); priorityFreezeRequest = singularityValidator.checkSingularityPriorityFreeze(priorityFreezeRequest); - final SingularityPriorityFreezeParent priorityFreezeRequestParent = new SingularityPriorityFreezeParent(priorityFreezeRequest, System.currentTimeMillis(), JavaUtils.getUserEmail(user)); + final SingularityPriorityFreezeParent priorityFreezeRequestParent = new SingularityPriorityFreezeParent(priorityFreezeRequest, System.currentTimeMillis(), user.getEmail()); priorityManager.createPriorityFreeze(priorityFreezeRequestParent); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java index 81d6eb37f5..c50a0801fe 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java @@ -13,7 +13,6 @@ import com.google.common.base.Optional; import com.google.inject.Inject; -import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.MachineState; import com.hubspot.singularity.SingularityAction; import com.hubspot.singularity.SingularityMachineStateHistoryUpdate; @@ -29,14 +28,16 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.RACK_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api( description="Manages Singularity racks.", value=ApiPaths.RACK_RESOURCE_PATH ) public class RackResource extends AbstractMachineResource { @Inject - public RackResource(RackManager rackManager, SingularityAuthorizationHelper authorizationHelper, Optional user, SingularityValidator validator) { - super(rackManager, authorizationHelper, user, validator); + public RackResource(RackManager rackManager, SingularityAuthorizationHelper authorizationHelper, SingularityValidator validator) { + super(rackManager, authorizationHelper, validator); } @Override @@ -61,46 +62,46 @@ public List getRackHistory(@ApiParam("Rack @DELETE @Path("/rack/{rackId}") @ApiOperation("Remove a known rack, erasing history. This operation will cancel decommissioning of racks") - public void removeRack(@ApiParam("Rack ID") @PathParam("rackId") String rackId) { - super.remove(rackId); + public void removeRack(@Auth SingularityUser user, @ApiParam("Rack ID") @PathParam("rackId") String rackId) { + super.remove(rackId, user); } @POST @Path("/rack/{rackId}/decommission") @ApiOperation("Begin decommissioning a specific active rack") - public void decommissionRack(@ApiParam("Active rack ID") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { + public void decommissionRack(@Auth SingularityUser user, @ApiParam("Active rack ID") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.decommission(rackId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.DECOMMISSION_RACK); + super.decommission(rackId, maybeChangeRequest, user, SingularityAction.DECOMMISSION_RACK); } @POST @Path("/rack/{rackId}/freeze") @ApiOperation("Freeze a specific rack") - public void freezeRack(@ApiParam("Rack ID") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { + public void freezeRack(@Auth SingularityUser user, @ApiParam("Rack ID") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.freeze(rackId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.FREEZE_RACK); + super.freeze(rackId, maybeChangeRequest, user, SingularityAction.FREEZE_RACK); } @POST @Path("/rack/{rackId}/activate") @ApiOperation("Activate a decomissioning rack, canceling decomission without erasing history") - public void activateRack(@ApiParam("Active rackId") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { + public void activateRack(@Auth SingularityUser user, @ApiParam("Active rackId") @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.activate(rackId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.ACTIVATE_RACK); + super.activate(rackId, maybeChangeRequest, user, SingularityAction.ACTIVATE_RACK); } @DELETE @Path("/rack/{rackId}/expiring") @ApiOperation("Delete any expiring machine state changes for this rack") - public void deleteExpiringStateChange(@ApiParam("Active slaveId") @PathParam("rackId") String rackId) { - super.cancelExpiring(rackId); + public void deleteExpiringStateChange(@Auth SingularityUser user, @ApiParam("Active slaveId") @PathParam("rackId") String rackId) { + super.cancelExpiring(rackId, user); } @GET @Path("/expiring") @ApiOperation("Get all expiring state changes for all racks") - public List getExpiringStateChanges() { - return super.getExpiringStateChanges(); + public List getExpiringStateChanges(@Auth SingularityUser user) { + return super.getExpiringStateChanges(user); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestResource.java index 3e37638c5d..51d63ec8c5 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestResource.java @@ -33,7 +33,6 @@ import com.google.common.collect.Lists; import com.google.inject.Inject; import com.hubspot.jackson.jaxrs.PropertyFiltering; -import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.MachineState; import com.hubspot.singularity.RequestCleanupType; import com.hubspot.singularity.RequestState; @@ -87,6 +86,8 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.REQUEST_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity Requests, the parent object for any deployed task", value=ApiPaths.REQUEST_RESOURCE_PATH, position=1) @@ -101,9 +102,9 @@ public class RequestResource extends AbstractRequestResource { @Inject public RequestResource(SingularityValidator validator, DeployManager deployManager, TaskManager taskManager, RequestManager requestManager, SingularityMailer mailer, - SingularityAuthorizationHelper authorizationHelper, Optional user, RequestHelper requestHelper, LeaderLatch leaderLatch, + SingularityAuthorizationHelper authorizationHelper, RequestHelper requestHelper, LeaderLatch leaderLatch, SlaveManager slaveManager, DisasterManager disasterManager, AsyncHttpClient httpClient, ObjectMapper objectMapper) { - super(requestManager, deployManager, user, validator, authorizationHelper, httpClient, leaderLatch, objectMapper); + super(requestManager, deployManager, validator, authorizationHelper, httpClient, leaderLatch, objectMapper); this.mailer = mailer; this.taskManager = taskManager; this.requestHelper = requestHelper; @@ -112,7 +113,7 @@ public RequestResource(SingularityValidator validator, DeployManager deployManag } private void submitRequest(SingularityRequest request, Optional oldRequestWithState, Optional historyType, - Optional skipHealthchecks, Optional message, Optional maybeBounceRequest) { + Optional skipHealthchecks, Optional message, Optional maybeBounceRequest, SingularityUser user) { checkNotNullBadRequest(request.getId(), "Request must have an id"); checkConflict(!requestManager.cleanupRequestExists(request.getId()), "Request %s is currently cleaning. Try again after a few moments", request.getId()); @@ -147,7 +148,7 @@ private void submitRequest(SingularityRequest request, Optional postRequest(request)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, request, () -> postRequest(request, user)); } - public SingularityRequestParent postRequest(SingularityRequest request) { - submitRequest(request, requestManager.getRequest(request.getId()), Optional. absent(), Optional. absent(), Optional. absent(), Optional.absent()); - return fillEntireRequest(fetchRequestWithState(request.getId())); + public SingularityRequestParent postRequest(SingularityRequest request, SingularityUser user) { + submitRequest(request, requestManager.getRequest(request.getId()), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), user); + return fillEntireRequest(fetchRequestWithState(request.getId(), user)); } private String getAndCheckDeployId(String requestId) { @@ -177,9 +179,10 @@ private String getAndCheckDeployId(String requestId) { @POST @Path("/request/{requestId}/bounce") - public SingularityRequestParent bounce(@PathParam("requestId") String requestId, + public SingularityRequestParent bounce(@Auth SingularityUser user, + @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext) { - return bounce(requestId, requestContext, null); + return bounce(user, requestId, requestContext, null); } @POST @@ -187,15 +190,16 @@ public SingularityRequestParent bounce(@PathParam("requestId") String requestId, @Consumes({ MediaType.APPLICATION_JSON }) @ApiOperation(value="Bounce a specific Singularity request. A bounce launches replacement task(s), and then kills the original task(s) if the replacement(s) are healthy.", response=SingularityRequestParent.class) - public SingularityRequestParent bounce(@ApiParam("The request ID to bounce") @PathParam("requestId") String requestId, + public SingularityRequestParent bounce(@Auth SingularityUser user, + @ApiParam("The request ID to bounce") @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext, @ApiParam("Bounce request options") SingularityBounceRequest bounceRequest) { final Optional maybeBounceRequest = Optional.fromNullable(bounceRequest); - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeBounceRequest.orNull(), () -> bounce(requestId, maybeBounceRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeBounceRequest.orNull(), () -> bounce(requestId, maybeBounceRequest, user)); } - public SingularityRequestParent bounce(String requestId, Optional bounceRequest) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + public SingularityRequestParent bounce(String requestId, Optional bounceRequest, SingularityUser user) { + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); validator.checkActionEnabled(SingularityAction.BOUNCE_REQUEST); @@ -233,14 +237,14 @@ public SingularityRequestParent bounce(String requestId, Optional absent(), Optional.absent(), requestId, Optional.of(deployId), skipHealthchecks, message, actionId, runBeforeKill)); + new SingularityRequestCleanup(user.getEmail(), isIncrementalBounce ? RequestCleanupType.INCREMENTAL_BOUNCE : RequestCleanupType.BOUNCE, + System.currentTimeMillis(), Optional.absent(), Optional.absent(), requestId, Optional.of(deployId), skipHealthchecks, message, actionId, runBeforeKill)); - requestManager.bounce(requestWithState.getRequest(), System.currentTimeMillis(), JavaUtils.getUserEmail(user), message); + requestManager.bounce(requestWithState.getRequest(), System.currentTimeMillis(), Optional.of(user.getId()), message); final SingularityBounceRequest validatedBounceRequest = validator.checkBounceRequest(bounceRequest.or(SingularityBounceRequest.defaultRequest())); - requestManager.saveExpiringObject(new SingularityExpiringBounce(requestId, deployId, JavaUtils.getUserEmail(user), + requestManager.saveExpiringObject(new SingularityExpiringBounce(requestId, deployId, Optional.of(user.getId()), System.currentTimeMillis(), validatedBounceRequest, actionId.get())); return fillEntireRequest(requestWithState); @@ -248,8 +252,8 @@ public SingularityRequestParent bounce(String requestId, Optional maybeRunNowRequest = Optional.fromNullable(runNowRequest); - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); @@ -271,7 +276,7 @@ public SingularityPendingRequestParent scheduleImmediately(@ApiParam("The reques final SingularityPendingRequest pendingRequest = validator.checkRunNowRequest( getAndCheckDeployId(requestId), - JavaUtils.getUserEmail(user), + user.getEmail(), requestWithState.getRequest(), maybeRunNowRequest, taskManager.getActiveTaskIdsForRequest(requestId), @@ -287,17 +292,18 @@ public SingularityPendingRequestParent scheduleImmediately(@ApiParam("The reques @GET @Path("/request/{requestId}/run/{runId}") @ApiOperation("Retrieve an active task by runId") - public Optional getTaskByRunId(@PathParam("requestId") String requestId, @PathParam("runId") String runId) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + public Optional getTaskByRunId(@Auth SingularityUser user, @PathParam("requestId") String requestId, @PathParam("runId") String runId) { + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.READ); return taskManager.getTaskByRunId(requestId, runId); } @POST @Path("/request/{requestId}/pause") - public SingularityRequestParent pause(@PathParam("requestId") String requestId, + public SingularityRequestParent pause(@Auth SingularityUser user, + @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext) { - return pause(requestId, requestContext, null); + return pause(user, requestId, requestContext, null); } @POST @@ -307,16 +313,17 @@ public SingularityRequestParent pause(@PathParam("requestId") String requestId, @ApiResponses({ @ApiResponse(code=409, message="Request is already paused or being cleaned"), }) - public SingularityRequestParent pause(@ApiParam("The request ID to pause") @PathParam("requestId") String requestId, + public SingularityRequestParent pause(@Auth SingularityUser user, + @ApiParam("The request ID to pause") @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext, @ApiParam("Pause Request Options") SingularityPauseRequest pauseRequest) { final Optional maybePauseRequest = Optional.fromNullable(pauseRequest); - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybePauseRequest.orNull(), () -> pause(requestId, maybePauseRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybePauseRequest.orNull(), () -> pause(requestId, maybePauseRequest, user)); } - public SingularityRequestParent pause(String requestId, Optional pauseRequest) { + public SingularityRequestParent pause(String requestId, Optional pauseRequest, SingularityUser user) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); @@ -343,17 +350,17 @@ public SingularityRequestParent pause(String requestId, Optional removeFromLoadBalancer = Optional.absent(); - SingularityCreateResult result = requestManager.createCleanupRequest(new SingularityRequestCleanup(JavaUtils.getUserEmail(user), + SingularityCreateResult result = requestManager.createCleanupRequest(new SingularityRequestCleanup(user.getEmail(), RequestCleanupType.PAUSING, now, killTasks, removeFromLoadBalancer, requestId, Optional. absent(), Optional. absent(), message, actionId, runBeforeKill)); checkConflict(result == SingularityCreateResult.CREATED, "%s is already pausing - try again soon", requestId, result); - mailer.sendRequestPausedMail(requestWithState.getRequest(), pauseRequest, JavaUtils.getUserEmail(user)); + mailer.sendRequestPausedMail(requestWithState.getRequest(), pauseRequest, user.getEmail()); - requestManager.pause(requestWithState.getRequest(), now, JavaUtils.getUserEmail(user), message); + requestManager.pause(requestWithState.getRequest(), now, user.getEmail(), message); if (pauseRequest.isPresent() && pauseRequest.get().getDurationMillis().isPresent()) { - requestManager.saveExpiringObject(new SingularityExpiringPause(requestId, JavaUtils.getUserEmail(user), + requestManager.saveExpiringObject(new SingularityExpiringPause(requestId, user.getEmail(), System.currentTimeMillis(), pauseRequest.get(), actionId.get())); } @@ -362,9 +369,10 @@ public SingularityRequestParent pause(String requestId, Optional maybeUnpauseRequest = Optional.fromNullable(unpauseRequest); - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeUnpauseRequest.orNull(), () -> unpause(requestId, maybeUnpauseRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeUnpauseRequest.orNull(), () -> unpause(requestId, maybeUnpauseRequest, user)); } - public SingularityRequestParent unpause(String requestId, Optional unpauseRequest) { + public SingularityRequestParent unpause(String requestId, Optional unpauseRequest, SingularityUser user) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); @@ -399,16 +408,17 @@ public SingularityRequestParent unpause(String requestId, Optional maybeExitCooldownRequest = Optional.fromNullable(exitCooldownRequest); - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeExitCooldownRequest.orNull(), () -> exitCooldown(requestId, maybeExitCooldownRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, maybeExitCooldownRequest.orNull(), () -> exitCooldown(requestId, maybeExitCooldownRequest, user)); } - public SingularityRequestParent exitCooldown(String requestId, Optional exitCooldownRequest) { - final SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + public SingularityRequestParent exitCooldown(String requestId, Optional exitCooldownRequest, SingularityUser user) { + final SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); authorizationHelper.checkForAuthorization(requestWithState.getRequest(), user, SingularityAuthorizationScope.WRITE); @@ -444,10 +455,10 @@ public SingularityRequestParent exitCooldown(String requestId, Optional getActiveRequests(@QueryParam("useWebCache") Boolean useWebCache) { - return getRequestsWithDeployState(requestManager.getActiveRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ); + public List getActiveRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { + return getRequestsWithDeployState(requestManager.getActiveRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ, user); } - private List getRequestsWithDeployState(Iterable requests, final SingularityAuthorizationScope scope) { + private List getRequestsWithDeployState(Iterable requests, final SingularityAuthorizationScope scope, SingularityUser user) { if (!authorizationHelper.hasAdminAuthorization(user) && disasterManager.isDisabled(SingularityAction.EXPENSIVE_API_CALLS)) { LOG.trace("Short circuting getRequestsWithDeployState() to [] due to EXPENSIVE_API_CALLS disabled"); return Collections.emptyList(); @@ -498,38 +510,42 @@ public boolean apply(SingularityRequestWithState input) { @PropertyFiltering @Path("/paused") @ApiOperation(value="Retrieve the list of paused requests", response=SingularityRequestParent.class, responseContainer="List") - public List getPausedRequests(@QueryParam("useWebCache") Boolean useWebCache) { - return getRequestsWithDeployState(requestManager.getPausedRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ); + public List getPausedRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { + return getRequestsWithDeployState(requestManager.getPausedRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ, user); } @GET @PropertyFiltering @Path("/cooldown") @ApiOperation(value="Retrieve the list of requests in system cooldown", response=SingularityRequestParent.class, responseContainer="List") - public List getCooldownRequests(@QueryParam("useWebCache") Boolean useWebCache) { - return getRequestsWithDeployState(requestManager.getCooldownRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ); + public List getCooldownRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { + return getRequestsWithDeployState(requestManager.getCooldownRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ, user); } @GET @PropertyFiltering @Path("/finished") @ApiOperation(value="Retreive the list of finished requests (Scheduled requests which have exhausted their schedules)", response=SingularityRequestParent.class, responseContainer="List") - public List getFinishedRequests(@QueryParam("useWebCache") Boolean useWebCache) { - return getRequestsWithDeployState(requestManager.getFinishedRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ); + public List getFinishedRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { + return getRequestsWithDeployState(requestManager.getFinishedRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ, user); } @GET @PropertyFiltering @ApiOperation(value="Retrieve the list of all requests", response=SingularityRequestParent.class, responseContainer="List") - public List getRequests(@QueryParam("useWebCache") Boolean useWebCache) { - return getRequestsWithDeployState(requestManager.getRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ); + public List getRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { + return getRequestsWithDeployState(requestManager.getRequests(useWebCache(useWebCache)), SingularityAuthorizationScope.READ, user); } @GET @PropertyFiltering @Path("/queued/pending") @ApiOperation(value="Retrieve the list of pending requests", response=SingularityPendingRequest.class, responseContainer="List") - public Iterable getPendingRequests() { + public Iterable getPendingRequests(@Auth SingularityUser user) { return authorizationHelper.filterByAuthorizedRequests(user, requestManager.getPendingRequests(), SingularityTransformHelpers.PENDING_REQUEST_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -537,7 +553,7 @@ public Iterable getPendingRequests() { @PropertyFiltering @Path("/queued/cleanup") @ApiOperation(value="Retrieve the list of requests being cleaned up", response=SingularityRequestCleanup.class, responseContainer="List") - public Iterable getCleanupRequests() { + public Iterable getCleanupRequests(@Auth SingularityUser user) { return authorizationHelper.filterByAuthorizedRequests(user, requestManager.getCleanupRequests(), SingularityTransformHelpers.REQUEST_CLEANUP_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -547,12 +563,14 @@ public Iterable getCleanupRequests() { @ApiResponses({ @ApiResponse(code=404, message="No Request with that ID"), }) - public SingularityRequestParent getRequest(@ApiParam("Request ID") @PathParam("requestId") String requestId, @QueryParam("useWebCache") Boolean useWebCache) { - return fillEntireRequest(fetchRequestWithState(requestId, useWebCache(useWebCache))); + public SingularityRequestParent getRequest(@Auth SingularityUser user, + @ApiParam("Request ID") @PathParam("requestId") String requestId, + @QueryParam("useWebCache") Boolean useWebCache) { + return fillEntireRequest(fetchRequestWithState(requestId, useWebCache(useWebCache), user)); } - public SingularityRequestParent getRequest(String requestId) { - return fillEntireRequest(fetchRequestWithState(requestId, false)); + public SingularityRequestParent getRequest(String requestId, SingularityUser user) { + return fillEntireRequest(fetchRequestWithState(requestId, false, user)); } @DELETE @@ -562,15 +580,16 @@ public SingularityRequestParent getRequest(String requestId) { @ApiResponses({ @ApiResponse(code=404, message="No Request with that ID"), }) - public SingularityRequest deleteRequest(@ApiParam("The request ID to delete.") @PathParam("requestId") String requestId, + public SingularityRequest deleteRequest(@Auth SingularityUser user, + @ApiParam("The request ID to delete.") @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext, @ApiParam("Delete options") SingularityDeleteRequestRequest deleteRequest) { final Optional maybeDeleteRequest = Optional.fromNullable(deleteRequest); - return maybeProxyToLeader(requestContext, SingularityRequest.class, maybeDeleteRequest.orNull(), () -> deleteRequest(requestId, maybeDeleteRequest)); + return maybeProxyToLeader(requestContext, SingularityRequest.class, maybeDeleteRequest.orNull(), () -> deleteRequest(requestId, maybeDeleteRequest, user)); } - public SingularityRequest deleteRequest(String requestId, Optional deleteRequest) { - SingularityRequest request = fetchRequestWithState(requestId).getRequest(); + public SingularityRequest deleteRequest(String requestId, Optional deleteRequest, SingularityUser user) { + SingularityRequest request = fetchRequestWithState(requestId, user).getRequest(); authorizationHelper.checkForAuthorization(request, user, SingularityAuthorizationScope.WRITE); validator.checkActionEnabled(SingularityAction.REMOVE_REQUEST); @@ -585,9 +604,9 @@ public SingularityRequest deleteRequest(String requestId, Optional scale(requestId, scaleRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, scaleRequest, () -> scale(requestId, scaleRequest, user)); } - public SingularityRequestParent scale(String requestId, SingularityScaleRequest scaleRequest) { - SingularityRequestWithState oldRequestWithState = fetchRequestWithState(requestId); + public SingularityRequestParent scale(String requestId, SingularityScaleRequest scaleRequest, SingularityUser user) { + SingularityRequestWithState oldRequestWithState = fetchRequestWithState(requestId, user); SingularityRequest oldRequest = oldRequestWithState.getRequest(); authorizationHelper.checkForAuthorization(oldRequest, user, SingularityAuthorizationScope.WRITE); @@ -637,23 +657,23 @@ public SingularityRequestParent scale(String requestId, SingularityScaleRequest SingularityBounceRequest bounceRequest = new SingularityBounceRequest(Optional.of(isIncrementalBounce), scaleRequest.getSkipHealthchecks(), Optional.absent(), Optional.of(UUID.randomUUID().toString()), Optional.absent(), Optional.absent()); - submitRequest(newRequest, Optional.of(oldRequestWithState), Optional.of(RequestHistoryType.SCALED), scaleRequest.getSkipHealthchecks(), Optional.of(scaleMessage), Optional.of(bounceRequest)); + submitRequest(newRequest, Optional.of(oldRequestWithState), Optional.of(RequestHistoryType.SCALED), scaleRequest.getSkipHealthchecks(), Optional.of(scaleMessage), Optional.of(bounceRequest), user); } else { - submitRequest(newRequest, Optional.of(oldRequestWithState), Optional.of(RequestHistoryType.SCALED), scaleRequest.getSkipHealthchecks(), Optional.of(scaleMessage), Optional.absent()); + submitRequest(newRequest, Optional.of(oldRequestWithState), Optional.of(RequestHistoryType.SCALED), scaleRequest.getSkipHealthchecks(), Optional.of(scaleMessage), Optional.absent(), user); } if (scaleRequest.getDurationMillis().isPresent()) { - requestManager.saveExpiringObject(new SingularityExpiringScale(requestId, JavaUtils.getUserEmail(user), + requestManager.saveExpiringObject(new SingularityExpiringScale(requestId, user.getEmail(), System.currentTimeMillis(), scaleRequest, oldRequest.getInstances(), scaleRequest.getActionId().or(UUID.randomUUID().toString()), scaleRequest.getBounce())); } - mailer.sendRequestScaledMail(newRequest, Optional.of(scaleRequest), oldRequest.getInstances(), JavaUtils.getUserEmail(user)); + mailer.sendRequestScaledMail(newRequest, Optional.of(scaleRequest), oldRequest.getInstances(), user.getEmail()); - return fillEntireRequest(fetchRequestWithState(requestId)); + return fillEntireRequest(fetchRequestWithState(requestId, user)); } - private > SingularityRequestParent deleteExpiringObject(Class clazz, String requestId) { - SingularityRequestWithState requestWithState = fetchRequestWithState(requestId); + private > SingularityRequestParent deleteExpiringObject(Class clazz, String requestId, SingularityUser user) { + SingularityRequestWithState requestWithState = fetchRequestWithState(requestId, user); SingularityDeleteResult deleteResult = requestManager.deleteExpiringObject(clazz, requestId); @@ -668,8 +688,9 @@ private > SingularityRequest @ApiResponses({ @ApiResponse(code=404, message="No Request or expiring scale request for that ID"), }) - public SingularityRequestParent deleteExpiringScale(@ApiParam("The Request ID") @PathParam("requestId") String requestId) { - return deleteExpiringObject(SingularityExpiringScale.class, requestId); + public SingularityRequestParent deleteExpiringScale(@Auth SingularityUser user, + @ApiParam("The Request ID") @PathParam("requestId") String requestId) { + return deleteExpiringObject(SingularityExpiringScale.class, requestId, user); } @Deprecated @@ -679,8 +700,9 @@ public SingularityRequestParent deleteExpiringScale(@ApiParam("The Request ID") @ApiResponses({ @ApiResponse(code=404, message="No Request or expiring skipHealthchecks request for that ID"), }) - public SingularityRequestParent deleteExpiringSkipHealthchecksDeprecated(@ApiParam("The Request ID") @PathParam("requestId") String requestId) { - return deleteExpiringSkipHealthchecks(requestId); + public SingularityRequestParent deleteExpiringSkipHealthchecksDeprecated(@Auth SingularityUser user, + @ApiParam("The Request ID") @PathParam("requestId") String requestId) { + return deleteExpiringSkipHealthchecks(user, requestId); } @DELETE @@ -689,8 +711,9 @@ public SingularityRequestParent deleteExpiringSkipHealthchecksDeprecated(@ApiPar @ApiResponses({ @ApiResponse(code=404, message="No Request or expiring skipHealthchecks request for that ID"), }) - public SingularityRequestParent deleteExpiringSkipHealthchecks(@ApiParam("The Request ID") @PathParam("requestId") String requestId) { - return deleteExpiringObject(SingularityExpiringSkipHealthchecks.class, requestId); + public SingularityRequestParent deleteExpiringSkipHealthchecks(@Auth SingularityUser user, + @ApiParam("The Request ID") @PathParam("requestId") String requestId) { + return deleteExpiringObject(SingularityExpiringSkipHealthchecks.class, requestId, user); } @DELETE @@ -699,8 +722,9 @@ public SingularityRequestParent deleteExpiringSkipHealthchecks(@ApiParam("The Re @ApiResponses({ @ApiResponse(code=404, message="No Request or expiring pause request for that ID"), }) - public SingularityRequestParent deleteExpiringPause(@ApiParam("The Request ID") @PathParam("requestId") String requestId) { - return deleteExpiringObject(SingularityExpiringPause.class, requestId); + public SingularityRequestParent deleteExpiringPause(@Auth SingularityUser user, + @ApiParam("The Request ID") @PathParam("requestId") String requestId) { + return deleteExpiringObject(SingularityExpiringPause.class, requestId, user); } @DELETE @@ -709,8 +733,9 @@ public SingularityRequestParent deleteExpiringPause(@ApiParam("The Request ID") @ApiResponses({ @ApiResponse(code=404, message="No Request or expiring bounce request for that ID"), }) - public SingularityRequestParent deleteExpiringBounce(@ApiParam("The Request ID") @PathParam("requestId") String requestId) { - return deleteExpiringObject(SingularityExpiringBounce.class, requestId); + public SingularityRequestParent deleteExpiringBounce(@Auth SingularityUser user, + @ApiParam("The Request ID") @PathParam("requestId") String requestId) { + return deleteExpiringObject(SingularityExpiringBounce.class, requestId, user); } @Deprecated @@ -721,10 +746,11 @@ public SingularityRequestParent deleteExpiringBounce(@ApiParam("The Request ID") @ApiResponses({ @ApiResponse(code=404, message="No Request with that ID"), }) - public SingularityRequestParent skipHealthchecksDeprecated(@ApiParam("The Request ID to scale") @PathParam("requestId") String requestId, + public SingularityRequestParent skipHealthchecksDeprecated(@Auth SingularityUser user, + @ApiParam("The Request ID to scale") @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext, @ApiParam("SkipHealtchecks options") SingularitySkipHealthchecksRequest skipHealthchecksRequest) { - return skipHealthchecks(requestId, requestContext, skipHealthchecksRequest); + return skipHealthchecks(user, requestId, requestContext, skipHealthchecksRequest); } @PUT @@ -734,33 +760,35 @@ public SingularityRequestParent skipHealthchecksDeprecated(@ApiParam("The Reques @ApiResponses({ @ApiResponse(code=404, message="No Request with that ID"), }) - public SingularityRequestParent skipHealthchecks(@ApiParam("The Request ID to scale") @PathParam("requestId") String requestId, + public SingularityRequestParent skipHealthchecks(@Auth SingularityUser user, + @ApiParam("The Request ID to scale") @PathParam("requestId") String requestId, @Context HttpServletRequest requestContext, @ApiParam("SkipHealtchecks options") SingularitySkipHealthchecksRequest skipHealthchecksRequest) { - return maybeProxyToLeader(requestContext, SingularityRequestParent.class, skipHealthchecksRequest, () -> skipHealthchecks(requestId, skipHealthchecksRequest)); + return maybeProxyToLeader(requestContext, SingularityRequestParent.class, skipHealthchecksRequest, () -> skipHealthchecks(requestId, skipHealthchecksRequest, user)); } - public SingularityRequestParent skipHealthchecks(String requestId, SingularitySkipHealthchecksRequest skipHealthchecksRequest) { - SingularityRequestWithState oldRequestWithState = fetchRequestWithState(requestId); + public SingularityRequestParent skipHealthchecks(String requestId, SingularitySkipHealthchecksRequest skipHealthchecksRequest, SingularityUser user) { + SingularityRequestWithState oldRequestWithState = fetchRequestWithState(requestId, user); SingularityRequest oldRequest = oldRequestWithState.getRequest(); SingularityRequest newRequest = oldRequest.toBuilder().setSkipHealthchecks(skipHealthchecksRequest.getSkipHealthchecks()).build(); - submitRequest(newRequest, Optional.of(oldRequestWithState), Optional. absent(), Optional. absent(), skipHealthchecksRequest.getMessage(), Optional.absent()); + submitRequest(newRequest, Optional.of(oldRequestWithState), Optional.absent(), Optional.absent(), skipHealthchecksRequest.getMessage(), Optional.absent(), user); if (skipHealthchecksRequest.getDurationMillis().isPresent()) { - requestManager.saveExpiringObject(new SingularityExpiringSkipHealthchecks(requestId, JavaUtils.getUserEmail(user), + requestManager.saveExpiringObject(new SingularityExpiringSkipHealthchecks(requestId, user.getEmail(), System.currentTimeMillis(), skipHealthchecksRequest, oldRequest.getSkipHealthchecks(), skipHealthchecksRequest.getActionId().or(UUID.randomUUID().toString()))); } - return fillEntireRequest(fetchRequestWithState(requestId)); + return fillEntireRequest(fetchRequestWithState(requestId, user)); } @GET @PropertyFiltering @Path("/lbcleanup") @ApiOperation("Retrieve the list of tasks being cleaned from load balancers.") - public Iterable getLbCleanupRequests(@QueryParam("useWebCache") Boolean useWebCache) { + public Iterable getLbCleanupRequests(@Auth SingularityUser user, + @QueryParam("useWebCache") Boolean useWebCache) { return authorizationHelper.filterAuthorizedRequestIds(user, requestManager.getLbCleanupRequestIds(), SingularityAuthorizationScope.READ, useWebCache(useWebCache)); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/S3LogResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/S3LogResource.java index ccddd7d225..1c0fba741c 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/S3LogResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/S3LogResource.java @@ -66,7 +66,6 @@ import com.hubspot.singularity.SingularityS3Log; import com.hubspot.singularity.SingularityS3LogMetadata; import com.hubspot.singularity.SingularityS3UploaderFile; -import com.hubspot.singularity.SingularityService; import com.hubspot.singularity.SingularityTaskHistory; import com.hubspot.singularity.SingularityTaskHistoryUpdate; import com.hubspot.singularity.SingularityTaskHistoryUpdate.SimplifiedTaskState; @@ -90,6 +89,8 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.S3_LOG_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity task logs stored in S3.", value=ApiPaths.S3_LOG_RESOURCE_PATH) @@ -116,8 +117,8 @@ public int compare(SingularityS3LogMetadata o1, SingularityS3LogMetadata o2) { @Inject public S3LogResource(RequestManager requestManager, HistoryManager historyManager, RequestHistoryHelper requestHistoryHelper, TaskManager taskManager, DeployManager deployManager, - Optional configuration, SingularityAuthorizationHelper authorizationHelper, Optional user,SingularityS3Services s3Services) { - super(historyManager, taskManager, deployManager, authorizationHelper, user); + Optional configuration, SingularityAuthorizationHelper authorizationHelper, SingularityS3Services s3Services) { + super(historyManager, taskManager, deployManager, authorizationHelper); this.requestManager = requestManager; this.configuration = configuration; this.requestHistoryHelper = requestHistoryHelper; @@ -125,8 +126,8 @@ public S3LogResource(RequestManager requestManager, HistoryManager historyManage } // Generation of prefixes - private Collection getS3PrefixesForTask(S3Configuration s3Configuration, SingularityTaskId taskId, Optional startArg, Optional endArg, String group) { - Optional history = getTaskHistory(taskId); + private Collection getS3PrefixesForTask(S3Configuration s3Configuration, SingularityTaskId taskId, Optional startArg, Optional endArg, String group, SingularityUser user) { + Optional history = getTaskHistory(taskId, user); long start = taskId.getStartedAt(); if (startArg.isPresent()) { @@ -204,8 +205,8 @@ private Collection getS3PrefixesForRequest(S3Configuration s3Configurati return prefixes; } - private Collection getS3PrefixesForDeploy(S3Configuration s3Configuration, String requestId, String deployId, Optional startArg, Optional endArg, String group) { - SingularityDeployHistory deployHistory = getDeployHistory(requestId, deployId); + private Collection getS3PrefixesForDeploy(S3Configuration s3Configuration, String requestId, String deployId, Optional startArg, Optional endArg, String group, SingularityUser user) { + SingularityDeployHistory deployHistory = getDeployHistory(requestId, deployId, user); long start = deployHistory.getDeployMarker().getTimestamp(); if (startArg.isPresent()) { @@ -240,15 +241,15 @@ private Collection getS3PrefixesForDeploy(S3Configuration s3Configuratio return prefixes; } - private Map> getServiceToPrefixes(SingularityS3SearchRequest search) { + private Map> getServiceToPrefixes(SingularityS3SearchRequest search, SingularityUser user) { Map> servicesToPrefixes = new HashMap<>(); if (!search.getTaskIds().isEmpty()) { for (String taskId : search.getTaskIds()) { SingularityTaskId taskIdObject = getTaskIdObject(taskId); - String group = getRequestGroupForTask(taskIdObject).or(SingularityS3FormatHelper.DEFAULT_GROUP_NAME); + String group = getRequestGroupForTask(taskIdObject, user).or(SingularityS3FormatHelper.DEFAULT_GROUP_NAME); Set s3Buckets = getBuckets(group); - Collection prefixes = getS3PrefixesForTask(configuration.get(), taskIdObject, search.getStart(), search.getEnd(), group); + Collection prefixes = getS3PrefixesForTask(configuration.get(), taskIdObject, search.getStart(), search.getEnd(), group, user); for (String s3Bucket : s3Buckets) { SingularityS3Service s3Service = s3Services.getServiceByGroupAndBucketOrDefault(group, s3Bucket); if (!servicesToPrefixes.containsKey(s3Service)) { @@ -260,12 +261,12 @@ private Map> getServiceToPrefixes(SingularityS } if (!search.getRequestsAndDeploys().isEmpty()) { for (Map.Entry> entry : search.getRequestsAndDeploys().entrySet()) { - String group = getRequestGroup(entry.getKey()).or(SingularityS3FormatHelper.DEFAULT_GROUP_NAME); + String group = getRequestGroup(entry.getKey(), user).or(SingularityS3FormatHelper.DEFAULT_GROUP_NAME); Set s3Buckets = getBuckets(group); List prefixes = new ArrayList<>(); if (!entry.getValue().isEmpty()) { for (String deployId : entry.getValue()) { - prefixes.addAll(getS3PrefixesForDeploy(configuration.get(), entry.getKey(), deployId, search.getStart(), search.getEnd(), group)); + prefixes.addAll(getS3PrefixesForDeploy(configuration.get(), entry.getKey(), deployId, search.getStart(), search.getEnd(), group, user)); } } else { prefixes.addAll(getS3PrefixesForRequest(configuration.get(), entry.getKey(), search.getStart(), search.getEnd(), group)); @@ -502,18 +503,18 @@ private boolean isFinalPageForAllPrefixes(Collection continua } // Finding request group - private Optional getRequestGroupForTask(final SingularityTaskId taskId) { - Optional maybeTaskHistory = getTaskHistory(taskId); + private Optional getRequestGroupForTask(final SingularityTaskId taskId, SingularityUser user) { + Optional maybeTaskHistory = getTaskHistory(taskId, user); if (maybeTaskHistory.isPresent()) { SingularityRequest request = maybeTaskHistory.get().getTask().getTaskRequest().getRequest(); authorizationHelper.checkForAuthorization(request, user, SingularityAuthorizationScope.READ); return request.getGroup(); } else { - return getRequestGroup(taskId.getRequestId()); + return getRequestGroup(taskId.getRequestId(), user); } } - private Optional getRequestGroup(final String requestId) { + private Optional getRequestGroup(final String requestId, SingularityUser user) { final Optional maybeRequest = requestManager.getRequest(requestId); if (maybeRequest.isPresent()) { authorizationHelper.checkForAuthorization(maybeRequest.get().getRequest(), user, SingularityAuthorizationScope.READ); @@ -540,6 +541,7 @@ private void checkS3() { @Path("/task/{taskId}") @ApiOperation("Retrieve the list of logs stored in S3 for a specific task.") public List getS3LogsForTask( + @Auth SingularityUser user, @ApiParam("The task ID to search for") @PathParam("taskId") String taskId, @ApiParam("Start timestamp (millis, 13 digit)") @QueryParam("start") Optional start, @ApiParam("End timestamp (mills, 13 digit)") @QueryParam("end") Optional end, @@ -558,7 +560,7 @@ public List getS3LogsForTask( Collections.emptyMap()); try { - return getS3Logs(configuration.get(), getServiceToPrefixes(search), search, false).getResults(); + return getS3Logs(configuration.get(), getServiceToPrefixes(search, user), search, false).getResults(); } catch (TimeoutException te) { throw timeout("Timed out waiting for response from S3 for %s", taskId); } catch (Throwable t) { @@ -570,6 +572,7 @@ public List getS3LogsForTask( @Path("/request/{requestId}") @ApiOperation("Retrieve the list of logs stored in S3 for a specific request.") public List getS3LogsForRequest( + @Auth SingularityUser user, @ApiParam("The request ID to search for") @PathParam("requestId") String requestId, @ApiParam("Start timestamp (millis, 13 digit)") @QueryParam("start") Optional start, @ApiParam("End timestamp (mills, 13 digit)") @QueryParam("end") Optional end, @@ -589,7 +592,7 @@ public List getS3LogsForRequest( Optional.absent(), Collections.emptyMap()); - return getS3Logs(configuration.get(), getServiceToPrefixes(search), search, false).getResults(); + return getS3Logs(configuration.get(), getServiceToPrefixes(search, user), search, false).getResults(); } catch (TimeoutException te) { throw timeout("Timed out waiting for response from S3 for %s", requestId); } catch (Throwable t) { @@ -601,6 +604,7 @@ public List getS3LogsForRequest( @Path("/request/{requestId}/deploy/{deployId}") @ApiOperation("Retrieve the list of logs stored in S3 for a specific deploy.") public List getS3LogsForDeploy( + @Auth SingularityUser user, @ApiParam("The request ID to search for") @PathParam("requestId") String requestId, @ApiParam("The deploy ID to search for") @PathParam("deployId") String deployId, @ApiParam("Start timestamp (millis, 13 digit)") @QueryParam("start") Optional start, @@ -621,7 +625,7 @@ public List getS3LogsForDeploy( Optional.absent(), Collections.emptyMap()); - return getS3Logs(configuration.get(), getServiceToPrefixes(search), search, false).getResults(); + return getS3Logs(configuration.get(), getServiceToPrefixes(search, user), search, false).getResults(); } catch (TimeoutException te) { throw timeout("Timed out waiting for response from S3 for %s-%s", requestId, deployId); } catch (Throwable t) { @@ -633,13 +637,13 @@ public List getS3LogsForDeploy( @Path("/search") @Consumes(MediaType.APPLICATION_JSON) @ApiOperation("Retrieve a paginated list of logs stored in S3") - public SingularityS3SearchResult getPaginatedS3Logs(@ApiParam(required = true) SingularityS3SearchRequest search) throws Exception { + public SingularityS3SearchResult getPaginatedS3Logs(@Auth SingularityUser user, @ApiParam(required = true) SingularityS3SearchRequest search) throws Exception { checkS3(); checkBadRequest(!search.getRequestsAndDeploys().isEmpty() || !search.getTaskIds().isEmpty(), "Must specify at least one request or task to search"); try { - return getS3Logs(configuration.get(), getServiceToPrefixes(search), search, true); + return getS3Logs(configuration.get(), getServiceToPrefixes(search, user), search, true); } catch (TimeoutException te) { throw timeout("Timed out waiting for response from S3 for %s", search); } catch (Throwable t) { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/SandboxResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/SandboxResource.java index 2a4466e436..f721166d1f 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/SandboxResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/SandboxResource.java @@ -45,6 +45,8 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.SANDBOX_RESOURCE_PATH) @Produces({MediaType.APPLICATION_JSON}) @Api(description="Provides a proxy to Mesos sandboxes.", value=ApiPaths.SANDBOX_RESOURCE_PATH) @@ -55,17 +57,17 @@ public class SandboxResource extends AbstractHistoryResource { @Inject public SandboxResource(HistoryManager historyManager, TaskManager taskManager, SandboxManager sandboxManager, DeployManager deployManager, SingularityMesosExecutorInfoSupport logSupport, - SingularityConfiguration configuration, SingularityAuthorizationHelper authorizationHelper, Optional user) { - super(historyManager, taskManager, deployManager, authorizationHelper, user); + SingularityConfiguration configuration, SingularityAuthorizationHelper authorizationHelper) { + super(historyManager, taskManager, deployManager, authorizationHelper); this.configuration = configuration; this.sandboxManager = sandboxManager; this.logSupport = logSupport; } - private SingularityTaskHistory checkHistory(String taskId) { + private SingularityTaskHistory checkHistory(String taskId, SingularityUser user) { final SingularityTaskId taskIdObj = getTaskIdObject(taskId); - final SingularityTaskHistory taskHistory = getTaskHistoryRequired(taskIdObj); + final SingularityTaskHistory taskHistory = getTaskHistoryRequired(taskIdObj, user); if (!taskHistory.getDirectory().isPresent()) { logSupport.checkDirectoryAndContainerId(taskIdObj); @@ -89,8 +91,8 @@ private String getCurrentDirectory(String taskId, String currentDirectory) { @GET @Path("/{taskId}/browse") @ApiOperation("Retrieve information about a specific task's sandbox.") - public SingularitySandbox browse(@ApiParam("The task ID to browse") @PathParam("taskId") String taskId, - @ApiParam("The path to browse from") @QueryParam("path") String path) { + public SingularitySandbox browse(@Auth SingularityUser user, @ApiParam("The task ID to browse") @PathParam("taskId") String taskId, + @ApiParam("The path to browse from") @QueryParam("path") String path) { authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); // Remove all trailing slashes from the path @@ -99,7 +101,7 @@ public SingularitySandbox browse(@ApiParam("The task ID to browse") @PathParam(" } final String currentDirectory = getCurrentDirectory(taskId, path); - final SingularityTaskHistory history = checkHistory(taskId); + final SingularityTaskHistory history = checkHistory(taskId, user); final String slaveHostname = history.getTask().getHostname(); final String pathToRoot = history.getDirectory().get(); @@ -127,14 +129,15 @@ public SingularitySandboxFile apply(MesosFileObject input) { @GET @Path("/{taskId}/read") @ApiOperation("Retrieve part of the contents of a file in a specific task's sandbox.") - public MesosFileChunkObject read(@ApiParam("The task ID of the sandbox to read from") @PathParam("taskId") String taskId, - @ApiParam("The path to the file to be read") @QueryParam("path") String path, - @ApiParam("Optional string to grep for") @QueryParam("grep") Optional grep, - @ApiParam("Byte offset to start reading from") @QueryParam("offset") Optional offset, - @ApiParam("Maximum number of bytes to read") @QueryParam("length") Optional length) { + public MesosFileChunkObject read(@Auth SingularityUser user, + @ApiParam("The task ID of the sandbox to read from") @PathParam("taskId") String taskId, + @ApiParam("The path to the file to be read") @QueryParam("path") String path, + @ApiParam("Optional string to grep for") @QueryParam("grep") Optional grep, + @ApiParam("Byte offset to start reading from") @QueryParam("offset") Optional offset, + @ApiParam("Maximum number of bytes to read") @QueryParam("length") Optional length) { authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); - final SingularityTaskHistory history = checkHistory(taskId); + final SingularityTaskHistory history = checkHistory(taskId, user); checkBadRequest(!Strings.isNullOrEmpty(path), "Must specify 'path'"); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java index ec233efeb7..0406381f85 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java @@ -13,7 +13,6 @@ import com.google.common.base.Optional; import com.google.inject.Inject; -import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.MachineState; import com.hubspot.singularity.SingularityAction; import com.hubspot.singularity.SingularityMachineStateHistoryUpdate; @@ -29,13 +28,15 @@ import com.wordnik.swagger.annotations.ApiOperation; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.SLAVE_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity slaves.", value=ApiPaths.SLAVE_RESOURCE_PATH) public class SlaveResource extends AbstractMachineResource { @Inject - public SlaveResource(SlaveManager slaveManager, SingularityAuthorizationHelper authorizationHelper, Optional user, SingularityValidator validator) { - super(slaveManager, authorizationHelper, user, validator); + public SlaveResource(SlaveManager slaveManager, SingularityAuthorizationHelper authorizationHelper, SingularityValidator validator) { + super(slaveManager, authorizationHelper, validator); } @Override @@ -67,46 +68,46 @@ public Optional getSlave(@ApiParam("Slave ID") @PathParam("sla @DELETE @Path("/slave/{slaveId}") @ApiOperation("Remove a known slave, erasing history. This operation will cancel decomissioning of the slave") - public void removeSlave(@ApiParam("Active SlaveId") @PathParam("slaveId") String slaveId) { - super.remove(slaveId); + public void removeSlave(@Auth SingularityUser user, @ApiParam("Active SlaveId") @PathParam("slaveId") String slaveId) { + super.remove(slaveId, user); } @POST @Path("/slave/{slaveId}/decommission") @ApiOperation("Begin decommissioning a specific active slave") - public void decommissionSlave(@ApiParam("Active slaveId") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { + public void decommissionSlave(@Auth SingularityUser user, @ApiParam("Active slaveId") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.decommission(slaveId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.DECOMMISSION_SLAVE); + super.decommission(slaveId, maybeChangeRequest, user, SingularityAction.DECOMMISSION_SLAVE); } @POST @Path("/slave/{slaveId}/freeze") @ApiOperation("Freeze tasks on a specific slave") - public void freezeSlave(@ApiParam("Slave ID") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { + public void freezeSlave(@Auth SingularityUser user, @ApiParam("Slave ID") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.freeze(slaveId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.FREEZE_SLAVE); + super.freeze(slaveId, maybeChangeRequest, user, SingularityAction.FREEZE_SLAVE); } @POST @Path("/slave/{slaveId}/activate") @ApiOperation("Activate a decomissioning slave, canceling decomission without erasing history") - public void activateSlave(@ApiParam("Active slaveId") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { + public void activateSlave(@Auth SingularityUser user, @ApiParam("Active slaveId") @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { final Optional maybeChangeRequest = Optional.fromNullable(changeRequest); - super.activate(slaveId, maybeChangeRequest, JavaUtils.getUserEmail(user), SingularityAction.ACTIVATE_SLAVE); + super.activate(slaveId, maybeChangeRequest, user, SingularityAction.ACTIVATE_SLAVE); } @DELETE @Path("/slave/{slaveId}/expiring") @ApiOperation("Delete any expiring machine state changes for this slave") - public void deleteExpiringStateChange(@ApiParam("Active slaveId") @PathParam("slaveId") String slaveId) { - super.cancelExpiring(slaveId); + public void deleteExpiringStateChange(@Auth SingularityUser user, @ApiParam("Active slaveId") @PathParam("slaveId") String slaveId) { + super.cancelExpiring(slaveId, user); } @GET @Path("/expiring") @ApiOperation("Get all expiring state changes for all slaves") - public List getExpiringStateChanges() { - return super.getExpiringStateChanges(); + public List getExpiringStateChanges(@Auth SingularityUser user) { + return super.getExpiringStateChanges(user); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java index 4382ee7d14..e3a972831e 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java @@ -36,7 +36,6 @@ import com.google.common.collect.Lists; import com.google.inject.Inject; import com.hubspot.jackson.jaxrs.PropertyFiltering; -import com.hubspot.mesos.JavaUtils; import com.hubspot.mesos.client.MesosClient; import com.hubspot.mesos.json.MesosTaskMonitorObject; import com.hubspot.mesos.json.MesosTaskStatisticsObject; @@ -87,6 +86,8 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.TASK_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity tasks.", value=ApiPaths.TASK_RESOURCE_PATH) @@ -99,7 +100,6 @@ public class TaskResource extends AbstractLeaderAwareResource { private final TaskRequestManager taskRequestManager; private final MesosClient mesosClient; private final SingularityAuthorizationHelper authorizationHelper; - private final Optional user; private final SingularityTaskMetadataConfiguration taskMetadataConfiguration; private final SingularityValidator validator; private final DisasterManager disasterManager; @@ -108,7 +108,7 @@ public class TaskResource extends AbstractLeaderAwareResource { @Inject public TaskResource(TaskRequestManager taskRequestManager, TaskManager taskManager, SlaveManager slaveManager, MesosClient mesosClient, SingularityTaskMetadataConfiguration taskMetadataConfiguration, - SingularityAuthorizationHelper authorizationHelper, Optional user, RequestManager requestManager, SingularityValidator validator, DisasterManager disasterManager, + SingularityAuthorizationHelper authorizationHelper, RequestManager requestManager, SingularityValidator validator, DisasterManager disasterManager, AsyncHttpClient httpClient, LeaderLatch leaderLatch, ObjectMapper objectMapper, SingularityDeployHealthHelper deployHealthHelper, DeployManager deployManager) { super(httpClient, leaderLatch, objectMapper); this.taskManager = taskManager; @@ -118,7 +118,6 @@ public TaskResource(TaskRequestManager taskRequestManager, TaskManager taskManag this.mesosClient = mesosClient; this.requestManager = requestManager; this.authorizationHelper = authorizationHelper; - this.user = user; this.validator = validator; this.disasterManager = disasterManager; this.deployHealthHelper = deployHealthHelper; @@ -129,7 +128,7 @@ public TaskResource(TaskRequestManager taskRequestManager, TaskManager taskManag @PropertyFiltering @Path("/scheduled") @ApiOperation("Retrieve list of scheduled tasks.") - public List getScheduledTasks(@QueryParam("useWebCache") Boolean useWebCache) { + public List getScheduledTasks(@Auth SingularityUser user, @QueryParam("useWebCache") Boolean useWebCache) { if (!authorizationHelper.hasAdminAuthorization(user) && disasterManager.isDisabled(SingularityAction.EXPENSIVE_API_CALLS)) { LOG.trace("Short circuting getScheduledTasks() to [] due to EXPENSIVE_API_CALLS disabled"); return Collections.emptyList(); @@ -143,7 +142,7 @@ public List getScheduledTasks(@QueryParam("useWebCache") @PropertyFiltering @Path("/scheduled/ids") @ApiOperation("Retrieve list of scheduled task IDs.") - public Iterable getScheduledTaskIds(@QueryParam("useWebCache") Boolean useWebCache) { + public Iterable getScheduledTaskIds(@Auth SingularityUser user, @QueryParam("useWebCache") Boolean useWebCache) { return authorizationHelper.filterByAuthorizedRequests(user, taskManager.getPendingTaskIds(useWebCache(useWebCache)), SingularityTransformHelpers.PENDING_TASK_ID_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -167,7 +166,7 @@ private SingularityTaskId getTaskIdFromStr(String activeTaskIdStr) { @PropertyFiltering @Path("/scheduled/task/{pendingTaskId}") @ApiOperation("Retrieve information about a pending task.") - public SingularityTaskRequest getPendingTask(@PathParam("pendingTaskId") String pendingTaskIdStr) { + public SingularityTaskRequest getPendingTask(@Auth SingularityUser user, @PathParam("pendingTaskId") String pendingTaskIdStr) { Optional pendingTask = taskManager.getPendingTask(getPendingTaskIdFromStr(pendingTaskIdStr)); checkNotFound(pendingTask.isPresent(), "Couldn't find %s", pendingTaskIdStr); @@ -185,7 +184,7 @@ public SingularityTaskRequest getPendingTask(@PathParam("pendingTaskId") String @PropertyFiltering @Path("/scheduled/request/{requestId}") @ApiOperation("Retrieve list of scheduled tasks for a specific request.") - public List getScheduledTasksForRequest(@PathParam("requestId") String requestId, @QueryParam("useWebCache") Boolean useWebCache) { + public List getScheduledTasksForRequest(@Auth SingularityUser user, @PathParam("requestId") String requestId, @QueryParam("useWebCache") Boolean useWebCache) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); final List tasks = Lists.newArrayList(Iterables.filter(taskManager.getPendingTasks(useWebCache(useWebCache)), SingularityPendingTask.matchingRequest(requestId))); @@ -196,7 +195,7 @@ public List getScheduledTasksForRequest(@PathParam("requ @GET @Path("/ids/request/{requestId}") @ApiOperation("Retrieve a list of task ids separated by status") - public Optional getTaskIdsByStatusForRequest(@PathParam("requestId") String requestId) { + public Optional getTaskIdsByStatusForRequest(@Auth SingularityUser user, @PathParam("requestId") String requestId) { authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); Optional requestWithState = requestManager.getRequest(requestId); @@ -235,7 +234,7 @@ public Optional getTaskIdsByStatusForRequest(@PathPa @GET @Path("/active/slave/{slaveId}") @ApiOperation("Retrieve list of active tasks on a specific slave.") - public Iterable getTasksForSlave(@PathParam("slaveId") String slaveId, @QueryParam("useWebCache") Boolean useWebCache) { + public Iterable getTasksForSlave(@Auth SingularityUser user, @PathParam("slaveId") String slaveId, @QueryParam("useWebCache") Boolean useWebCache) { Optional maybeSlave = slaveManager.getObject(slaveId); checkNotFound(maybeSlave.isPresent(), "Couldn't find a slave in any state with id %s", slaveId); @@ -247,7 +246,7 @@ public Iterable getTasksForSlave(@PathParam("slaveId") String s @PropertyFiltering @Path("/active") @ApiOperation("Retrieve the list of active tasks.") - public Iterable getActiveTasks(@QueryParam("useWebCache") Boolean useWebCache) { + public Iterable getActiveTasks(@Auth SingularityUser user, @QueryParam("useWebCache") Boolean useWebCache) { return authorizationHelper.filterByAuthorizedRequests(user, taskManager.getActiveTasks(useWebCache(useWebCache)), SingularityTransformHelpers.TASK_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -255,7 +254,7 @@ public Iterable getActiveTasks(@QueryParam("useWebCache") Boole @PropertyFiltering @Path("/cleaning") @ApiOperation("Retrieve the list of cleaning tasks.") - public Iterable getCleaningTasks(@QueryParam("useWebCache") Boolean useWebCache) { + public Iterable getCleaningTasks(@Auth SingularityUser user, @QueryParam("useWebCache") Boolean useWebCache) { if (!authorizationHelper.hasAdminAuthorization(user) && disasterManager.isDisabled(SingularityAction.EXPENSIVE_API_CALLS)) { LOG.trace("Short circuting getCleaningTasks() to [] due to EXPENSIVE_API_CALLS disabled"); return Collections.emptyList(); @@ -267,7 +266,7 @@ public Iterable getCleaningTasks(@QueryParam("useWebCach @GET @Path("/killed") @ApiOperation("Retrieve the list of killed tasks.") - public Iterable getKilledTasks() { + public Iterable getKilledTasks(@Auth SingularityUser user) { return authorizationHelper.filterByAuthorizedRequests(user, taskManager.getKilledTaskIdRecords(), SingularityTransformHelpers.KILLED_TASK_ID_RECORD_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } @@ -275,11 +274,11 @@ public Iterable getKilledTasks() { @PropertyFiltering @Path("/lbcleanup") @ApiOperation("Retrieve the list of tasks being cleaned from load balancers.") - public Iterable getLbCleanupTasks() { + public Iterable getLbCleanupTasks(@Auth SingularityUser user) { return authorizationHelper.filterByAuthorizedRequests(user, taskManager.getLBCleanupTasks(), SingularityTransformHelpers.TASK_ID_TO_REQUEST_ID, SingularityAuthorizationScope.READ); } - private SingularityTask checkActiveTask(String taskId, SingularityAuthorizationScope scope) { + private SingularityTask checkActiveTask(String taskId, SingularityAuthorizationScope scope, SingularityUser user) { SingularityTaskId taskIdObj = getTaskIdFromStr(taskId); Optional task = taskManager.getTask(taskIdObj); @@ -296,15 +295,15 @@ private SingularityTask checkActiveTask(String taskId, SingularityAuthorizationS @GET @Path("/task/{taskId}") @ApiOperation("Retrieve information about a specific active task.") - public SingularityTask getActiveTask(@PathParam("taskId") String taskId) { - return checkActiveTask(taskId, SingularityAuthorizationScope.READ); + public SingularityTask getActiveTask(@Auth SingularityUser user, @PathParam("taskId") String taskId) { + return checkActiveTask(taskId, SingularityAuthorizationScope.READ, user); } @GET @Path("/task/{taskId}/statistics") @ApiOperation("Retrieve statistics about a specific active task.") - public MesosTaskStatisticsObject getTaskStatistics(@PathParam("taskId") String taskId) { - SingularityTask task = checkActiveTask(taskId, SingularityAuthorizationScope.READ); + public MesosTaskStatisticsObject getTaskStatistics(@Auth SingularityUser user, @PathParam("taskId") String taskId) { + SingularityTask task = checkActiveTask(taskId, SingularityAuthorizationScope.READ, user); String executorIdToMatch = null; @@ -326,7 +325,7 @@ public MesosTaskStatisticsObject getTaskStatistics(@PathParam("taskId") String t @GET @Path("/task/{taskId}/cleanup") @ApiOperation("Get the cleanup object for the task, if it exists") - public Optional getTaskCleanup(@PathParam("taskId") String taskId) { + public Optional getTaskCleanup(@Auth SingularityUser user, @PathParam("taskId") String taskId) { authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); return taskManager.getTaskCleanup(taskId); @@ -334,8 +333,8 @@ public Optional getTaskCleanup(@PathParam("taskId") Stri @DELETE @Path("/task/{taskId}") - public SingularityTaskCleanup killTask(@PathParam("taskId") String taskId, @Context HttpServletRequest requestContext) { - return killTask(taskId, requestContext, null); + public SingularityTaskCleanup killTask(@Auth SingularityUser user, @PathParam("taskId") String taskId, @Context HttpServletRequest requestContext) { + return killTask(taskId, requestContext, null, user); } @DELETE @@ -347,13 +346,14 @@ public SingularityTaskCleanup killTask(@PathParam("taskId") String taskId, @Cont }) public SingularityTaskCleanup killTask(@PathParam("taskId") String taskId, @Context HttpServletRequest requestContext, - SingularityKillTaskRequest killTaskRequest) { + SingularityKillTaskRequest killTaskRequest, + SingularityUser user) { final Optional maybeKillTaskRequest = Optional.fromNullable(killTaskRequest); - return maybeProxyToLeader(requestContext, SingularityTaskCleanup.class, maybeKillTaskRequest.orNull(), () -> killTask(taskId, maybeKillTaskRequest)); + return maybeProxyToLeader(requestContext, SingularityTaskCleanup.class, maybeKillTaskRequest.orNull(), () -> killTask(taskId, maybeKillTaskRequest, user)); } - public SingularityTaskCleanup killTask(String taskId, Optional killTaskRequest) { - final SingularityTask task = checkActiveTask(taskId, SingularityAuthorizationScope.WRITE); + public SingularityTaskCleanup killTask(String taskId, Optional killTaskRequest, SingularityUser user) { + final SingularityTask task = checkActiveTask(taskId, SingularityAuthorizationScope.WRITE, user); Optional message = Optional.absent(); Optional override = Optional.absent(); @@ -367,7 +367,7 @@ public SingularityTaskCleanup killTask(String taskId, Optional> absent(), Optional. absent(), Optional. absent(), message, actionId)); } @@ -429,7 +429,7 @@ boolean userRequestedKillTakesPriority(String taskId) { @Path("/commands/queued") @ApiOperation(value="Retrieve a list of all the shell commands queued for execution") - public List getQueuedShellCommands() { + public List getQueuedShellCommands(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return taskManager.getAllQueuedTaskShellCommandRequests(); } @@ -443,7 +443,7 @@ public List getQueuedShellCommands() { @ApiResponse(code=409, message="Metadata with this type/timestamp already existed") }) @Consumes({ MediaType.APPLICATION_JSON }) - public void postTaskMetadata(@PathParam("taskId") String taskId, final SingularityTaskMetadataRequest taskMetadataRequest) { + public void postTaskMetadata(@Auth SingularityUser user, @PathParam("taskId") String taskId, final SingularityTaskMetadataRequest taskMetadataRequest) { SingularityTaskId taskIdObj = getTaskIdFromStr(taskId); authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.WRITE); @@ -464,7 +464,7 @@ public void postTaskMetadata(@PathParam("taskId") String taskId, final Singulari WebExceptions.checkNotFound(taskManager.taskExistsInZk(taskIdObj), "Task %s not found in ZooKeeper (can not save metadata to tasks which have been persisted", taskIdObj); final SingularityTaskMetadata taskMetadata = new SingularityTaskMetadata(taskIdObj, System.currentTimeMillis(), taskMetadataRequest.getType(), taskMetadataRequest.getTitle(), - taskMetadataRequest.getMessage(), JavaUtils.getUserEmail(user), taskMetadataRequest.getLevel()); + taskMetadataRequest.getMessage(), user.getEmail(), taskMetadataRequest.getLevel()); SingularityCreateResult result = taskManager.saveTaskMetadata(taskMetadata); @@ -479,7 +479,7 @@ public void postTaskMetadata(@PathParam("taskId") String taskId, final Singulari @ApiResponse(code=403, message="Given shell command doesn't exist") }) @Consumes({ MediaType.APPLICATION_JSON }) - public SingularityTaskShellCommandRequest runShellCommand(@PathParam("taskId") String taskId, final SingularityShellCommand shellCommand) { + public SingularityTaskShellCommandRequest runShellCommand(@Auth SingularityUser user, @PathParam("taskId") String taskId, final SingularityShellCommand shellCommand) { SingularityTaskId taskIdObj = getTaskIdFromStr(taskId); authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.WRITE); @@ -489,13 +489,13 @@ public SingularityTaskShellCommandRequest runShellCommand(@PathParam("taskId") S throw WebExceptions.badRequest("%s is not an active task, can't run %s on it", taskId, shellCommand.getName()); } - return startShellCommand(taskIdObj, shellCommand); + return startShellCommand(taskIdObj, shellCommand, user); } - private SingularityTaskShellCommandRequest startShellCommand(SingularityTaskId taskId, final SingularityShellCommand shellCommand) { + private SingularityTaskShellCommandRequest startShellCommand(SingularityTaskId taskId, final SingularityShellCommand shellCommand, SingularityUser user) { validator.checkValidShellCommand(shellCommand); - SingularityTaskShellCommandRequest shellRequest = new SingularityTaskShellCommandRequest(taskId, JavaUtils.getUserEmail(user), System.currentTimeMillis(), shellCommand); + SingularityTaskShellCommandRequest shellRequest = new SingularityTaskShellCommandRequest(taskId, user.getEmail(), System.currentTimeMillis(), shellCommand); taskManager.saveTaskShellCommandRequestToQueue(shellRequest); return shellRequest; } @@ -503,7 +503,7 @@ private SingularityTaskShellCommandRequest startShellCommand(SingularityTaskId t @GET @Path("/task/{taskId}/command") @ApiOperation(value="Retrieve a list of shell commands that have run for a task") - public List getShellCommandHisotry(@PathParam("taskId") String taskId) { + public List getShellCommandHisotry(@Auth SingularityUser user, @PathParam("taskId") String taskId) { authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); SingularityTaskId taskIdObj = getTaskIdFromStr(taskId); @@ -513,7 +513,7 @@ public List getShellCommandHisotry(@PathPara @GET @Path("/task/{taskId}/command/{commandName}/{commandTimestamp}") @ApiOperation(value="Retrieve a list of shell commands updates for a particular shell command on a task") - public List getShellCommandHisotryUpdates(@PathParam("taskId") String taskId, @PathParam("commandName") String commandName, @PathParam("commandTimestamp") Long commandTimestamp) { + public List getShellCommandHisotryUpdates(@Auth SingularityUser user, @PathParam("taskId") String taskId, @PathParam("commandName") String commandName, @PathParam("commandTimestamp") Long commandTimestamp) { authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); SingularityTaskId taskIdObj = getTaskIdFromStr(taskId); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java index 1b2222da90..746c53626e 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java @@ -20,13 +20,17 @@ import com.hubspot.singularity.SingularityTaskCurrentUsageWithId; import com.hubspot.singularity.SingularityTaskId; import com.hubspot.singularity.SingularityTaskUsage; +import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.WebExceptions; +import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.config.ApiPaths; import com.hubspot.singularity.data.SlaveManager; import com.hubspot.singularity.data.TaskManager; import com.hubspot.singularity.data.UsageManager; import com.wordnik.swagger.annotations.Api; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.USAGE_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Provides usage data about slaves and tasks", value=ApiPaths.USAGE_RESOURCE_PATH) @@ -37,23 +41,27 @@ public class UsageResource { private final UsageManager usageManager; private final TaskManager taskManager; private final SlaveManager slaveManager; + private final SingularityAuthorizationHelper authorizationHelper; @Inject - public UsageResource(UsageManager usageManager, TaskManager taskManager, SlaveManager slaveManager) { + public UsageResource(UsageManager usageManager, TaskManager taskManager, SlaveManager slaveManager, SingularityAuthorizationHelper authorizationHelper) { this.usageManager = usageManager; this.taskManager = taskManager; this.slaveManager = slaveManager; + this.authorizationHelper = authorizationHelper; } @GET @Path("/slaves") - public List getSlavesWithUsage() { + public List getSlavesWithUsage(@Auth SingularityUser user) { + authorizationHelper.checkAdminAuthorization(user); return usageManager.getAllCurrentSlaveUsage(); } @GET @Path("/slaves/{slaveId}/tasks/current") - public List getSlaveCurrentTaskUsage(@PathParam("slaveId") String slaveId) { + public List getSlaveCurrentTaskUsage(@Auth SingularityUser user, @PathParam("slaveId") String slaveId) { + authorizationHelper.checkAdminAuthorization(user); Optional slave = slaveManager.getObject(slaveId); WebExceptions.checkNotFound(slave.isPresent(), "No slave found with id %s", slaveId); @@ -70,19 +78,22 @@ public List getSlaveCurrentTaskUsage(@PathPar @GET @Path("/slaves/{slaveId}/history") - public List getSlaveUsageHistory(@PathParam("slaveId") String slaveId) { + public List getSlaveUsageHistory(@Auth SingularityUser user, @PathParam("slaveId") String slaveId) { + authorizationHelper.checkAdminAuthorization(user); return usageManager.getSlaveUsage(slaveId); } @GET @Path("/tasks/{taskId}/history") - public List getTaskUsageHistory(@PathParam("taskId") String taskId) { + public List getTaskUsageHistory(@Auth SingularityUser user, @PathParam("taskId") String taskId) { + authorizationHelper.checkAdminAuthorization(user); return usageManager.getTaskUsage(taskId); } @GET @Path("/cluster/utilization") - public SingularityClusterUtilization getClusterUtilization() { + public SingularityClusterUtilization getClusterUtilization(@Auth SingularityUser user) { + authorizationHelper.checkAdminAuthorization(user); WebExceptions.checkNotFound(usageManager.getClusterUtilization().isPresent(), "No cluster utilization has been saved yet"); return usageManager.getClusterUtilization().get(); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/UserResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/UserResource.java index 13f5a3b41a..f304ba9274 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/UserResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/UserResource.java @@ -7,52 +7,48 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import com.google.common.base.Optional; import com.google.inject.Inject; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.SingularityUserSettings; -import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.config.ApiPaths; import com.hubspot.singularity.data.UserManager; import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiParam; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.USER_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description = "Retrieve settings for a particular user", value = ApiPaths.USER_RESOURCE_PATH) public class UserResource { private final UserManager userManager; - private final Optional user; - private final SingularityAuthorizationHelper authorizationHelper; @Inject - public UserResource(UserManager userManager, Optional user, SingularityAuthorizationHelper authorizationHelper) { + public UserResource(UserManager userManager) { this.userManager = userManager; - this.user = user; - this.authorizationHelper = authorizationHelper; } @GET @Path("/settings") - public SingularityUserSettings getUserSettings() { - return userManager.getUserSettings(authorizationHelper.getAuthUserId(user)).or(SingularityUserSettings.empty()); + public SingularityUserSettings getUserSettings(@Auth SingularityUser user) { + return userManager.getUserSettings(user.getId()).or(SingularityUserSettings.empty()); } @POST @Path("/settings") - public void setUserSettings(@ApiParam("Update all settings for a user") SingularityUserSettings settings) { - userManager.updateUserSettings(authorizationHelper.getAuthUserId(user), settings); + public void setUserSettings(@Auth SingularityUser user, @ApiParam("Update all settings for a user") SingularityUserSettings settings) { + userManager.updateUserSettings(user.getId(), settings); } @POST @Path("/settings/starred-requests") - public void addStarredRequests(@ApiParam("Add starred requests for a user") SingularityUserSettings settings) { - userManager.addStarredRequestIds(authorizationHelper.getAuthUserId(user), settings.getStarredRequestIds()); + public void addStarredRequests(@Auth SingularityUser user, @ApiParam("Add starred requests for a user") SingularityUserSettings settings) { + userManager.addStarredRequestIds(user.getId(), settings.getStarredRequestIds()); } @DELETE @Path("/settings/starred-requests") - public void deleteStarredRequests(@ApiParam("Remove starred requests for a user") SingularityUserSettings settings) { - userManager.deleteStarredRequestIds(authorizationHelper.getAuthUserId(user), settings.getStarredRequestIds()); + public void deleteStarredRequests(@Auth SingularityUser user, @ApiParam("Remove starred requests for a user") SingularityUserSettings settings) { + userManager.deleteStarredRequestIds(user.getId(), settings.getStarredRequestIds()); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/WebhookResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/WebhookResource.java index 3a2b16bd00..1c4fe2cbf5 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/WebhookResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/WebhookResource.java @@ -11,7 +11,6 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; -import com.google.common.base.Optional; import com.google.inject.Inject; import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.SingularityAction; @@ -30,26 +29,26 @@ import com.wordnik.swagger.annotations.Api; import com.wordnik.swagger.annotations.ApiOperation; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.WEBHOOK_RESOURCE_PATH) @Produces({ MediaType.APPLICATION_JSON }) @Api(description="Manages Singularity webhooks.", value=ApiPaths.WEBHOOK_RESOURCE_PATH) public class WebhookResource { private final WebhookManager webhookManager; - private final Optional user; private final SingularityAuthorizationHelper authorizationHelper; private final SingularityValidator validator; @Inject - public WebhookResource(WebhookManager webhookManager, SingularityAuthorizationHelper authorizationHelper, Optional user, SingularityValidator validator) { + public WebhookResource(WebhookManager webhookManager, SingularityAuthorizationHelper authorizationHelper, SingularityValidator validator) { this.webhookManager = webhookManager; this.authorizationHelper = authorizationHelper; - this.user = user; this.validator = validator; } @GET @ApiOperation("Retrieve a list of active webhooks.") - public List getActiveWebhooks() { + public List getActiveWebhooks(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getActiveWebhooks(); } @@ -57,14 +56,14 @@ public List getActiveWebhooks() { @GET @Path("/summary") @ApiOperation("Retrieve a summary of each active webhook") - public List getWebhooksWithQueueSize() { + public List getWebhooksWithQueueSize(@Auth SingularityUser user) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getWebhooksWithQueueSize(); } @POST @ApiOperation("Add a new webhook.") - public SingularityCreateResult addWebhook(SingularityWebhook webhook) { + public SingularityCreateResult addWebhook(@Auth SingularityUser user, SingularityWebhook webhook) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(SingularityAction.ADD_WEBHOOK); validator.checkSingularityWebhook(webhook); @@ -75,7 +74,7 @@ public SingularityCreateResult addWebhook(SingularityWebhook webhook) { @Deprecated @Path("/{webhookId}") @ApiOperation("Delete a specific webhook.") - public SingularityDeleteResult deleteWebhookDeprecated(@PathParam("webhookId") String webhookId) { + public SingularityDeleteResult deleteWebhookDeprecated(@Auth SingularityUser user, @PathParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(SingularityAction.REMOVE_WEBHOOK); return webhookManager.deleteWebhook(JavaUtils.urlEncode(webhookId)); @@ -85,7 +84,7 @@ public SingularityDeleteResult deleteWebhookDeprecated(@PathParam("webhookId") S @Deprecated @Path("/deploy/{webhookId}") @ApiOperation("Retrieve a list of queued deploy updates for a specific webhook.") - public List getQueuedDeployUpdatesDeprecated(@PathParam("webhookId") String webhookId) { + public List getQueuedDeployUpdatesDeprecated(@Auth SingularityUser user, @PathParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedDeployUpdatesForHook(JavaUtils.urlEncode(webhookId)); } @@ -94,7 +93,7 @@ public List getQueuedDeployUpdatesDeprecated(@PathParam @Deprecated @Path("/request/{webhookId}") @ApiOperation("Retrieve a list of queued request updates for a specific webhook.") - public List getQueuedRequestUpdatesDeprecated(@PathParam("webhookId") String webhookId) { + public List getQueuedRequestUpdatesDeprecated(@Auth SingularityUser user, @PathParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedRequestHistoryForHook(JavaUtils.urlEncode(webhookId)); } @@ -103,14 +102,14 @@ public List getQueuedRequestUpdatesDeprecated(@PathPa @Deprecated @Path("/task/{webhookId}") @ApiOperation("Retrieve a list of queued task updates for a specific webhook.") - public List getQueuedTaskUpdatesDeprecated(@PathParam("webhookId") String webhookId) { + public List getQueuedTaskUpdatesDeprecated(@Auth SingularityUser user, @PathParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedTaskUpdatesForHook(JavaUtils.urlEncode(webhookId)); } @DELETE @ApiOperation("Delete a specific webhook.") - public SingularityDeleteResult deleteWebhook(@QueryParam("webhookId") String webhookId) { + public SingularityDeleteResult deleteWebhook(@Auth SingularityUser user, @QueryParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); validator.checkActionEnabled(SingularityAction.REMOVE_WEBHOOK); return webhookManager.deleteWebhook(JavaUtils.urlEncode(webhookId)); @@ -119,7 +118,7 @@ public SingularityDeleteResult deleteWebhook(@QueryParam("webhookId") String web @GET @Path("/deploy") @ApiOperation("Retrieve a list of queued deploy updates for a specific webhook.") - public List getQueuedDeployUpdates(@QueryParam("webhookId") String webhookId) { + public List getQueuedDeployUpdates(@Auth SingularityUser user, @QueryParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedDeployUpdatesForHook(JavaUtils.urlEncode(webhookId)); } @@ -127,7 +126,7 @@ public List getQueuedDeployUpdates(@QueryParam("webhook @GET @Path("/request") @ApiOperation("Retrieve a list of queued request updates for a specific webhook.") - public List getQueuedRequestUpdates(@QueryParam("webhookId") String webhookId) { + public List getQueuedRequestUpdates(@Auth SingularityUser user, @QueryParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedRequestHistoryForHook(JavaUtils.urlEncode(webhookId)); } @@ -135,7 +134,7 @@ public List getQueuedRequestUpdates(@QueryParam("webh @GET @Path("/task") @ApiOperation("Retrieve a list of queued task updates for a specific webhook.") - public List getQueuedTaskUpdates(@QueryParam("webhookId") String webhookId) { + public List getQueuedTaskUpdates(@Auth SingularityUser user, @QueryParam("webhookId") String webhookId) { authorizationHelper.checkAdminAuthorization(user); return webhookManager.getQueuedTaskUpdatesForHook(JavaUtils.urlEncode(webhookId)); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/sentry/SingularityExceptionNotifier.java b/SingularityService/src/main/java/com/hubspot/singularity/sentry/SingularityExceptionNotifier.java index 45ab11ffa1..36a996c837 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/sentry/SingularityExceptionNotifier.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/sentry/SingularityExceptionNotifier.java @@ -12,7 +12,6 @@ import com.google.inject.Provider; import com.google.inject.name.Named; import com.hubspot.singularity.SingularityMainModule; -import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.config.SentryConfiguration; import net.kencochrane.raven.Raven; @@ -21,20 +20,17 @@ import net.kencochrane.raven.event.EventBuilder; import net.kencochrane.raven.event.interfaces.ExceptionInterface; import net.kencochrane.raven.event.interfaces.HttpInterface; -import net.kencochrane.raven.event.interfaces.UserInterface; @Singleton public class SingularityExceptionNotifier { private final Optional raven; private final Optional sentryConfiguration; private final Provider> requestProvider; - private final Provider> userProvider; @Inject - public SingularityExceptionNotifier(Optional sentryConfiguration, @Named(SingularityMainModule.CURRENT_HTTP_REQUEST) Provider> requestProvider, Provider> userProvider) { + public SingularityExceptionNotifier(Optional sentryConfiguration, @Named(SingularityMainModule.CURRENT_HTTP_REQUEST) Provider> requestProvider) { this.sentryConfiguration = sentryConfiguration; this.requestProvider = requestProvider; - this.userProvider = userProvider; if (sentryConfiguration.isPresent()) { this.raven = Optional.of(RavenFactory.ravenInstance(sentryConfiguration.get().getDsn())); } else { @@ -88,12 +84,6 @@ public void notify(String message, Throwable t, Map extraData) { eventBuilder.withSentryInterface(new HttpInterface(maybeRequest.get())); } - final Optional maybeUser = userProvider.get(); - - if (maybeUser.isPresent()) { - eventBuilder.withSentryInterface(new UserInterface(maybeUser.get().getId(), maybeUser.get().getId(), "", maybeUser.get().getEmail().or(""))); - } - if (extraData != null && !extraData.isEmpty()) { for (Map.Entry entry : extraData.entrySet()) { eventBuilder.withExtra(entry.getKey(), entry.getValue()); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java b/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java index 6a502991b2..e51b0d0152 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java @@ -58,11 +58,11 @@ public static SingularityConfiguration buildAuthEnabledConfig(Set requir public static final SingularityRequest REQUEST_WITH_GROUP_B = new SingularityRequestBuilder("test_b", RequestType.SERVICE).setGroup(Optional.of("b")).build(); - public static final Optional NOT_LOGGED_IN = Optional.absent(); - public static final Optional USER_GROUP_A = Optional.of(new SingularityUser("test1", Optional.of("test user1"), Optional.of("test1@test.com"), ImmutableSet.of("a"))); - public static final Optional USER_GROUP_AB = Optional.of(new SingularityUser("test2", Optional.of("test user2"), Optional.of("test2@test.com"), ImmutableSet.of("a", "b"))); - public static final Optional USER_GROUP_B = Optional.of(new SingularityUser("test3", Optional.of("test user3"), Optional.of("test3@test.com"), ImmutableSet.of("b"))); - public static final Optional USER_GROUP_ADMIN = Optional.of(new SingularityUser("admin", Optional.of("admin user"), Optional.of("admin@test.com"), ImmutableSet.of("admin"))); + public static final SingularityUser NOT_LOGGED_IN = SingularityUser.defaultUser(); + public static final SingularityUser USER_GROUP_A = new SingularityUser("test1", Optional.of("test user1"), Optional.of("test1@test.com"), ImmutableSet.of("a")); + public static final SingularityUser USER_GROUP_AB = new SingularityUser("test2", Optional.of("test user2"), Optional.of("test2@test.com"), ImmutableSet.of("a", "b")); + public static final SingularityUser USER_GROUP_B = new SingularityUser("test3", Optional.of("test user3"), Optional.of("test3@test.com"), ImmutableSet.of("b")); + public static final SingularityUser USER_GROUP_ADMIN = new SingularityUser("admin", Optional.of("admin user"), Optional.of("admin@test.com"), ImmutableSet.of("admin")); private SingularityAuthorizationHelper buildAuthorizationHelper(SingularityConfiguration configuration) { return new SingularityAuthorizationHelper(requestManager, configuration); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/SingularityHistoryTest.java b/SingularityService/src/test/java/com/hubspot/singularity/SingularityHistoryTest.java index 60cf0956ed..95dd9cc683 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/SingularityHistoryTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/SingularityHistoryTest.java @@ -1,8 +1,7 @@ package com.hubspot.singularity; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.List; @@ -20,7 +19,6 @@ import com.google.common.base.Optional; import com.google.inject.Inject; import com.google.inject.Provider; -import com.hubspot.mesos.Resources; import com.hubspot.singularity.SingularityRequestHistory.RequestHistoryType; import com.hubspot.singularity.api.SingularityDeleteRequestRequest; import com.hubspot.singularity.api.SingularityRunNowRequest; @@ -199,8 +197,8 @@ public void testRunId() { String runId = "my-run-id"; - SingularityPendingRequestParent parent = requestResource.scheduleImmediately(requestId, - new SingularityRunNowRequest(Optional. absent(), Optional. absent(), Optional.of(runId), Optional.> absent(), Optional.absent(), Optional.absent())); + SingularityPendingRequestParent parent = requestResource.scheduleImmediately(singularityUser, requestId, + new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.of(runId), Optional.absent(), Optional.absent(), Optional.absent())); Assert.assertEquals(runId, parent.getPendingRequest().getRunId().get()); @@ -220,7 +218,7 @@ public void testRunId() { Assert.assertEquals(runId, historyManager.getTaskHistory(taskId.getId()).get().getTask().getTaskRequest().getPendingTask().getRunId().get()); Assert.assertEquals(runId, getTaskHistoryForRequest(requestId, 0, 10).get(0).getRunId().get()); - parent = requestResource.scheduleImmediately(requestId); + parent = requestResource.scheduleImmediately(singularityUser, requestId); Assert.assertTrue(parent.getPendingRequest().getRunId().isPresent()); } @@ -261,7 +259,7 @@ public void testPersisterRaceCondition() { initScheduledRequest(); initFirstDeploy(); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); resourceOffers(); @@ -490,8 +488,8 @@ public void testMessage() { msg = msg + i; } - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(2), Optional. absent(), Optional. absent(), Optional. absent(), Optional.of(msg), Optional.absent(), Optional.absent())); - requestResource.deleteRequest(requestId, Optional.of(new SingularityDeleteRequestRequest(Optional.of("a msg"), Optional. absent(), Optional.absent()))); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(2), Optional.absent(), Optional.absent(), Optional.absent(), Optional.of(msg), Optional.absent(), Optional.absent()), singularityUser); + requestResource.deleteRequest(requestId, Optional.of(new SingularityDeleteRequestRequest(Optional.of("a msg"), Optional.absent(), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/SingularityTestAuthenticator.java b/SingularityService/src/test/java/com/hubspot/singularity/SingularityTestAuthenticator.java index 07b1da5e9e..1816ddd5de 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/SingularityTestAuthenticator.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/SingularityTestAuthenticator.java @@ -1,11 +1,14 @@ package com.hubspot.singularity; -import com.google.common.base.Optional; +import java.util.Optional; + +import javax.ws.rs.container.ContainerRequestContext; + import com.google.inject.Inject; import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; public class SingularityTestAuthenticator implements SingularityAuthenticator { - private Optional user = Optional.absent(); + private Optional user = Optional.empty(); @Inject public SingularityTestAuthenticator() { @@ -13,7 +16,7 @@ public SingularityTestAuthenticator() { } @Override - public Optional get() { + public Optional getUser(ContainerRequestContext context) { return user; } diff --git a/SingularityService/src/test/java/com/hubspot/singularity/data/StateManagerTest.java b/SingularityService/src/test/java/com/hubspot/singularity/data/StateManagerTest.java index 770cdd6106..25f2aa9b9e 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/data/StateManagerTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/data/StateManagerTest.java @@ -28,7 +28,7 @@ public void itDoesntCountCleaningTasks() { initRequest(); initFirstDeploy(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); saveAndSchedule(request.toBuilder().setInstances(Optional.of(3))); resourceOffers(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityStartupTest.java b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityStartupTest.java index 470674e03d..b86e00d0c6 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityStartupTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityStartupTest.java @@ -112,7 +112,7 @@ public void testScheduledTasksDontGetRescheduledDuringRun() { boolean caughtException = false; try { - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); } catch (Exception e) { caughtException = true; } diff --git a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityTaskShellCommandTest.java b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityTaskShellCommandTest.java index f924130e94..5e2c91fc11 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityTaskShellCommandTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/mesos/SingularityTaskShellCommandTest.java @@ -63,19 +63,19 @@ public void testTaskShellCommandPersistence() { // bad shell cmd try { - taskResource.runShellCommand(task.getTaskId().getId(), new SingularityShellCommand("test-cmd", Optional.of(Arrays.asList("one", "two")), user, Optional.absent())); + taskResource.runShellCommand(singularityUser, task.getTaskId().getId(), new SingularityShellCommand("test-cmd", Optional.of(Arrays.asList("one", "two")), user, Optional.absent())); } catch (WebApplicationException exception) { assertEquals(400, exception.getResponse().getStatus()); } // bad option try { - taskResource.runShellCommand(task.getTaskId().getId(), new SingularityShellCommand("d1", Optional.of(Arrays.asList("one", "two")), user, Optional.absent())); + taskResource.runShellCommand(singularityUser, task.getTaskId().getId(), new SingularityShellCommand("d1", Optional.of(Arrays.asList("one", "two")), user, Optional.absent())); } catch (WebApplicationException exception) { assertEquals(400, exception.getResponse().getStatus()); } - SingularityTaskShellCommandRequest firstShellRequest = taskResource.runShellCommand(task.getTaskId().getId(), new SingularityShellCommand("d1", Optional.of(Arrays.asList("o1", "o2")), user, Optional.absent())); + SingularityTaskShellCommandRequest firstShellRequest = taskResource.runShellCommand(singularityUser, task.getTaskId().getId(), new SingularityShellCommand("d1", Optional.of(Arrays.asList("o1", "o2")), user, Optional.absent())); try { Thread.sleep(3); @@ -83,7 +83,7 @@ public void testTaskShellCommandPersistence() { } - SingularityTaskShellCommandRequest secondShellRequest = taskResource.runShellCommand(task.getTaskId().getId(), new SingularityShellCommand("d2", Optional.> absent(), user, Optional.absent())); + SingularityTaskShellCommandRequest secondShellRequest = taskResource.runShellCommand(singularityUser, task.getTaskId().getId(), new SingularityShellCommand("d2", Optional.> absent(), user, Optional.absent())); assertEquals(2, taskManager.getAllQueuedTaskShellCommandRequests().size()); @@ -127,8 +127,8 @@ public void testRunCommandBeforeBounceKill() { launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); - requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), - Optional.of(new SingularityShellCommand("d1", Optional.of(Arrays.asList("o1", "o2")), user, Optional.absent()))))); + requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), + Optional.of(new SingularityShellCommand("d1", Optional.of(Arrays.asList("o1", "o2")), user, Optional.absent())))), singularityUser); cleaner.drainCleanupQueue(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityDeploysTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityDeploysTest.java index 0ddd3c75a2..011058206c 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityDeploysTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityDeploysTest.java @@ -110,9 +110,9 @@ public void testDeployClearsObsoleteScheduledTasks() { public void testDeployAllInstancesAtOnce() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); @@ -144,9 +144,9 @@ public void testDeployAllInstancesAtOnce() { public void testDeployOneInstanceAtATime() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); @@ -200,8 +200,8 @@ public void testIncrementalDeployCancel() { initRequest(); // Set up incremental deploy that is partly finished - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); SingularityTask firstTask = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); @@ -221,7 +221,7 @@ public void testIncrementalDeployCancel() { resourceOffers(); // End in-progress incremental deploy setup - deployResource.cancelDeploy(requestId, secondDeployId); + deployResource.cancelDeploy(singularityUser, requestId, secondDeployId); deployChecker.checkDeploys(); Assert.assertEquals(taskManager.getCleanupTasks().get(0).getCleanupType(), TaskCleanupType.INCREMENTAL_DEPLOY_CANCELLED); @@ -239,9 +239,9 @@ public void testIncrementalDeployCancel() { public void testScaleDownDuringDeploy() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); @@ -259,7 +259,7 @@ public void testScaleDownDuringDeploy() { statusUpdate(taskManager.getTask(taskId).get(), TaskState.TASK_RUNNING); } - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(1)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(1)).build(), singularityUser); scheduler.drainPendingQueue(); Assert.assertEquals(1, taskManager.getCleanupTaskIds().size()); @@ -276,9 +276,9 @@ public void testScaleDownDuringDeploy() { public void testDeployWithManualStep() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); @@ -314,7 +314,7 @@ public void testDeployWithManualStep() { Assert.assertEquals(1, deployProgressStepOne.getTargetActiveInstances()); // Add the 'ok' to move to the next step - deployResource.updatePendingDeploy(new SingularityUpdatePendingDeployRequest(requestId, secondDeployId, 2)); + deployResource.updatePendingDeploy(singularityUser, new SingularityUpdatePendingDeployRequest(requestId, secondDeployId, 2)); deployChecker.checkDeploys(); @@ -341,9 +341,9 @@ public void testDeployWithManualStep() { public void testDeployMultipleInstancesAtOnce() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).build(), singularityUser); initFirstDeploy(); @@ -401,7 +401,7 @@ public void testDeployMultipleInstancesAtOnce() { public void testCancelDeploy() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); initFirstDeploy(); @@ -417,7 +417,7 @@ public void testCancelDeploy() { SingularityTaskId firstNewTaskId = taskManager.getActiveTaskIdsForDeploy(requestId, secondDeployId).get(0); statusUpdate(taskManager.getTask(firstNewTaskId).get(), TaskState.TASK_RUNNING); - deployResource.cancelDeploy(requestId, secondDeployId); + deployResource.cancelDeploy(singularityUser, requestId, secondDeployId); deployChecker.checkDeploys(); @@ -431,7 +431,7 @@ public void testCancelDeploy() { public void testDeployFails() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); initFirstDeploy(); @@ -458,7 +458,7 @@ public void testDeployFails() { public void testDeployFailsForInvalidRequestState() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); initFirstDeploy(); @@ -517,8 +517,8 @@ public void testDeploySucceedsWithTaskRetries() { public void testLbUpdatesAfterEachDeployStep() { initLoadBalancedRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); SingularityTask firstTask = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); SingularityTask secondTask = launchTask(request, firstDeploy, 2, TaskState.TASK_RUNNING); @@ -594,9 +594,9 @@ public void testLbUpdatesAfterEachDeployStep() { public void testCanceledDeployTasksStayActiveUntilReplaced() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); @@ -625,7 +625,7 @@ public void testCanceledDeployTasksStayActiveUntilReplaced() { statusUpdate(firstTask, TaskState.TASK_KILLED); deployChecker.checkDeploys(); - deployResource.cancelDeploy(requestId, secondDeployId); + deployResource.cancelDeploy(singularityUser, requestId, secondDeployId); deployChecker.checkDeploys(); scheduler.drainPendingQueue(); @@ -789,13 +789,13 @@ public void testUsesNewRequestDataFromPendingDeploy() { Assert.assertEquals(2, taskManager.getPendingTaskIds().size()); Assert.assertEquals(2, requestManager.getRequest(requestId).get().getRequest().getInstancesSafe()); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); SingularityRequest newRequest = request.toBuilder().setInstances(Optional.of(1)).build(); String deployId = "test_new_request_data"; SingularityDeploy deploy = new SingularityDeployBuilder(request.getId(), deployId).setCommand(Optional.of("sleep 100")).build(); - deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.of(newRequest))); + deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.of(newRequest)), singularityUser); deployChecker.checkDeploys(); scheduler.drainPendingQueue(); @@ -819,19 +819,18 @@ public void testUsesNewRequestDataFromPendingDeploy() { Assert.assertEquals(1, requestManager.getRequest(requestId).get().getRequest().getInstancesSafe()); } - @Test(expected = WebApplicationException.class) public void testCannotUpdateRequestDuringPendingDeployWithNewData() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); SingularityRequest newRequest = request.toBuilder().setInstances(Optional.of(1)).build(); String deployId = "test_new_request_data"; SingularityDeploy deploy = new SingularityDeployBuilder(request.getId(), deployId).setCommand(Optional.of("sleep 100")).build(); - deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.of(newRequest))); + deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.of(newRequest)), singularityUser); - requestResource.postRequest(newRequest); + requestResource.postRequest(newRequest, singularityUser); } @Test @@ -855,8 +854,8 @@ public void testDeployTimesOut() { @Test public void testIncrementalDeployInstanceCounter() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).build()); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).build(), singularityUser); initFirstDeploy(); SingularityTask firstTask = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); @@ -917,7 +916,7 @@ public void testDeployWithImmediateRunIsLaunchedImmediately() { .setCommand(Optional.of("printenv > tmp.txt")) .build(); SingularityDeployRequest deployRequest = new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent()); - deployResource.deploy(deployRequest); + deployResource.deploy(deployRequest, singularityUser); deployChecker.checkDeploys(); scheduler.drainPendingQueue(); @@ -942,7 +941,7 @@ public void testDeployWithImmediateRunSchedulesAfterRunningImmediately() { .setCommand(Optional.of("printenv > tmp.txt")) .build(); SingularityDeployRequest deployRequest = new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent()); - deployResource.deploy(deployRequest); + deployResource.deploy(deployRequest, singularityUser); deployChecker.checkDeploys(); scheduler.drainPendingQueue(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityExpiringActionsTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityExpiringActionsTest.java index d8ff317951..83f6cacdc1 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityExpiringActionsTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityExpiringActionsTest.java @@ -6,7 +6,6 @@ import com.google.common.base.Optional; import com.hubspot.singularity.RequestState; -import com.hubspot.singularity.SingularityShellCommand; import com.hubspot.singularity.SingularityTask; import com.hubspot.singularity.api.SingularityBounceRequest; import com.hubspot.singularity.api.SingularityPauseRequest; @@ -26,7 +25,7 @@ public void testExpiringPause() { SingularityTask taskOne = startTask(firstDeploy); - requestResource.pause(requestId, Optional.of(new SingularityPauseRequest(Optional. absent(), Optional.of(1L), Optional. absent(), Optional.absent(), Optional.absent()))); + requestResource.pause(requestId, Optional.of(new SingularityPauseRequest(Optional.absent(), Optional.of(1L), Optional.absent(), Optional.absent(), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); @@ -65,7 +64,7 @@ public void testExpiringBounceGoesAway() { startTask(firstDeploy, 1); requestResource.bounce(requestId, - Optional.of(new SingularityBounceRequest(Optional.of(false), Optional. absent(), Optional.of(1L), Optional. absent(), Optional.of("msg"), Optional.absent()))); + Optional.of(new SingularityBounceRequest(Optional.of(false), Optional.absent(), Optional.of(1L), Optional.absent(), Optional.of("msg"), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); resourceOffers(); @@ -84,7 +83,7 @@ public void testExpiringNonIncrementalBounce() { initWithTasks(3); requestResource.bounce(requestId, - Optional.of(new SingularityBounceRequest(Optional. absent(), Optional. absent(), Optional.of(1L), Optional.of("aid"), Optional. absent(), Optional.absent()))); + Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.absent(), Optional.of(1L), Optional.of("aid"), Optional.absent(), Optional.absent())), singularityUser); Assert.assertTrue(!requestManager.getCleanupRequests().get(0).getMessage().isPresent()); Assert.assertEquals("aid", requestManager.getCleanupRequests().get(0).getActionId().get()); @@ -129,7 +128,7 @@ public void testExpiringNonIncrementalBounce() { public void testExpiringIncrementalBounce() { initRequest(); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional. absent(), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); initFirstDeploy(); @@ -138,7 +137,7 @@ public void testExpiringIncrementalBounce() { startTask(firstDeploy, 3); requestResource.bounce(requestId, - Optional.of(new SingularityBounceRequest(Optional.of(true), Optional. absent(), Optional.of(1L), Optional. absent(), Optional.of("msg"), Optional.absent()))); + Optional.of(new SingularityBounceRequest(Optional.of(true), Optional.absent(), Optional.of(1L), Optional.absent(), Optional.of("msg"), Optional.absent())), singularityUser); Assert.assertTrue(requestManager.cleanupRequestExists(requestId)); Assert.assertEquals("msg", requestManager.getCleanupRequests().get(0).getMessage().get()); @@ -187,7 +186,7 @@ public void testExpiringScale() { initRequest(); initFirstDeploy(); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); try { Thread.sleep(2); @@ -214,7 +213,7 @@ public void testExpiringSkipHealthchecks() { Assert.assertTrue(healthchecker.cancelHealthcheck(firstTask.getTaskId().getId())); - requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(true), Optional.of(1L), Optional. absent(), Optional.absent())); + requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(true), Optional.of(1L), Optional.absent(), Optional.absent()), singularityUser); statusUpdate(firstTask, TaskState.TASK_FAILED); @@ -236,9 +235,9 @@ public void testExpiringScaleWithBounce() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setBounceAfterScale(Optional.of(true)).build()); + requestResource.postRequest(request.toBuilder().setBounceAfterScale(Optional.of(true)).build(), singularityUser); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); Assert.assertEquals(1, requestManager.getCleanupRequests().size()); cleaner.drainCleanupQueue(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityHealthchecksTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityHealthchecksTest.java index e9a09f9e2d..69c00adce0 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityHealthchecksTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityHealthchecksTest.java @@ -17,7 +17,6 @@ import com.hubspot.singularity.DeployState; import com.hubspot.singularity.SingularityDeploy; import com.hubspot.singularity.SingularityDeployBuilder; -import com.hubspot.singularity.SingularityShellCommand; import com.hubspot.singularity.SingularityTask; import com.hubspot.singularity.SingularityTaskHealthcheckResult; import com.hubspot.singularity.SingularityTaskId; @@ -42,7 +41,7 @@ public void testSkipHealthchecksEdgeCases() { initRequest(); initHCDeploy(); - requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(Boolean.TRUE), Optional. absent(), Optional. absent(), Optional. absent())); + requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(Boolean.TRUE), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); SingularityTask firstTask = startTask(firstDeploy, 1); @@ -53,7 +52,7 @@ public void testSkipHealthchecksEdgeCases() { Assert.assertEquals(1, taskManager.getNumActiveTasks()); - requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(Boolean.FALSE), Optional. absent(), Optional. absent(), Optional. absent())); + requestResource.skipHealthchecks(requestId, new SingularitySkipHealthchecksRequest(Optional.of(Boolean.FALSE), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); // run new task check ONLY. newTaskChecker.enqueueNewTaskCheck(firstTask, requestManager.getRequest(requestId), healthchecker); @@ -79,7 +78,7 @@ public void testSkipHealthchecksDuringBounce() { SingularityTask firstTask = startTask(firstDeploy, 1); - requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional. absent(), Optional.of(true), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent()))); + requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.of(true), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent())), singularityUser); setConfigurationForNoDelay(); @@ -121,7 +120,7 @@ public void testHealthchecksDuringBounce() { startTask(firstDeploy); - requestResource.bounce(requestId, Optional.absent()); + requestResource.bounce(requestId, Optional.absent(), singularityUser); cleaner.drainCleanupQueue(); @@ -373,7 +372,7 @@ public void testPortIndices() { firstDeploy = initAndFinishDeploy(request, new SingularityDeployBuilder(request.getId(), firstDeployId).setCommand(Optional.of("sleep 100")) .setHealthcheck(Optional.of(options)), Optional.of(new Resources(1, 64, 3, 0))); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); scheduler.drainPendingQueue(); String[] portRange = {"80:82"}; @@ -404,7 +403,7 @@ public void testPortNumber() { .setCommand(Optional.of("sleep 100")).setResources(Optional.of(new Resources(1, 64, 3, 0))) .setHealthcheck(Optional.of(options)), Optional.absent()); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); scheduler.drainPendingQueue(); String[] portRange = {"80:82"}; diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityMachineStatesTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityMachineStatesTest.java index 9e33a5fa66..299f3d70f9 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityMachineStatesTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityMachineStatesTest.java @@ -218,7 +218,7 @@ public void testDecommissioning() { Assert.assertEquals(0, taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave1").get()).size()); - slaveResource.activateSlave("slave1", null); + slaveResource.activateSlave(singularityUser, "slave1", null); sms.resourceOffers(Arrays.asList(createOffer(1, 129, "slave1", "host1", Optional.of("rack1")))); @@ -437,7 +437,7 @@ public void testExpiringMachineState() { SingularitySlave slave = slaveManager.getObjects().get(0); - slaveResource.freezeSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.ACTIVE), Optional.absent())); + slaveResource.freezeSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.ACTIVE), Optional.absent())); Assert.assertEquals(MachineState.FROZEN, slaveManager.getObjects().get(0).getCurrentState().getState()); @@ -454,31 +454,31 @@ private SingularitySlave getSingleSlave() { @Test(expected = WebApplicationException.class) public void testCannotUseStateReservedForSystem() { SingularitySlave slave = getSingleSlave(); - slaveResource.freezeSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DEAD), Optional.absent())); + slaveResource.freezeSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DEAD), Optional.absent())); } @Test(expected = WebApplicationException.class) public void testBadExpiringStateTransition() { SingularitySlave slave = getSingleSlave(); - slaveResource.decommissionSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.FROZEN), Optional.absent())); + slaveResource.decommissionSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.FROZEN), Optional.absent())); } @Test(expected = WebApplicationException.class) public void testInvalidTransitionToDecommissioned() { SingularitySlave slave = getSingleSlave(); - slaveResource.decommissionSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DECOMMISSIONED), Optional.absent())); + slaveResource.decommissionSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DECOMMISSIONED), Optional.absent())); } @Test public void testValidTransitionToDecommissioned() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); scheduler.drainPendingQueue(); resourceOffers(1); SingularitySlave slave = slaveManager.getObjects().get(0); - slaveResource.decommissionSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DECOMMISSIONED), Optional.of(true))); + slaveResource.decommissionSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.DECOMMISSIONED), Optional.of(true))); Assert.assertEquals(MachineState.STARTING_DECOMMISSION, slaveManager.getObjects().get(0).getCurrentState().getState()); scheduler.checkForDecomissions(); scheduler.drainPendingQueue(); @@ -492,10 +492,10 @@ public void testValidTransitionToDecommissioned() { @Test public void testSystemChangeClearsExpiringChangeIfInvalid() { SingularitySlave slave = getSingleSlave(); - slaveResource.freezeSlave(slave.getId(), null); - slaveResource.activateSlave(slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.FROZEN), Optional.absent())); + slaveResource.freezeSlave(singularityUser, slave.getId(), null); + slaveResource.activateSlave(singularityUser, slave.getId(), new SingularityMachineChangeRequest(Optional.of(1L), Optional.absent(), Optional.absent(), Optional.of(MachineState.FROZEN), Optional.absent())); Assert.assertTrue(slaveManager.getExpiringObject(slave.getId()).isPresent()); - slaveResource.decommissionSlave(slave.getId(), null); + slaveResource.decommissionSlave(singularityUser, slave.getId(), null); Assert.assertFalse(slaveManager.getExpiringObject(slave.getId()).isPresent()); } diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTest.java index e219fdd86a..5c20a206cd 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTest.java @@ -82,7 +82,6 @@ import com.hubspot.singularity.api.SingularityPriorityFreeze; import com.hubspot.singularity.api.SingularityRunNowRequest; import com.hubspot.singularity.api.SingularityScaleRequest; -import com.hubspot.singularity.api.SingularityUnpauseRequest; import com.hubspot.singularity.data.AbstractMachineManager.StateChangeResult; import com.hubspot.singularity.data.SingularityValidator; import com.hubspot.singularity.mesos.OfferCache; @@ -129,7 +128,7 @@ public void testOfferCacheRescindOffers() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setSlavePlacement(Optional.of(SlavePlacement.SEPARATE)).setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setSlavePlacement(Optional.of(SlavePlacement.SEPARATE)).setInstances(Optional.of(2)).build(), singularityUser); schedulerPoller.runActionOnPoll(); @@ -163,7 +162,7 @@ public void testOfferCache() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setSlavePlacement(Optional.of(SlavePlacement.SEPARATE)).setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setSlavePlacement(Optional.of(SlavePlacement.SEPARATE)).setInstances(Optional.of(2)).build(), singularityUser); schedulerPoller.runActionOnPoll(); @@ -305,7 +304,7 @@ public void testCleanerLeavesPausedRequestTasksByDemand() { SingularityTask firstTask = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); createAndSchedulePendingTask(firstDeployId); - requestResource.pause(requestId, Optional.of(new SingularityPauseRequest(Optional.of(false), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent()))); + requestResource.pause(requestId, Optional.of(new SingularityPauseRequest(Optional.of(false), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); @@ -326,7 +325,7 @@ public void testTaskKill() { SingularityTask firstTask = startTask(firstDeploy); - taskResource.killTask(firstTask.getTaskId().getId(), Optional.absent()); + taskResource.killTask(firstTask.getTaskId().getId(), Optional.absent(), singularityUser); cleaner.drainCleanupQueue(); killKilledTasks(); @@ -345,7 +344,7 @@ public void testTaskDestroy() { SingularityTask thirdTask = startTask(firstDeploy, 3); taskResource.killTask(secondTask.getTaskId().getId(), Optional.of( - new SingularityKillTaskRequest(Optional.of(true), Optional.of("kill -9 bb"), Optional. absent(), Optional. absent(), Optional. absent()))); + new SingularityKillTaskRequest(Optional.of(true), Optional.of("kill -9 bb"), Optional.absent(), Optional.absent(), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); killKilledTasks(); @@ -363,7 +362,7 @@ public void testTaskBounce() { SingularityTask firstTask = startTask(firstDeploy); taskResource.killTask(firstTask.getTaskId().getId(), Optional.of( - new SingularityKillTaskRequest(Optional. absent(), Optional.of("msg"), Optional. absent(), Optional.of(true), Optional.absent()))); + new SingularityKillTaskRequest(Optional.absent(), Optional.of("msg"), Optional.absent(), Optional.of(true), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); @@ -396,7 +395,7 @@ public void testBounceWithLoadBalancer() { saveLoadBalancerState(BaragonRequestState.SUCCESS, taskOne.getTaskId(), LoadBalancerRequestType.ADD); - requestResource.bounce(requestId, Optional. absent()); + requestResource.bounce(requestId, Optional.absent(), singularityUser); cleaner.drainCleanupQueue(); resourceOffers(); @@ -443,7 +442,7 @@ public void testKilledTaskIdRecords() { launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); - requestResource.deleteRequest(requestId, Optional. absent()); + requestResource.deleteRequest(requestId, Optional.absent(), singularityUser); Assert.assertTrue(requestManager.getCleanupRequests().size() == 1); @@ -489,7 +488,7 @@ public void testSchedulerCanBatchOnOffers() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(3)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(3)).build(), singularityUser); scheduler.drainPendingQueue(); List oneOffer = Arrays.asList(createOffer(12, 1024)); @@ -505,7 +504,7 @@ public void testSchedulerExhaustsOffers() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(10)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(10)).build(), singularityUser); scheduler.drainPendingQueue(); sms.resourceOffers(Arrays.asList(createOffer(2, 1024), createOffer(1, 1024))); @@ -519,7 +518,7 @@ public void testSchedulerRandomizesOffers() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(15)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(15)).build(), singularityUser); scheduler.drainPendingQueue(); sms.resourceOffers(Arrays.asList(createOffer(20, 1024), createOffer(20, 1024))); @@ -543,18 +542,18 @@ public void testSchedulerHandlesFinishedTasks() { schedule = "*/1 * * * * ? 1995"; // cause it to be pending - requestResource.postRequest(request.toBuilder().setQuartzSchedule(Optional.of(schedule)).build()); + requestResource.postRequest(request.toBuilder().setQuartzSchedule(Optional.of(schedule)).build(), singularityUser); scheduler.drainPendingQueue(); - Assert.assertTrue(requestResource.getActiveRequests(false).isEmpty()); + Assert.assertTrue(requestResource.getActiveRequests(singularityUser, false).isEmpty()); Assert.assertTrue(requestManager.getRequest(requestId).get().getState() == RequestState.FINISHED); Assert.assertTrue(taskManager.getPendingTaskIds().isEmpty()); schedule = "*/1 * * * * ?"; - requestResource.postRequest(request.toBuilder().setQuartzSchedule(Optional.of(schedule)).build()); + requestResource.postRequest(request.toBuilder().setQuartzSchedule(Optional.of(schedule)).build(), singularityUser); scheduler.drainPendingQueue(); - Assert.assertTrue(!requestResource.getActiveRequests(false).isEmpty()); + Assert.assertTrue(!requestResource.getActiveRequests(singularityUser, false).isEmpty()); Assert.assertTrue(requestManager.getRequest(requestId).get().getState() == RequestState.ACTIVE); Assert.assertTrue(!taskManager.getPendingTaskIds().isEmpty()); @@ -563,7 +562,7 @@ public void testSchedulerHandlesFinishedTasks() { @Test public void testOneOffsDontRunByThemselves() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); Assert.assertTrue(requestManager.getPendingRequests().isEmpty()); deploy("d2"); @@ -573,7 +572,7 @@ public void testOneOffsDontRunByThemselves() { Assert.assertTrue(requestManager.getPendingRequests().isEmpty()); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); resourceOffers(); @@ -585,7 +584,7 @@ public void testOneOffsDontRunByThemselves() { Assert.assertEquals(0, taskManager.getActiveTaskIds().size()); Assert.assertEquals(0, taskManager.getPendingTaskIds().size()); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); resourceOffers(); @@ -601,10 +600,10 @@ public void testOneOffsDontRunByThemselves() { @Test public void testOneOffsDontMoveDuringDecomission() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("d2"); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); validateTaskDoesntMoveDuringDecommission(); } @@ -637,11 +636,11 @@ private void validateTaskDoesntMoveDuringDecommission() { @Test public void testCustomResourcesWithRunNowRequest() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("d2"); SingularityRunNowRequest runNowRequest = new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.>absent(), Optional.of(new Resources(2, 2, 0)), Optional.absent()); - requestResource.scheduleImmediately(requestId, runNowRequest); + requestResource.scheduleImmediately(singularityUser, requestId, runNowRequest); scheduler.drainPendingQueue(); @@ -661,7 +660,7 @@ public void testRunOnceRunOnlyOnce() { request = bldr.build(); saveRequest(request); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); @@ -680,7 +679,7 @@ public void testRunOnceRunOnlyOnce() { Assert.assertTrue(taskManager.getActiveTaskIds().isEmpty()); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d2").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d2").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); @@ -707,11 +706,11 @@ public void testMultipleRunOnceTasks() { request = bldr.build(); saveRequest(request); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); deployChecker.checkDeploys(); Assert.assertEquals(1, requestManager.getSizeOfPendingQueue()); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d2").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d2").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); deployChecker.checkDeploys(); Assert.assertEquals(2, requestManager.getSizeOfPendingQueue()); @@ -727,7 +726,7 @@ public void testRunOnceDontMoveDuringDecomission() { request = bldr.build(); saveRequest(request); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); @@ -740,7 +739,7 @@ public void testRunOnceDontMoveDuringDecomission() { public void testDecommissionDoesntKillPendingDeploy() { initRequest(); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); deployChecker.checkDeploys(); @@ -748,7 +747,7 @@ public void testDecommissionDoesntKillPendingDeploy() { Assert.assertEquals(1, taskManager.getNumActiveTasks()); - slaveResource.decommissionSlave(taskManager.getActiveTasks().get(0).getAgentId().getValue(), null); + slaveResource.decommissionSlave(singularityUser, taskManager.getActiveTasks().get(0).getAgentId().getValue(), null); scheduler.checkForDecomissions(); @@ -778,7 +777,7 @@ public void testRetries() { request = bldr.setNumRetriesOnFailure(Optional.of(2)).build(); saveRequest(request); - deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(new SingularityDeployBuilder(requestId, "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); deployChecker.checkDeploys(); @@ -982,7 +981,7 @@ public void testLbCleanupSkippedOnSkipRemoveFlag() { boolean removeFromLoadBalancer = false; SingularityDeleteRequestRequest deleteRequest = new SingularityDeleteRequestRequest(Optional.absent(), Optional.absent(), Optional.of(removeFromLoadBalancer)); - requestResource.deleteRequest(requestId, Optional.of(deleteRequest)); + requestResource.deleteRequest(requestId, Optional.of(deleteRequest), singularityUser); testingLbClient.setNextBaragonRequestState(BaragonRequestState.WAITING); @@ -1003,7 +1002,7 @@ public void testLbCleanupOccursOnRequestDelete() { initLoadBalancedDeploy(); startTask(firstDeploy); - requestResource.deleteRequest(requestId, Optional.absent()); + requestResource.deleteRequest(requestId, Optional.absent(), singularityUser); testingLbClient.setNextBaragonRequestState(BaragonRequestState.WAITING); @@ -1149,7 +1148,7 @@ public void testPause() { SingularityTask taskOne = startTask(firstDeploy); - requestResource.pause(requestId, Optional. absent()); + requestResource.pause(requestId, Optional.absent(), singularityUser); cleaner.drainCleanupQueue(); @@ -1164,7 +1163,7 @@ public void testPause() { Assert.assertEquals(RequestState.PAUSED, requestManager.getRequest(requestId).get().getState()); Assert.assertEquals(requestId, requestManager.getPausedRequests(false).iterator().next().getRequest().getId()); - requestResource.unpause(requestId, Optional. absent()); + requestResource.unpause(requestId, Optional.absent(), singularityUser); resourceOffers(); @@ -1179,7 +1178,7 @@ public void testPause() { public void testBounce() { initRequest(); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional. absent(), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); initFirstDeploy(); @@ -1187,7 +1186,7 @@ public void testBounce() { SingularityTask taskTwo = startTask(firstDeploy, 2); SingularityTask taskThree = startTask(firstDeploy, 3); - requestResource.bounce(requestId, Optional. absent()); + requestResource.bounce(requestId, Optional.absent(), singularityUser); Assert.assertTrue(requestManager.cleanupRequestExists(requestId)); @@ -1225,7 +1224,7 @@ public void testBounce() { public void testIncrementalBounceShutsDownOldTasksPerNewHealthyTask() { initRequest(); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional. absent(), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); initFirstDeploy(); @@ -1234,7 +1233,7 @@ public void testIncrementalBounceShutsDownOldTasksPerNewHealthyTask() { startTask(firstDeploy, 3); requestResource.bounce(requestId, - Optional.of(new SingularityBounceRequest(Optional.of(true), Optional.absent(), Optional.of(1L), Optional.absent(), Optional.of("msg"), Optional.absent()))); + Optional.of(new SingularityBounceRequest(Optional.of(true), Optional.absent(), Optional.of(1L), Optional.absent(), Optional.of("msg"), Optional.absent())), singularityUser); Assert.assertTrue(requestManager.cleanupRequestExists(requestId)); @@ -1269,7 +1268,7 @@ public void testBounceOnPendingInstancesReleasesLock() { Assert.assertEquals("Bounce starts when tasks have not yet been launched", 0, taskManager.getActiveTaskIds().size()); - requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.of(true), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()))); + requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.of(true), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent())), singularityUser); // It acquires a lock on the bounce Assert.assertTrue("Lock on bounce should be acquired during bounce", requestManager.getExpiringBounce(requestId).isPresent()); @@ -1308,7 +1307,7 @@ public void testBounceOnRunningInstancesReleasesLock() { startTask(firstDeploy, 1); Assert.assertEquals(1, taskManager.getActiveTaskIds().size()); - requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.of(true), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()))); + requestResource.bounce(requestId, Optional.of(new SingularityBounceRequest(Optional.absent(), Optional.of(true), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent())), singularityUser); cleaner.drainCleanupQueue(); // It acquires a lock on the bounce @@ -1343,11 +1342,11 @@ public void testIncrementalBounce() { initRequest(); resourceOffers(2); // set up slaves so scale validate will pass - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); requestResource.postRequest(request.toBuilder() .setSlavePlacement(Optional.of(SlavePlacement.SEPARATE_BY_REQUEST)) - .setInstances(Optional.of(2)).build() + .setInstances(Optional.of(2)).build(), singularityUser ); initHCDeploy(); @@ -1505,25 +1504,25 @@ public void testTaskOddities() { @Test public void testOnDemandTasksPersist() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("d2"); deployChecker.checkDeploys(); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); resourceOffers(); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); resourceOffers(); Assert.assertEquals(2, taskManager.getActiveTaskIds().size()); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); scheduler.drainPendingQueue(); - requestResource.scheduleImmediately(requestId); + requestResource.scheduleImmediately(singularityUser, requestId); scheduler.drainPendingQueue(); @@ -1537,12 +1536,12 @@ public void testOnDemandTasksPersist() { @Test public void testRunNowScheduledJobDoesNotRetry() { initScheduledRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); SingularityRequest newRequest = request.toBuilder().setNumRetriesOnFailure(Optional.of(2)).build(); - requestResource.postRequest(newRequest); + requestResource.postRequest(newRequest, singularityUser); initFirstDeploy(); - requestResource.scheduleImmediately(requestId, new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scheduleImmediately(singularityUser, requestId, new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent())); resourceOffers(); SingularityTask task = taskManager.getActiveTasks().get(0); @@ -1565,6 +1564,7 @@ public void testOnDemandRunNowJobRespectsSpecifiedRunAtTime() { long requestedLaunchTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10); requestResource.scheduleImmediately( + singularityUser, requestId, new SingularityRunNowRequest( Optional.absent(), @@ -1592,6 +1592,7 @@ public void testScheduledRunNowJobRespectsSpecifiedRunAtTime() { long requestedLaunchTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10); requestResource.scheduleImmediately( + singularityUser, requestId, new SingularityRunNowRequest( Optional.absent(), @@ -1642,8 +1643,8 @@ public void testScaleDownTakesHighestInstances() { Assert.assertEquals(5, taskManager.getActiveTaskIds().size()); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(2), Optional. absent(), Optional. absent(), - Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(2), Optional.absent(), Optional.absent(), + Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); resourceOffers(); cleaner.drainCleanupQueue(); @@ -1683,8 +1684,8 @@ public void testScaleDownTakesHighestInstancesWithPendingTask() { System.out.println(taskManager.getPendingTaskIds()); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional. absent(), Optional. absent(), - Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(3), Optional.absent(), Optional.absent(), + Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); scheduler.drainPendingQueue(); cleaner.drainCleanupQueue(); @@ -1823,8 +1824,8 @@ public void itCorrectlyUpdatesRequestDeletingStateHistory() { public void itSetsRequestStateToDeletedAfterAllTasksAreCleanedUp() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); @@ -1849,8 +1850,8 @@ public void itSetsRequestStateToDeletedAfterAllTasksAreCleanedUp() { public void itSetsRequestStateToDeletedIfTaskCleanupFails() { initRequest(); - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); initFirstDeploy(); SingularityTask firstTask = launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); @@ -1881,7 +1882,7 @@ public void testMaxTasksPerOffer() { initRequest(); initFirstDeploy(); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(20)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(20)).build(), singularityUser); scheduler.drainPendingQueue(); sms.resourceOffers(Arrays.asList(createOffer(36, 12024))); @@ -1905,7 +1906,7 @@ public void testRequestedPorts() { initRequest(); initAndFinishDeploy(request, deployBuilder, Optional.of(new Resources(1, 64, 3, 0))); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(2)).build(), singularityUser); scheduler.drainPendingQueue(); String[] portRangeWithNoRequestedPorts = {"65:70"}; @@ -1948,7 +1949,7 @@ private SingularityDeployBuilder dockerDeployWithPorts() { @Test public void testQueueMultipleOneOffs() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("on_demand_deploy"); deployChecker.checkDeploys(); @@ -1981,7 +1982,7 @@ public void testPriorityFreezeKillsActiveTasks() { final SingularityTask highPriorityTask = launchTask(highPriorityRequest, highPriorityDeploy, 10, 1, TaskState.TASK_RUNNING); // priority freeze of .5 means that lowPriorityRequest's task should have a cleanup - priorityResource.createPriorityFreeze(new SingularityPriorityFreeze(.5, true, Optional.of("test"), Optional.absent())); + priorityResource.createPriorityFreeze(singularityUser, new SingularityPriorityFreeze(.5, true, Optional.of("test"), Optional.absent())); // perform the killing priorityKillPoller.runActionOnPoll(); @@ -2007,7 +2008,7 @@ public void testPriorityFreezeKillsActiveTasks() { Assert.assertEquals(lowPriorityRequest.getId(), pendingTaskId.getRequestId()); // end the priority freeze - priorityResource.deleteActivePriorityFreeze(); + priorityResource.deleteActivePriorityFreeze(singularityUser); // launch task(s) scheduler.drainPendingQueue(); @@ -2023,21 +2024,21 @@ public void testPriorityFreezeDoesntLaunchTasks() { final SingularityRequest lowPriorityRequest = new SingularityRequestBuilder("lowPriorityRequest", RequestType.ON_DEMAND).setTaskPriorityLevel(Optional.of(.25)).build(); saveRequest(lowPriorityRequest); deployResource.deploy( - new SingularityDeployRequest(new SingularityDeployBuilder(lowPriorityRequest.getId(), "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent())); + new SingularityDeployRequest(new SingularityDeployBuilder(lowPriorityRequest.getId(), "d1").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); // deploy medium priority request (NOT affected by priority freeze) final SingularityRequest mediumPriorityRequest = new SingularityRequestBuilder("mediumPriorityRequest", RequestType.ON_DEMAND).setTaskPriorityLevel(Optional.of(.5)).build(); saveRequest(mediumPriorityRequest); deployResource.deploy( - new SingularityDeployRequest(new SingularityDeployBuilder(mediumPriorityRequest.getId(), "d2").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent())); + new SingularityDeployRequest(new SingularityDeployBuilder(mediumPriorityRequest.getId(), "d2").setCommand(Optional.of("cmd")).build(), Optional.absent(), Optional.absent()), singularityUser); // create priority freeze priorityManager.createPriorityFreeze( new SingularityPriorityFreezeParent(new SingularityPriorityFreeze(0.3, true, Optional.absent(), Optional.absent()), System.currentTimeMillis(), Optional.absent())); // launch both tasks - requestResource.scheduleImmediately(lowPriorityRequest.getId()); - requestResource.scheduleImmediately(mediumPriorityRequest.getId()); + requestResource.scheduleImmediately(singularityUser, lowPriorityRequest.getId()); + requestResource.scheduleImmediately(singularityUser, mediumPriorityRequest.getId()); // drain pending queue scheduler.drainPendingQueue(); @@ -2068,7 +2069,7 @@ public void testObsoletePendingRequestsRemoved() { initRequest(); initFirstDeploy(); SingularityTask taskOne = startTask(firstDeploy); - requestResource.pause(requestId, Optional. absent()); + requestResource.pause(requestId, Optional.absent(), singularityUser); requestManager.addToPendingQueue(new SingularityPendingRequest(requestId, firstDeployId, System.currentTimeMillis(), Optional.absent(), PendingType.NEW_DEPLOY, Optional.absent(), Optional.absent())); Assert.assertEquals(requestManager.getPendingRequests().size(), 1); @@ -2106,7 +2107,7 @@ public void testCronScheduleChanges() throws Exception { final SingularityDeploy newDeploy = new SingularityDeployBuilder(request.getId(), "2").setCommand(Optional.of("sleep 100")).build(); - deployResource.deploy(new SingularityDeployRequest(newDeploy, Optional.absent(), Optional.absent(), Optional.of(newRequest))); + deployResource.deploy(new SingularityDeployRequest(newDeploy, Optional.absent(), Optional.absent(), Optional.of(newRequest)), singularityUser); deployChecker.checkDeploys(); @@ -2123,7 +2124,7 @@ public void testImmediateRunReplacesScheduledTask() { .setCommand(Optional.of("sleep 100")) .build(); SingularityDeployRequest singularityDeployRequest = new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.absent()); - deployResource.deploy(singularityDeployRequest); + deployResource.deploy(singularityDeployRequest, singularityUser); scheduler.drainPendingQueue(); @@ -2148,14 +2149,14 @@ public void testSchedulerDropsMultipleScheduledTaskInstances() { .setCommand(Optional.of("sleep 100")) .build(); SingularityDeployRequest singularityDeployRequest = new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent(), Optional.absent()); - deployResource.deploy(singularityDeployRequest); + deployResource.deploy(singularityDeployRequest, singularityUser); scheduler.drainPendingQueue(); requestManager.addToPendingQueue(new SingularityPendingRequest(requestId, firstDeployId, Instant.now().plus(3, ChronoUnit.DAYS).toEpochMilli(), Optional.absent(), PendingType.NEW_DEPLOY, Optional.absent(), Optional.absent())); SingularityRunNowRequest runNowRequest = new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()); - requestResource.scheduleImmediately(requestId, runNowRequest); + requestResource.scheduleImmediately(singularityUser, requestId, runNowRequest); @@ -2164,7 +2165,6 @@ public void testSchedulerDropsMultipleScheduledTaskInstances() { Assert.assertEquals(PendingType.IMMEDIATE, requestManager.getPendingRequests().get(0).getPendingType()); Assert.assertEquals(PendingType.NEW_DEPLOY, requestManager.getPendingRequests().get(1).getPendingType()); - scheduler.drainPendingQueue(); Assertions.assertThat(taskManager.getPendingTaskIds()) .describedAs("Only the immediate request gets run") @@ -2186,7 +2186,7 @@ public void testInvalidQuartzTimeZoneErrors() { .setScheduleTimeZone(Optional.of("invalid_timezone")) .build(); - requestResource.postRequest(req); + requestResource.postRequest(req, singularityUser); } @Test @@ -2205,8 +2205,8 @@ public void testDifferentQuartzTimeZones() { .setScheduleTimeZone(Optional.of("GMT")) .build(); - requestResource.postRequest(requestEST); - requestResource.postRequest(requestGMT); + requestResource.postRequest(requestEST, singularityUser); + requestResource.postRequest(requestGMT, singularityUser); SingularityDeploy deployEST = new SingularityDeployBuilder(requestEST.getId(), "est_deploy_id") .setCommand(Optional.of("sleep 1")) @@ -2216,8 +2216,8 @@ public void testDifferentQuartzTimeZones() { .setCommand(Optional.of("sleep 1")) .build(); - deployResource.deploy(new SingularityDeployRequest(deployEST, Optional.absent(), Optional.absent(), Optional.absent())); - deployResource.deploy(new SingularityDeployRequest(deployGMT, Optional.absent(), Optional.absent(), Optional.absent())); + deployResource.deploy(new SingularityDeployRequest(deployEST, Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); + deployResource.deploy(new SingularityDeployRequest(deployGMT, Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); deployChecker.checkDeploys(); scheduler.drainPendingQueue(); @@ -2245,7 +2245,7 @@ public void testDeployCleanupOverwritesTaskBounceCleanup() { final SingularityTask oldTask = startTask(firstDeploy); taskResource - .killTask(oldTask.getTaskId().getId(), Optional.of(new SingularityKillTaskRequest(Optional. absent(), Optional. absent(), Optional. absent(), Optional.of(true), Optional.absent()))); + .killTask(oldTask.getTaskId().getId(), Optional.of(new SingularityKillTaskRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.of(true), Optional.absent())), singularityUser); final Optional taskCleanup = taskManager.getTaskCleanup(oldTask.getTaskId().getId()); Assert.assertTrue(taskCleanup.isPresent()); @@ -2268,7 +2268,7 @@ public void testCleanerFindsTasksWithSkippedHealthchecks() { initRequest(); resourceOffers(2); // set up slaves so scale validate will pass - SingularityRequest request = requestResource.getRequest(requestId).getRequest(); + SingularityRequest request = requestResource.getRequest(requestId, singularityUser).getRequest(); long now = System.currentTimeMillis(); @@ -2318,7 +2318,7 @@ public void testScaleWithBounceDoesNotLaunchExtraInstances() { initFirstDeploy(); launchTask(request, firstDeploy, 1, TaskState.TASK_RUNNING); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional. absent(), Optional. absent(), Optional.absent(), Optional.of(true), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(5), Optional.of(1L), Optional.absent(), Optional.absent(), Optional.absent(), Optional.of(true), Optional.absent()), singularityUser); Assert.assertEquals(1, requestManager.getCleanupRequests().size()); cleaner.drainCleanupQueue(); @@ -2332,11 +2332,11 @@ public void testScaleWithBounceDoesNotLaunchExtraInstances() { public void testAcceptOffersWithRoleForRequestWithRole() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); bldr.setRequiredRole(Optional.of("test-role")); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("d2"); SingularityRunNowRequest runNowRequest = new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.>absent(), Optional.of(new Resources(2, 2, 0)), Optional.absent()); - requestResource.scheduleImmediately(requestId, runNowRequest); + requestResource.scheduleImmediately(singularityUser, requestId, runNowRequest); scheduler.drainPendingQueue(); @@ -2358,11 +2358,11 @@ public void testAcceptOffersWithRoleForRequestWithRole() { @Test public void testNotAcceptOfferWithRoleForRequestWithoutRole() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("d2"); SingularityRunNowRequest runNowRequest = new SingularityRunNowRequest(Optional.absent(), Optional.absent(), Optional.absent(), Optional.>absent(), Optional.of(new Resources(2, 2, 0)), Optional.absent()); - requestResource.scheduleImmediately(requestId, runNowRequest); + requestResource.scheduleImmediately(singularityUser, requestId, runNowRequest); scheduler.drainPendingQueue(); @@ -2381,7 +2381,7 @@ public void testNotAcceptOfferWithRoleForRequestWithoutRole() { public void testMaxOnDemandTasks() { SingularityRequestBuilder bldr = new SingularityRequestBuilder(requestId, RequestType.ON_DEMAND); bldr.setInstances(Optional.of(1)); - requestResource.postRequest(bldr.build()); + requestResource.postRequest(bldr.build(), singularityUser); deploy("on_demand_deploy"); deployChecker.checkDeploys(); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java index c0d75a0f6b..9db66cbba3 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java @@ -75,6 +75,7 @@ import com.hubspot.singularity.SingularityTaskId; import com.hubspot.singularity.SingularityTaskRequest; import com.hubspot.singularity.SingularityTaskStatusHolder; +import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.SlavePlacement; import com.hubspot.singularity.api.SingularityDeployRequest; import com.hubspot.singularity.api.SingularityScaleRequest; @@ -188,6 +189,8 @@ public class SingularitySchedulerTestBase extends SingularityCuratorTestBase { protected Optional user = Optional.absent(); + protected SingularityUser singularityUser = SingularityUser.defaultUser(); + public SingularitySchedulerTestBase(boolean useDBTests) { super(useDBTests); } @@ -459,7 +462,7 @@ protected SingularityDeploy deployRequest(SingularityRequest request, double cpu .setResources(Optional.of(r)) .build(); - deployResource.deploy(new SingularityDeployRequest(deploy, Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent()), singularityUser); return deploy; } @@ -488,7 +491,7 @@ protected SingularityRequest startAndDeploySecondRequest() { SingularityDeploy deploy = new SingularityDeployBuilder(request.getId(), "d1").setCommand(Optional.of("sleep 1")).build(); - deployResource.deploy(new SingularityDeployRequest(deploy, Optional. absent(), Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(deploy, Optional.absent(), Optional.absent()), singularityUser); return request; } @@ -510,7 +513,7 @@ protected void initRequest() { protected void initWithTasks(int num) { initRequest(); - requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(num), Optional. absent(), Optional. absent(), Optional. absent(), Optional.absent(), Optional.absent(), Optional.absent())); + requestResource.scale(requestId, new SingularityScaleRequest(Optional.of(num), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()), singularityUser); initFirstDeploy(); @@ -667,7 +670,7 @@ protected void deploy(String deployId, Optional unpauseOnDeploy, Option .setServiceBasePath(Optional.of("/basepath")) .setLoadBalancerGroups(Optional.of(groups)); } - deployResource.deploy(new SingularityDeployRequest(builder.build(), unpauseOnDeploy, Optional. absent())); + deployResource.deploy(new SingularityDeployRequest(builder.build(), unpauseOnDeploy, Optional.absent()), singularityUser); } protected SingularityPendingTask createAndSchedulePendingTask(String deployId) { diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java index e409afdd41..125d067cf8 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySlavePlacementTest.java @@ -306,7 +306,7 @@ public void testRackPlacementOnScaleDown() { Assert.assertEquals(7, taskManager.getActiveTaskIds().size()); - requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).setRackSensitive(Optional.of(true)).build()); + requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).setRackSensitive(Optional.of(true)).build(), singularityUser); scheduler.drainPendingQueue(); @@ -345,7 +345,7 @@ public void testPlacementOfBounceTasks() { sms.resourceOffers(Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack1")))); Assert.assertEquals(2, taskManager.getActiveTaskIds().size()); - requestResource.bounce(requestId, Optional.absent()); + requestResource.bounce(requestId, Optional.absent(), singularityUser); cleaner.drainCleanupQueue(); scheduler.drainPendingQueue(); diff --git a/pom.xml b/pom.xml index 380e741bf8..f5a53811d3 100644 --- a/pom.xml +++ b/pom.xml @@ -160,6 +160,12 @@ ${dropwizard.guicier.version} + + io.dropwizard + dropwizard-auth + ${dropwizard.version} + + com.hubspot.jackson jackson-jaxrs-propertyfiltering From e86652bb548f1af8977d7918da9ac52bd46fc237 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Thu, 26 Oct 2017 15:56:40 -0400 Subject: [PATCH 28/79] more wip auth --- .../singularity/SingularityAuthModule.java | 7 --- .../singularity/SingularityService.java | 14 ++++- .../auth/SingularityAuthFactoryProvider.java | 35 ++++++++++++ .../auth/SingularityAuthFeature.java | 29 ++++++++++ .../auth/SingularityAuthFilter.java | 23 ++++++++ ...SingularityAuthParamInjectionResolver.java | 14 +++++ .../auth/SingularityAuthedUserFactory.java | 56 +++++++++++++++++++ 7 files changed, 169 insertions(+), 9 deletions(-) create mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java create mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java create mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java create mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthParamInjectionResolver.java create mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java index e0ab550ff7..5d2cc22492 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java @@ -1,21 +1,15 @@ package com.hubspot.singularity; -import javax.ws.rs.container.ContainerRequestContext; - import com.google.inject.Binder; import com.google.inject.Module; import com.google.inject.Scopes; -import com.google.inject.TypeLiteral; import com.google.inject.multibindings.Multibinder; import com.hubspot.singularity.auth.SingularityAuthenticatorClass; import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; -import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; import com.hubspot.singularity.config.SingularityConfiguration; -import io.dropwizard.auth.Authenticator; - public class SingularityAuthModule implements Module { private final SingularityConfiguration configuration; @@ -29,7 +23,6 @@ public void configure(Binder binder) { for (SingularityAuthenticatorClass clazz : configuration.getAuthConfiguration().getAuthenticators()) { multibinder.addBinding().to(clazz.getAuthenticatorClass()); } - binder.bind(new TypeLiteral>() {}).to(SingularityMultiLevelAuthenticator.class); binder.bind(SingularityAuthDatastore.class).to(configuration.getAuthConfiguration().getDatastore().getAuthDatastoreClass()); binder.bind(SingularityAuthorizationHelper.class).in(Scopes.SINGLETON); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java index 27aa0f1823..25af2f6f08 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java @@ -2,6 +2,8 @@ import static com.google.common.base.Preconditions.checkNotNull; +import javax.ws.rs.container.ContainerRequestContext; + import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; @@ -11,6 +13,7 @@ import com.google.inject.Module; import com.hubspot.dropwizard.guicier.GuiceBundle; import com.hubspot.jackson.datatype.protobuf.ProtobufModule; +import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; import com.hubspot.singularity.bundles.CorsBundle; import com.hubspot.singularity.config.ApiPaths; import com.hubspot.singularity.config.MergingSourceProvider; @@ -20,6 +23,8 @@ import io.dropwizard.Bundle; import io.dropwizard.ConfiguredBundle; import io.dropwizard.assets.AssetsBundle; +import io.dropwizard.auth.AuthDynamicFeature; +import io.dropwizard.auth.AuthFilter; import io.dropwizard.db.DataSourceFactory; import io.dropwizard.migrations.MigrationsBundle; import io.dropwizard.setup.Bootstrap; @@ -75,7 +80,12 @@ public DataSourceFactory getDataSourceFactory(final SingularityConfiguration con } @Override - public void run(final T configuration, final Environment environment) throws Exception {} + public void run(final T configuration, final Environment environment) throws Exception { + SingularityMultiLevelAuthenticator authenticator = guiceBundle.getInjector().getInstance(SingularityMultiLevelAuthenticator.class); + environment.jersey().register(new AuthDynamicFeature( + + )); + } /** * Guice modules used in addition to the modules required by Singularity. This is an extension point when embedding @@ -100,7 +110,7 @@ public Iterable> getDropwizardConfiguredBundles(Bo public static void main(final String[] args) throws Exception { try { - new SingularityService<>().run(args); + new SingularityService().run(args); } catch (final Throwable t) { t.printStackTrace(); System.exit(1); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java new file mode 100644 index 0000000000..416d7d8bbe --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java @@ -0,0 +1,35 @@ +package com.hubspot.singularity.auth; + +import org.glassfish.hk2.api.Factory; +import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider; +import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider; +import org.glassfish.jersey.server.model.Parameter; +import org.glassfish.jersey.server.model.Parameter.Source; + +import com.google.inject.Inject; +import com.hubspot.singularity.SingularityUser; + +import io.dropwizard.auth.Auth; + +public class SingularityAuthFactoryProvider extends AbstractValueFactoryProvider { + private SingularityAuthedUserFactory authFactory; + + @Inject + public SingularityAuthFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider, + ServiceLocator locator, + SingularityAuthedUserFactory authFactory) { + super(extractorProvider, locator, Source.UNKNOWN); + this.authFactory = authFactory; + } + + @Override + protected Factory createValueFactory(Parameter parameter) { + Class paramType = parameter.getRawType(); + Auth annotation = parameter.getAnnotation(Auth.class); + if (annotation != null && paramType.isAssignableFrom(SingularityUser.class)) { + return authFactory; + } + return null; + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java new file mode 100644 index 0000000000..62d990eb99 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java @@ -0,0 +1,29 @@ +package com.hubspot.singularity.auth; + +import javax.inject.Singleton; +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; + +import org.glassfish.hk2.api.InjectionResolver; +import org.glassfish.hk2.api.TypeLiteral; +import org.glassfish.hk2.utilities.binding.AbstractBinder; + +import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; + +import io.dropwizard.auth.Auth; + +public class SingularityAuthFeature implements Feature { + @Override + public boolean configure(FeatureContext context) { + context.register(new AbstractBinder() { + @Override + public void configure() { + bind(SingularityMultiLevelAuthenticator.class).to(SingularityMultiLevelAuthenticator.class).in(Singleton.class); + bind(SingularityAuthedUserFactory.class).to(SingularityAuthedUserFactory.class).in(Singleton.class); + bind(SingularityAuthFactoryProvider.class).to(SingularityAuthFactoryProvider.class).in(Singleton.class); + bind(SingularityAuthParamInjectionResolver.class).to(new TypeLiteral>(){}).in(Singleton.class); + } + }); + return true; + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java new file mode 100644 index 0000000000..f229ed1de6 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java @@ -0,0 +1,23 @@ +package com.hubspot.singularity.auth; + +import java.io.IOException; + +import javax.ws.rs.container.ContainerRequestContext; + +import com.google.inject.Inject; +import com.hubspot.singularity.SingularityUser; + +import io.dropwizard.auth.AuthFilter; + +public class SingularityAuthFilter extends AuthFilter { + + @Inject + public SingularityAuthFilter() { + + } + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthParamInjectionResolver.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthParamInjectionResolver.java new file mode 100644 index 0000000000..d33d4f1ef9 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthParamInjectionResolver.java @@ -0,0 +1,14 @@ +package com.hubspot.singularity.auth; + +import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver; + +import com.google.inject.Singleton; + +import io.dropwizard.auth.Auth; + +@Singleton +public class SingularityAuthParamInjectionResolver extends ParamInjectionResolver { + public SingularityAuthParamInjectionResolver() { + super(SingularityAuthFactoryProvider.class); + } +} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java new file mode 100644 index 0000000000..4e4ce0e7f9 --- /dev/null +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java @@ -0,0 +1,56 @@ +package com.hubspot.singularity.auth; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.ws.rs.WebApplicationException; + +import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.WebExceptions; +import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; +import com.hubspot.singularity.config.SingularityConfiguration; + +@Singleton +public class SingularityAuthedUserFactory extends AbstractContainerRequestValueFactory { + private final Set authenticators; + private final SingularityConfiguration configuration; + + @Inject + public SingularityAuthedUserFactory(Set authenticators, SingularityConfiguration configuration) { + this.authenticators = authenticators; + this.configuration = configuration; + } + + @Override + public SingularityUser provide() { + WebApplicationException unauthenticatedException = null; + for (SingularityAuthenticator authenticator : authenticators) { + try { + Optional maybeUser = authenticator.getUser(getContainerRequest()); + if (maybeUser.isPresent()) { + return maybeUser.get(); + } + } catch (WebApplicationException e) { + unauthenticatedException = e; + } + } + + // No user found if we got here + if (configuration.getAuthConfiguration().isEnabled()) { + if (unauthenticatedException != null) { + throw unauthenticatedException; + } else { + throw WebExceptions.unauthorized(String.format("Unable to authenticate user using methods: %s", + authenticators.stream().map(SingularityAuthenticator::getClass).collect(Collectors.toList()))); + } + } + + // Auth is disabled, return a dummy/default user + return SingularityUser.defaultUser(); + } +} From f9f38f0371d421510245be25c4347aef71b8909a Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Thu, 26 Oct 2017 17:46:03 -0400 Subject: [PATCH 29/79] more wip --- .../singularity/SingularityAuthModule.java | 19 +++++++++++-------- .../singularity/SingularityService.java | 13 ++----------- .../singularity/SingularityServiceModule.java | 2 -- .../auth/SingularityAuthFactoryProvider.java | 3 +-- .../auth/SingularityAuthFeature.java | 4 ++++ .../auth/SingularityAuthFilter.java | 15 ++++++++++++--- .../auth/SingularityAuthedUserFactory.java | 3 +-- .../SingularityMultiLevelAuthenticator.java | 5 +++-- .../scheduler/SingularityTestModule.java | 2 +- 9 files changed, 35 insertions(+), 31 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java index 5d2cc22492..e81689a372 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java @@ -1,30 +1,33 @@ package com.hubspot.singularity; import com.google.inject.Binder; -import com.google.inject.Module; import com.google.inject.Scopes; import com.google.inject.multibindings.Multibinder; +import com.hubspot.dropwizard.guicier.DropwizardAwareModule; +import com.hubspot.singularity.auth.SingularityAuthFeature; import com.hubspot.singularity.auth.SingularityAuthenticatorClass; import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; +import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; import com.hubspot.singularity.config.SingularityConfiguration; -public class SingularityAuthModule implements Module { - private final SingularityConfiguration configuration; +public class SingularityAuthModule extends DropwizardAwareModule { - public SingularityAuthModule(SingularityConfiguration configuration) { - this.configuration = configuration; - } + public SingularityAuthModule() {} @Override public void configure(Binder binder) { Multibinder multibinder = Multibinder.newSetBinder(binder, SingularityAuthenticator.class); - for (SingularityAuthenticatorClass clazz : configuration.getAuthConfiguration().getAuthenticators()) { + for (SingularityAuthenticatorClass clazz : getConfiguration().getAuthConfiguration().getAuthenticators()) { multibinder.addBinding().to(clazz.getAuthenticatorClass()); } - binder.bind(SingularityAuthDatastore.class).to(configuration.getAuthConfiguration().getDatastore().getAuthDatastoreClass()); + binder.bind(SingularityAuthFeature.class); + binder.bind(SingularityMultiLevelAuthenticator.class); + binder.bind(SingularityAuthDatastore.class).to(getConfiguration().getAuthConfiguration().getDatastore().getAuthDatastoreClass()); binder.bind(SingularityAuthorizationHelper.class).in(Scopes.SINGLETON); + + getEnvironment().jersey().register(SingularityAuthFeature.class); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java index 25af2f6f08..2e21b1fb75 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityService.java @@ -2,8 +2,6 @@ import static com.google.common.base.Preconditions.checkNotNull; -import javax.ws.rs.container.ContainerRequestContext; - import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; @@ -13,7 +11,6 @@ import com.google.inject.Module; import com.hubspot.dropwizard.guicier.GuiceBundle; import com.hubspot.jackson.datatype.protobuf.ProtobufModule; -import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; import com.hubspot.singularity.bundles.CorsBundle; import com.hubspot.singularity.config.ApiPaths; import com.hubspot.singularity.config.MergingSourceProvider; @@ -23,8 +20,6 @@ import io.dropwizard.Bundle; import io.dropwizard.ConfiguredBundle; import io.dropwizard.assets.AssetsBundle; -import io.dropwizard.auth.AuthDynamicFeature; -import io.dropwizard.auth.AuthFilter; import io.dropwizard.db.DataSourceFactory; import io.dropwizard.migrations.MigrationsBundle; import io.dropwizard.setup.Bootstrap; @@ -50,6 +45,7 @@ public void initialize(final Bootstrap bootstrap) { guiceBundle = GuiceBundle.defaultBuilder(SingularityConfiguration.class) .modules(new SingularityServiceModule()) + .modules(new SingularityAuthModule()) .modules(additionalModules) .build(); bootstrap.addBundle(guiceBundle); @@ -80,12 +76,7 @@ public DataSourceFactory getDataSourceFactory(final SingularityConfiguration con } @Override - public void run(final T configuration, final Environment environment) throws Exception { - SingularityMultiLevelAuthenticator authenticator = guiceBundle.getInjector().getInstance(SingularityMultiLevelAuthenticator.class); - environment.jersey().register(new AuthDynamicFeature( - - )); - } + public void run(final T configuration, final Environment environment) throws Exception {} /** * Guice modules used in addition to the modules required by Singularity. This is an extension point when embedding diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java index f21163957a..c0cf1d028a 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java @@ -37,8 +37,6 @@ public void configure(Binder binder) { binder.install(new SingularityJerseyModule()); binder.install(new SingularityEventModule(getConfiguration())); - - binder.install(new SingularityAuthModule(getConfiguration())); } @Provides diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java index 416d7d8bbe..d55503797d 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFactoryProvider.java @@ -7,7 +7,6 @@ import org.glassfish.jersey.server.model.Parameter; import org.glassfish.jersey.server.model.Parameter.Source; -import com.google.inject.Inject; import com.hubspot.singularity.SingularityUser; import io.dropwizard.auth.Auth; @@ -15,7 +14,7 @@ public class SingularityAuthFactoryProvider extends AbstractValueFactoryProvider { private SingularityAuthedUserFactory authFactory; - @Inject + @javax.inject.Inject public SingularityAuthFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider, ServiceLocator locator, SingularityAuthedUserFactory authFactory) { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java index 62d990eb99..5b9578536d 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java @@ -12,7 +12,11 @@ import io.dropwizard.auth.Auth; +@javax.ws.rs.ext.Provider public class SingularityAuthFeature implements Feature { + @javax.inject.Inject + SingularityAuthFeature() {} + @Override public boolean configure(FeatureContext context) { context.register(new AbstractBinder() { diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java index f229ed1de6..d79b322422 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java @@ -1,23 +1,32 @@ package com.hubspot.singularity.auth; import java.io.IOException; +import java.util.Optional; +import javax.annotation.Priority; +import javax.ws.rs.Priorities; import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.ext.Provider; import com.google.inject.Inject; import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; +import com.hubspot.singularity.config.SingularityConfiguration; import io.dropwizard.auth.AuthFilter; +@Priority(Priorities.AUTHENTICATION) +@Provider public class SingularityAuthFilter extends AuthFilter { + private final SingularityMultiLevelAuthenticator authenticator; @Inject - public SingularityAuthFilter() { - + public SingularityAuthFilter(SingularityMultiLevelAuthenticator authenticator, SingularityConfiguration configuration) { + this.authenticator = authenticator; } @Override public void filter(ContainerRequestContext requestContext) throws IOException { - + Optional maybeUser = authenticator.authenticate(requestContext); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java index 4e4ce0e7f9..02cbe0f86e 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java @@ -8,7 +8,6 @@ import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory; -import com.google.inject.Inject; import com.google.inject.Singleton; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.WebExceptions; @@ -20,7 +19,7 @@ public class SingularityAuthedUserFactory extends AbstractContainerRequestValueF private final Set authenticators; private final SingularityConfiguration configuration; - @Inject + @javax.inject.Inject public SingularityAuthedUserFactory(Set authenticators, SingularityConfiguration configuration) { this.authenticators = authenticators; this.configuration = configuration; diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java index a8d0054748..ce4cc7bfa7 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java @@ -6,24 +6,25 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.ext.Provider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.inject.Inject; import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.WebExceptions; import com.hubspot.singularity.config.SingularityConfiguration; import io.dropwizard.auth.Authenticator; +@Provider public class SingularityMultiLevelAuthenticator implements Authenticator { private static final Logger LOG = LoggerFactory.getLogger(SingularityMultiLevelAuthenticator.class); private final Set authenticators; private final SingularityConfiguration configuration; - @Inject + @javax.inject.Inject public SingularityMultiLevelAuthenticator(Set authenticators, SingularityConfiguration configuration) { this.authenticators = authenticators; this.configuration = configuration; diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java index 1a8164db80..3f4379cf29 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java @@ -192,7 +192,7 @@ public void configure(Binder binder) { mainBinder.install(new SingularityZkMigrationsModule()); mainBinder.install(new SingularityEventModule(configuration)); - mainBinder.install(Modules.override(new SingularityAuthModule(configuration)) + mainBinder.install(Modules.override(new SingularityAuthModule()) .with(new Module() { @Override public void configure(Binder binder) { From fdb3bf829243eb3fdee1b00f0b10b73e7a9b5ac7 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Thu, 26 Oct 2017 22:15:40 -0400 Subject: [PATCH 30/79] look, it works now! --- SingularityService/pom.xml | 2 +- .../auth/SingularityAuthFeature.java | 8 +++-- .../auth/SingularityAuthFilter.java | 32 ------------------- .../SingularityMultiLevelAuthenticator.java | 2 -- .../singularity/resources/TaskResource.java | 2 +- 5 files changed, 8 insertions(+), 38 deletions(-) delete mode 100644 SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java diff --git a/SingularityService/pom.xml b/SingularityService/pom.xml index 27b01abdd9..cdea44ec3c 100644 --- a/SingularityService/pom.xml +++ b/SingularityService/pom.xml @@ -286,7 +286,7 @@ - com.sun.jersey + org.glassfish.jersey.core jersey-server diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java index 5b9578536d..66679049a9 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java @@ -1,16 +1,20 @@ package com.hubspot.singularity.auth; import javax.inject.Singleton; +import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.core.Feature; import javax.ws.rs.core.FeatureContext; import org.glassfish.hk2.api.InjectionResolver; import org.glassfish.hk2.api.TypeLiteral; import org.glassfish.hk2.utilities.binding.AbstractBinder; +import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider; +import com.hubspot.singularity.SingularityUser; import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; import io.dropwizard.auth.Auth; +import io.dropwizard.auth.Authenticator; @javax.ws.rs.ext.Provider public class SingularityAuthFeature implements Feature { @@ -22,9 +26,9 @@ public boolean configure(FeatureContext context) { context.register(new AbstractBinder() { @Override public void configure() { - bind(SingularityMultiLevelAuthenticator.class).to(SingularityMultiLevelAuthenticator.class).in(Singleton.class); + bind(SingularityMultiLevelAuthenticator.class).to(new TypeLiteral>() {}).in(Singleton.class); bind(SingularityAuthedUserFactory.class).to(SingularityAuthedUserFactory.class).in(Singleton.class); - bind(SingularityAuthFactoryProvider.class).to(SingularityAuthFactoryProvider.class).in(Singleton.class); + bind(SingularityAuthFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class); bind(SingularityAuthParamInjectionResolver.class).to(new TypeLiteral>(){}).in(Singleton.class); } }); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java deleted file mode 100644 index d79b322422..0000000000 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFilter.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.hubspot.singularity.auth; - -import java.io.IOException; -import java.util.Optional; - -import javax.annotation.Priority; -import javax.ws.rs.Priorities; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.ext.Provider; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityUser; -import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; -import com.hubspot.singularity.config.SingularityConfiguration; - -import io.dropwizard.auth.AuthFilter; - -@Priority(Priorities.AUTHENTICATION) -@Provider -public class SingularityAuthFilter extends AuthFilter { - private final SingularityMultiLevelAuthenticator authenticator; - - @Inject - public SingularityAuthFilter(SingularityMultiLevelAuthenticator authenticator, SingularityConfiguration configuration) { - this.authenticator = authenticator; - } - - @Override - public void filter(ContainerRequestContext requestContext) throws IOException { - Optional maybeUser = authenticator.authenticate(requestContext); - } -} diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java index ce4cc7bfa7..dfb831fb1b 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java @@ -6,7 +6,6 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.ext.Provider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,7 +16,6 @@ import io.dropwizard.auth.Authenticator; -@Provider public class SingularityMultiLevelAuthenticator implements Authenticator { private static final Logger LOG = LoggerFactory.getLogger(SingularityMultiLevelAuthenticator.class); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java index e3a972831e..c9552b991c 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskResource.java @@ -347,7 +347,7 @@ public SingularityTaskCleanup killTask(@Auth SingularityUser user, @PathParam("t public SingularityTaskCleanup killTask(@PathParam("taskId") String taskId, @Context HttpServletRequest requestContext, SingularityKillTaskRequest killTaskRequest, - SingularityUser user) { + @Auth SingularityUser user) { final Optional maybeKillTaskRequest = Optional.fromNullable(killTaskRequest); return maybeProxyToLeader(requestContext, SingularityTaskCleanup.class, maybeKillTaskRequest.orNull(), () -> killTask(taskId, maybeKillTaskRequest, user)); } From dd3d139ca3cf75eb8c14de4aa6ae4103d7a41c1a Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 10:15:15 -0400 Subject: [PATCH 31/79] add auth header in the tailer as well --- .../SingularityTailer/src/actions/index.js | 28 ++++++++++++++++--- .../SingularityTailer/src/reducers/config.js | 8 +++++- SingularityUI/app/actions/api/base.es6 | 19 +------------ SingularityUI/app/actions/tailer.es6 | 10 +++++++ .../containers/CustomLogTailerContainer.jsx | 7 +++-- .../containers/RequestLogTailerContainer.jsx | 5 +++- .../app/containers/TaskLogTailerContainer.jsx | 4 ++- SingularityUI/app/utils.es6 | 13 +++++++++ 8 files changed, 67 insertions(+), 27 deletions(-) diff --git a/SingularityUI/SingularityTailer/src/actions/index.js b/SingularityUI/SingularityTailer/src/actions/index.js index f07192e03d..d3469b0209 100644 --- a/SingularityUI/SingularityTailer/src/actions/index.js +++ b/SingularityUI/SingularityTailer/src/actions/index.js @@ -146,7 +146,12 @@ export const sandboxFetchChunk = (id, taskId, path, start, end, config) => { const query = `?path=${path}&offset=${start}&length=${end - start}`; const apiPath = `${apiRoot}/sandbox/${taskId}/read${query}`; - return fetch(apiPath, {credentials: 'include'}) + const options = {credentials: 'include'} + if (config.authorizationHeader) { + options['headers'] = {'Authorization', config.authorizationHeader} + } + + return fetch(apiPath, options) .then((r) => {return checkStatus(r, taskId)}) .then(parseJSON) .then(({data, offset}) => { @@ -185,7 +190,12 @@ export const sandboxFetchLength = (id, taskId, path, config) => { const query = `?path=${path}&length=${0}`; const apiPath = `${apiRoot}/sandbox/${taskId}/read${query}`; - return fetch(apiPath, {credentials: 'include'}) + const options = {credentials: 'include'} + if (config.authorizationHeader) { + options['headers'] = {'Authorization', config.authorizationHeader} + } + + return fetch(apiPath, options) .then((r) => {return checkStatus(r, taskId)}) .then(parseJSON) .then(({offset}) => { @@ -247,7 +257,12 @@ export const blazarLogFetchChunk = (id, buildId, start, end, config) => { const query = `?offset=${start}&length=${end - start}`; const apiPath = `${apiRoot}/modules/builds/${buildId}/log${query}`; - return fetch(apiPath, {credentials: 'include'}) + const options = {credentials: 'include'} + if (config.authorizationHeader) { + options['headers'] = {'Authorization', config.authorizationHeader} + } + + return fetch(apiPath, options) .then(checkStatus) .then(parseJSON) .then(({data, offset}) => { @@ -285,7 +300,12 @@ export const blazarLogFetchLength = (id, buildId, config) => { const apiRoot = config.blazarApiRoot; const apiPath = `${apiRoot}/modules/builds/${buildId}/log/size`; - return fetch(apiPath, {credentials: 'include'}) + const options = {credentials: 'include'} + if (config.authorizationHeader) { + options['headers'] = {'Authorization', config.authorizationHeader} + } + + return fetch(apiPath, options) .then(checkStatus) .then(parseJSON) .then(({size}) => { diff --git a/SingularityUI/SingularityTailer/src/reducers/config.js b/SingularityUI/SingularityTailer/src/reducers/config.js index edb2c5a4a2..52101ac6b1 100644 --- a/SingularityUI/SingularityTailer/src/reducers/config.js +++ b/SingularityUI/SingularityTailer/src/reducers/config.js @@ -3,7 +3,8 @@ import { BLAZAR_SET_API_ROOT, TOGGLE_ANSI_COLORING, TOGGLE_FETCH_OVERSCAN, - SET_TAIL_INTERVAL_MS + SET_TAIL_INTERVAL_MS, + SET_AUTHORIZATION_HEADER } from '../actions'; const initialState = { @@ -39,6 +40,11 @@ const configReducer = (state = initialState, action) => { ...state, tailIntervalMs: action.tailIntervalMs } + case SET_AUTHORIZATION_HEADER: + return { + ...state, + authorizationHeader: action.authorizationHeader + } default: return state; } diff --git a/SingularityUI/app/actions/api/base.es6 b/SingularityUI/app/actions/api/base.es6 index 2bdb28fabb..2e4e55a858 100644 --- a/SingularityUI/app/actions/api/base.es6 +++ b/SingularityUI/app/actions/api/base.es6 @@ -58,23 +58,6 @@ export function buildApiAction(actionName, opts = {}, keyFunc = undefined) { }; } - function getCookie(key) { - if (!key) { - return null; - } - const encodedKey = encodeURIComponent(key).replace(/[\-\.\+\*]/g, '\\$&'); - return decodeURIComponent(document.cookie.replace(new RegExp(`(?:(?:^|.*;)\\s*${encodedKey}\\s*\\=\\s*([^;]*).*$)|^.*$`), '$1')) || null; - } - - function getAuthHeader(headerName) { - const authCookie = getCookie(headerName); - if (!authCookie) { - return ''; - } - const authToken = JSON.parse(authCookie).token; - return `Bearer ${ authToken }`; - } - function trigger(...args) { return (dispatch) => { let key; @@ -96,7 +79,7 @@ export function buildApiAction(actionName, opts = {}, keyFunc = undefined) { if (config.generateAuthHeader) { options.headers = options.headers || {}; - options.headers.Authorization = getAuthHeader(config.authCookieName) + options.headers.Authorization = Utils.getAuthTokenHeader(); } return fetch(config.apiRoot + options.url + userParam, _.extend({credentials: 'include'}, _.omit(options, 'url'))) diff --git a/SingularityUI/app/actions/tailer.es6 b/SingularityUI/app/actions/tailer.es6 index d4407021c8..1a15b1f041 100644 --- a/SingularityUI/app/actions/tailer.es6 +++ b/SingularityUI/app/actions/tailer.es6 @@ -10,6 +10,7 @@ export const PICK_TAILER_GROUP = "TAILER_PICK_TAILER_GROUP"; export const TOGGLE_TAILER_GROUP = "TAILER_TOGGLE_TAILER_GROUP"; export const SET_COLOR = "TAILER_SET_COLOR"; export const TAILER_SET_NOT_FOUND = "TAILER_SET_NOT_FOUND"; +export const SET_AUTHORIZATION_HEADER = "SET_AUTHORIZATION_HEADER"; export const buildTailerGroupInfo = (taskId, path, offset=-1) => ({taskId, path, offset}); @@ -23,6 +24,15 @@ export const setSearch = (search) => ({ search }); +export const setAuthHeaderIfConfigured = () => (dispatch) => { + if (config.generateAuthHeader) { + dispatch({ + type: SET_AUTHORIZATION_HEADER, + authorizationHeader: utils.getAuthTokenHeader() + }) + } +} + export const markNotFound = (taskId) => (dispatch, getState) => { const { tailerView } = getState(); const notFound = tailerView.notFound; diff --git a/SingularityUI/app/containers/CustomLogTailerContainer.jsx b/SingularityUI/app/containers/CustomLogTailerContainer.jsx index f24aa555b5..c704ef7e9d 100644 --- a/SingularityUI/app/containers/CustomLogTailerContainer.jsx +++ b/SingularityUI/app/containers/CustomLogTailerContainer.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { setTailerGroups } from '../actions/tailer'; +import { setTailerGroups, setAuthHeaderIfConfigured } from '../actions/tailer'; import LogTailerContainer from './LogTailerContainer'; import Utils from '../utils'; @@ -15,6 +15,8 @@ class CustomLogTailerContainer extends React.Component { // TODO: error if no task ids const tg = taskIds.map((taskId) => ({taskId, path: this.props.params.splat, offset: -1})); + this.props.setAuthHeaderIfConfigured(); + if (unifiedView) { this.props.setTailerGroups([tg]); } else { @@ -28,5 +30,6 @@ class CustomLogTailerContainer extends React.Component { }; export default connect(null, { - setTailerGroups + setTailerGroups, + setAuthHeaderIfConfigured })(CustomLogTailerContainer); diff --git a/SingularityUI/app/containers/RequestLogTailerContainer.jsx b/SingularityUI/app/containers/RequestLogTailerContainer.jsx index 9da96bacc7..129144ea53 100644 --- a/SingularityUI/app/containers/RequestLogTailerContainer.jsx +++ b/SingularityUI/app/containers/RequestLogTailerContainer.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { setTailerGroups } from '../actions/tailer'; +import { setTailerGroups, setAuthHeaderIfConfigured } from '../actions/tailer'; import LogTailerContainer from './LogTailerContainer'; @@ -29,6 +29,8 @@ class RequestLogTailerContainer extends React.Component { offset: -1 })); + this.props.setAuthHeaderIfConfigured(); + if (unifiedView) { this.props.setTailerGroups([tg]); } else { @@ -44,5 +46,6 @@ class RequestLogTailerContainer extends React.Component { export default connect(null, { setTailerGroups, + setAuthHeaderIfConfigured, fetchActiveTasksForRequest: FetchActiveTasksForRequest.trigger })(RequestLogTailerContainer); diff --git a/SingularityUI/app/containers/TaskLogTailerContainer.jsx b/SingularityUI/app/containers/TaskLogTailerContainer.jsx index 11855ccaed..5a8af66f59 100644 --- a/SingularityUI/app/containers/TaskLogTailerContainer.jsx +++ b/SingularityUI/app/containers/TaskLogTailerContainer.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { setTailerGroups } from '../actions/tailer'; +import { setTailerGroups, setAuthHeaderIfConfigured } from '../actions/tailer'; import { FetchActiveTasksForRequest } from '../actions/api/history'; import Utils from '../utils'; @@ -10,6 +10,7 @@ import LogTailerContainer from './LogTailerContainer'; class TaskLogTailerContainer extends React.Component { componentWillMount() { + this.props.setAuthHeaderIfConfigured(); this.props.setTailerGroups([[{ taskId: this.props.params.taskId, path: this.props.params.splat, @@ -26,5 +27,6 @@ class TaskLogTailerContainer extends React.Component { export default connect(null, { setTailerGroups, + setAuthHeaderIfConfigured, fetchActiveTasksForRequest: FetchActiveTasksForRequest.trigger })(TaskLogTailerContainer); diff --git a/SingularityUI/app/utils.es6 b/SingularityUI/app/utils.es6 index 8a8a57e0e7..59b3166cf2 100644 --- a/SingularityUI/app/utils.es6 +++ b/SingularityUI/app/utils.es6 @@ -473,6 +473,19 @@ const Utils = { } } return array.join('&'); + }, + + getAuthTokenHeader() { + if (!config.authCookieName) { + return null; + } + const encodedKey = encodeURIComponent(config.authCookieName).replace(/[\-\.\+\*]/g, '\\$&'); + const authCookie = decodeURIComponent(document.cookie.replace(new RegExp(`(?:(?:^|.*;)\\s*${encodedKey}\\s*\\=\\s*([^;]*).*$)|^.*$`), '$1')) || null; + if (!authCookie) { + return ''; + } + const authToken = JSON.parse(authCookie).token; + return `Bearer ${ authToken }`; } }; From 1b1fd359ed2b70b46b25536a3a626405e7a105cb Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 10:17:15 -0400 Subject: [PATCH 32/79] bump tailer version --- SingularityUI/SingularityTailer/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SingularityUI/SingularityTailer/package.json b/SingularityUI/SingularityTailer/package.json index 7f21b09593..193d6a0971 100644 --- a/SingularityUI/SingularityTailer/package.json +++ b/SingularityUI/SingularityTailer/package.json @@ -1,6 +1,6 @@ { "name": "singularityui-tailer", - "version": "0.3.11", + "version": "0.3.12", "description": "A robust log tailer", "author": "Danny Wolf ", "contributors": [ From 7d3976f933850cede913c33010f898d9de3bd36c Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 10:21:21 -0400 Subject: [PATCH 33/79] new tailer version --- SingularityUI/SingularityTailer/src/actions/index.js | 8 ++++---- SingularityUI/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/SingularityUI/SingularityTailer/src/actions/index.js b/SingularityUI/SingularityTailer/src/actions/index.js index d3469b0209..cbc0490e62 100644 --- a/SingularityUI/SingularityTailer/src/actions/index.js +++ b/SingularityUI/SingularityTailer/src/actions/index.js @@ -148,7 +148,7 @@ export const sandboxFetchChunk = (id, taskId, path, start, end, config) => { const options = {credentials: 'include'} if (config.authorizationHeader) { - options['headers'] = {'Authorization', config.authorizationHeader} + options['headers'] = {'Authorization': config.authorizationHeader} } return fetch(apiPath, options) @@ -192,7 +192,7 @@ export const sandboxFetchLength = (id, taskId, path, config) => { const options = {credentials: 'include'} if (config.authorizationHeader) { - options['headers'] = {'Authorization', config.authorizationHeader} + options['headers'] = {'Authorization': config.authorizationHeader} } return fetch(apiPath, options) @@ -259,7 +259,7 @@ export const blazarLogFetchChunk = (id, buildId, start, end, config) => { const options = {credentials: 'include'} if (config.authorizationHeader) { - options['headers'] = {'Authorization', config.authorizationHeader} + options['headers'] = {'Authorization': config.authorizationHeader} } return fetch(apiPath, options) @@ -302,7 +302,7 @@ export const blazarLogFetchLength = (id, buildId, config) => { const options = {credentials: 'include'} if (config.authorizationHeader) { - options['headers'] = {'Authorization', config.authorizationHeader} + options['headers'] = {'Authorization': config.authorizationHeader} } return fetch(apiPath, options) diff --git a/SingularityUI/package.json b/SingularityUI/package.json index 76db4cc4bd..d8544e5b30 100644 --- a/SingularityUI/package.json +++ b/SingularityUI/package.json @@ -62,7 +62,7 @@ "redux-thunk": "^2.0.1", "reselect": "^3.0.1", "select2": "^4.0.3", - "singularityui-tailer": "0.3.11", + "singularityui-tailer": "0.3.12", "typeahead.js": "^0.11.1", "underscore": "~1.8.0", "uuid": "^3.0.0" From d4fba378b646cd1e6caa640398eac12c3fcc9c3c Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 10:41:19 -0400 Subject: [PATCH 34/79] guice fix for test module --- .../scheduler/SingularityTestModule.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java index 3f4379cf29..42118f0c5c 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularityTestModule.java @@ -35,10 +35,12 @@ import com.hubspot.jackson.datatype.protobuf.ProtobufModule; import com.hubspot.mesos.client.MesosClient; import com.hubspot.singularity.SingularityAbort; -import com.hubspot.singularity.SingularityAuthModule; import com.hubspot.singularity.SingularityMainModule; import com.hubspot.singularity.SingularityTestAuthenticator; +import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; +import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; +import com.hubspot.singularity.auth.datastore.SingularityDisabledAuthDatastore; import com.hubspot.singularity.config.MesosConfiguration; import com.hubspot.singularity.config.SMTPConfiguration; import com.hubspot.singularity.config.SentryConfiguration; @@ -192,14 +194,12 @@ public void configure(Binder binder) { mainBinder.install(new SingularityZkMigrationsModule()); mainBinder.install(new SingularityEventModule(configuration)); - mainBinder.install(Modules.override(new SingularityAuthModule()) - .with(new Module() { - @Override - public void configure(Binder binder) { - binder.bind(SingularityAuthenticator.class).to(SingularityTestAuthenticator.class); - binder.bind(SingularityTestAuthenticator.class).in(Scopes.SINGLETON); - } - })); + + // Auth module bits + mainBinder.bind(SingularityAuthenticator.class).to(SingularityTestAuthenticator.class); + mainBinder.bind(SingularityAuthDatastore.class).to(SingularityDisabledAuthDatastore.class); + mainBinder.bind(SingularityAuthorizationHelper.class).in(Scopes.SINGLETON); + mainBinder.bind(SingularityTestAuthenticator.class).in(Scopes.SINGLETON); mainBinder.bind(DeployResource.class); mainBinder.bind(RequestResource.class); From 2f9108d5d6f2f1f00c4b11158b53b04fec58910e Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 10:49:11 -0400 Subject: [PATCH 35/79] missing dep on hk2-api --- SingularityService/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/SingularityService/pom.xml b/SingularityService/pom.xml index cdea44ec3c..f60b941619 100644 --- a/SingularityService/pom.xml +++ b/SingularityService/pom.xml @@ -290,6 +290,11 @@ jersey-server + + org.glassfish.hk2 + hk2-api + + com.fasterxml.jackson.core jackson-annotations From 4c2e394d6c83671f74433e027c274bf96a9955b1 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 13:46:30 -0400 Subject: [PATCH 36/79] move isAuthenticated --- .../SingularityUserPermissionsResponse.java | 14 +++----------- .../SingularityWebhookAuthenticator.java | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java index 0da3261995..02344fad72 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityUserPermissionsResponse.java @@ -9,13 +9,11 @@ public class SingularityUserPermissionsResponse { private final Optional user; - private final boolean authenticated; private final Optional error; @JsonCreator - public SingularityUserPermissionsResponse(@JsonProperty("user") Optional user, @JsonProperty("authenticated") boolean authenticated, @JsonProperty("error") Optional error) { + public SingularityUserPermissionsResponse(@JsonProperty("user") Optional user, @JsonProperty("error") Optional error) { this.user = user; - this.authenticated = authenticated; this.error = error; } @@ -23,10 +21,6 @@ public Optional getUser() { return user; } - public boolean isAuthenticated() { - return authenticated; - } - public Optional getError() { return error; } @@ -38,8 +32,7 @@ public boolean equals(Object obj) { } if (obj instanceof SingularityUserPermissionsResponse) { final SingularityUserPermissionsResponse that = (SingularityUserPermissionsResponse) obj; - return Objects.equals(this.authenticated, that.authenticated) && - Objects.equals(this.user, that.user) && + return Objects.equals(this.user, that.user) && Objects.equals(this.error, that.error); } return false; @@ -47,14 +40,13 @@ public boolean equals(Object obj) { @Override public int hashCode() { - return Objects.hash(user, authenticated, error); + return Objects.hash(user, error); } @Override public String toString() { return "SingularityUserPermissionsResponse{" + "user=" + user + - ", authenticated=" + authenticated + ", error=" + error + '}'; } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java index cda3292840..b5a68a92eb 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -74,7 +74,7 @@ private SingularityUserPermissionsResponse verify(String authHeaderValue) { } else { String responseBody = response.getResponseBody(); SingularityUserPermissionsResponse permissionsResponse = objectMapper.readValue(responseBody, SingularityUserPermissionsResponse.class); - if (!permissionsResponse.isAuthenticated()) { + if (!permissionsResponse.getUser().isPresent() && permissionsResponse.getUser().get().isAuthenticated()) { throw WebExceptions.unauthorized(String.format("(Webhook) User not authenticated (response: %s)", permissionsResponse)); } if (!permissionsResponse.getUser().isPresent()) { From baacbb68db832968315e487031a8b357ae0e662c Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 13:55:29 -0400 Subject: [PATCH 37/79] utils -> Utils --- SingularityUI/app/actions/tailer.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SingularityUI/app/actions/tailer.es6 b/SingularityUI/app/actions/tailer.es6 index 1a15b1f041..487358b297 100644 --- a/SingularityUI/app/actions/tailer.es6 +++ b/SingularityUI/app/actions/tailer.es6 @@ -28,7 +28,7 @@ export const setAuthHeaderIfConfigured = () => (dispatch) => { if (config.generateAuthHeader) { dispatch({ type: SET_AUTHORIZATION_HEADER, - authorizationHeader: utils.getAuthTokenHeader() + authorizationHeader: Utils.getAuthTokenHeader() }) } } From 990947d0da8a23cfdb85039f23f5bf568f6bd7b2 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 14:09:56 -0400 Subject: [PATCH 38/79] correctly wire up setAuthHeader --- SingularityUI/SingularityTailer/package.json | 2 +- SingularityUI/SingularityTailer/src/actions/index.js | 6 ++++++ SingularityUI/app/actions/tailer.es6 | 10 ---------- .../app/containers/CustomLogTailerContainer.jsx | 5 +---- .../app/containers/RequestLogTailerContainer.jsx | 5 +---- .../app/containers/TaskLogTailerContainer.jsx | 4 +--- SingularityUI/app/initialize.jsx | 3 +++ SingularityUI/package.json | 2 +- 8 files changed, 14 insertions(+), 23 deletions(-) diff --git a/SingularityUI/SingularityTailer/package.json b/SingularityUI/SingularityTailer/package.json index 193d6a0971..4bda86036b 100644 --- a/SingularityUI/SingularityTailer/package.json +++ b/SingularityUI/SingularityTailer/package.json @@ -1,6 +1,6 @@ { "name": "singularityui-tailer", - "version": "0.3.12", + "version": "0.3.13", "description": "A robust log tailer", "author": "Danny Wolf ", "contributors": [ diff --git a/SingularityUI/SingularityTailer/src/actions/index.js b/SingularityUI/SingularityTailer/src/actions/index.js index cbc0490e62..0aac3d836a 100644 --- a/SingularityUI/SingularityTailer/src/actions/index.js +++ b/SingularityUI/SingularityTailer/src/actions/index.js @@ -136,6 +136,12 @@ export const tailIntervalMs = (tailIntervalMs) => ({ tailIntervalMs }); +export const SET_AUTHORIZATION_HEADER = `${frameworkName}_SET_AUTHORIZATION_HEADER`; +export const setAuthorizationHeader = (authorizationHeader) => ({ + type: SET_AUTHORIZATION_HEADER, + authorizationHeader +}); + export const sandboxFetchChunk = (id, taskId, path, start, end, config) => { return (dispatch) => { dispatch( diff --git a/SingularityUI/app/actions/tailer.es6 b/SingularityUI/app/actions/tailer.es6 index 487358b297..d4407021c8 100644 --- a/SingularityUI/app/actions/tailer.es6 +++ b/SingularityUI/app/actions/tailer.es6 @@ -10,7 +10,6 @@ export const PICK_TAILER_GROUP = "TAILER_PICK_TAILER_GROUP"; export const TOGGLE_TAILER_GROUP = "TAILER_TOGGLE_TAILER_GROUP"; export const SET_COLOR = "TAILER_SET_COLOR"; export const TAILER_SET_NOT_FOUND = "TAILER_SET_NOT_FOUND"; -export const SET_AUTHORIZATION_HEADER = "SET_AUTHORIZATION_HEADER"; export const buildTailerGroupInfo = (taskId, path, offset=-1) => ({taskId, path, offset}); @@ -24,15 +23,6 @@ export const setSearch = (search) => ({ search }); -export const setAuthHeaderIfConfigured = () => (dispatch) => { - if (config.generateAuthHeader) { - dispatch({ - type: SET_AUTHORIZATION_HEADER, - authorizationHeader: Utils.getAuthTokenHeader() - }) - } -} - export const markNotFound = (taskId) => (dispatch, getState) => { const { tailerView } = getState(); const notFound = tailerView.notFound; diff --git a/SingularityUI/app/containers/CustomLogTailerContainer.jsx b/SingularityUI/app/containers/CustomLogTailerContainer.jsx index c704ef7e9d..538940aadb 100644 --- a/SingularityUI/app/containers/CustomLogTailerContainer.jsx +++ b/SingularityUI/app/containers/CustomLogTailerContainer.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { setTailerGroups, setAuthHeaderIfConfigured } from '../actions/tailer'; +import { setTailerGroups } from '../actions/tailer'; import LogTailerContainer from './LogTailerContainer'; import Utils from '../utils'; @@ -15,8 +15,6 @@ class CustomLogTailerContainer extends React.Component { // TODO: error if no task ids const tg = taskIds.map((taskId) => ({taskId, path: this.props.params.splat, offset: -1})); - this.props.setAuthHeaderIfConfigured(); - if (unifiedView) { this.props.setTailerGroups([tg]); } else { @@ -31,5 +29,4 @@ class CustomLogTailerContainer extends React.Component { export default connect(null, { setTailerGroups, - setAuthHeaderIfConfigured })(CustomLogTailerContainer); diff --git a/SingularityUI/app/containers/RequestLogTailerContainer.jsx b/SingularityUI/app/containers/RequestLogTailerContainer.jsx index 129144ea53..9da96bacc7 100644 --- a/SingularityUI/app/containers/RequestLogTailerContainer.jsx +++ b/SingularityUI/app/containers/RequestLogTailerContainer.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { setTailerGroups, setAuthHeaderIfConfigured } from '../actions/tailer'; +import { setTailerGroups } from '../actions/tailer'; import LogTailerContainer from './LogTailerContainer'; @@ -29,8 +29,6 @@ class RequestLogTailerContainer extends React.Component { offset: -1 })); - this.props.setAuthHeaderIfConfigured(); - if (unifiedView) { this.props.setTailerGroups([tg]); } else { @@ -46,6 +44,5 @@ class RequestLogTailerContainer extends React.Component { export default connect(null, { setTailerGroups, - setAuthHeaderIfConfigured, fetchActiveTasksForRequest: FetchActiveTasksForRequest.trigger })(RequestLogTailerContainer); diff --git a/SingularityUI/app/containers/TaskLogTailerContainer.jsx b/SingularityUI/app/containers/TaskLogTailerContainer.jsx index 5a8af66f59..11855ccaed 100644 --- a/SingularityUI/app/containers/TaskLogTailerContainer.jsx +++ b/SingularityUI/app/containers/TaskLogTailerContainer.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { setTailerGroups, setAuthHeaderIfConfigured } from '../actions/tailer'; +import { setTailerGroups } from '../actions/tailer'; import { FetchActiveTasksForRequest } from '../actions/api/history'; import Utils from '../utils'; @@ -10,7 +10,6 @@ import LogTailerContainer from './LogTailerContainer'; class TaskLogTailerContainer extends React.Component { componentWillMount() { - this.props.setAuthHeaderIfConfigured(); this.props.setTailerGroups([[{ taskId: this.props.params.taskId, path: this.props.params.splat, @@ -27,6 +26,5 @@ class TaskLogTailerContainer extends React.Component { export default connect(null, { setTailerGroups, - setAuthHeaderIfConfigured, fetchActiveTasksForRequest: FetchActiveTasksForRequest.trigger })(TaskLogTailerContainer); diff --git a/SingularityUI/app/initialize.jsx b/SingularityUI/app/initialize.jsx index 872b1627aa..174b27dc4e 100644 --- a/SingularityUI/app/initialize.jsx +++ b/SingularityUI/app/initialize.jsx @@ -61,6 +61,9 @@ document.addEventListener('DOMContentLoaded', () => { const store = configureStore({}, history); store.dispatch(tailerActions.sandboxSetApiRoot(config.apiRoot)); + if (config.generateAuthHeader) { + store.dispath(tailerActions.setAuthorizationHeader(Utils.getAuthTokenHeader())); + } // set up user let userId; diff --git a/SingularityUI/package.json b/SingularityUI/package.json index d8544e5b30..2369cf5764 100644 --- a/SingularityUI/package.json +++ b/SingularityUI/package.json @@ -62,7 +62,7 @@ "redux-thunk": "^2.0.1", "reselect": "^3.0.1", "select2": "^4.0.3", - "singularityui-tailer": "0.3.12", + "singularityui-tailer": "0.3.13", "typeahead.js": "^0.11.1", "underscore": "~1.8.0", "uuid": "^3.0.0" From 4bef1daed89725c2b65fe53ac716a1dd6d191a9e Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 14:14:09 -0400 Subject: [PATCH 39/79] typo --- SingularityUI/app/initialize.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SingularityUI/app/initialize.jsx b/SingularityUI/app/initialize.jsx index 174b27dc4e..86a721a577 100644 --- a/SingularityUI/app/initialize.jsx +++ b/SingularityUI/app/initialize.jsx @@ -62,7 +62,7 @@ document.addEventListener('DOMContentLoaded', () => { store.dispatch(tailerActions.sandboxSetApiRoot(config.apiRoot)); if (config.generateAuthHeader) { - store.dispath(tailerActions.setAuthorizationHeader(Utils.getAuthTokenHeader())); + store.dispatch(tailerActions.setAuthorizationHeader(Utils.getAuthTokenHeader())); } // set up user From 4a4544c7b02d2ca9bdeb9ae2366b3d1c291313c5 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 14:16:40 -0400 Subject: [PATCH 40/79] better name for this --- .../java/com/hubspot/singularity/SingularityAuthModule.java | 4 ++-- .../hubspot/singularity/auth/SingularityAuthFeature.java | 4 ++-- ...icator.java => SingularityMultiMethodAuthenticator.java} | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/{SingularityMultiLevelAuthenticator.java => SingularityMultiMethodAuthenticator.java} (86%) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java index e81689a372..669f2cbf19 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java @@ -8,7 +8,7 @@ import com.hubspot.singularity.auth.SingularityAuthenticatorClass; import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.auth.authenticator.SingularityAuthenticator; -import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; +import com.hubspot.singularity.auth.authenticator.SingularityMultiMethodAuthenticator; import com.hubspot.singularity.auth.datastore.SingularityAuthDatastore; import com.hubspot.singularity.config.SingularityConfiguration; @@ -24,7 +24,7 @@ public void configure(Binder binder) { } binder.bind(SingularityAuthFeature.class); - binder.bind(SingularityMultiLevelAuthenticator.class); + binder.bind(SingularityMultiMethodAuthenticator.class); binder.bind(SingularityAuthDatastore.class).to(getConfiguration().getAuthConfiguration().getDatastore().getAuthDatastoreClass()); binder.bind(SingularityAuthorizationHelper.class).in(Scopes.SINGLETON); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java index 66679049a9..833b009bed 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthFeature.java @@ -11,7 +11,7 @@ import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider; import com.hubspot.singularity.SingularityUser; -import com.hubspot.singularity.auth.authenticator.SingularityMultiLevelAuthenticator; +import com.hubspot.singularity.auth.authenticator.SingularityMultiMethodAuthenticator; import io.dropwizard.auth.Auth; import io.dropwizard.auth.Authenticator; @@ -26,7 +26,7 @@ public boolean configure(FeatureContext context) { context.register(new AbstractBinder() { @Override public void configure() { - bind(SingularityMultiLevelAuthenticator.class).to(new TypeLiteral>() {}).in(Singleton.class); + bind(SingularityMultiMethodAuthenticator.class).to(new TypeLiteral>() {}).in(Singleton.class); bind(SingularityAuthedUserFactory.class).to(SingularityAuthedUserFactory.class).in(Singleton.class); bind(SingularityAuthFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class); bind(SingularityAuthParamInjectionResolver.class).to(new TypeLiteral>(){}).in(Singleton.class); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiMethodAuthenticator.java similarity index 86% rename from SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java rename to SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiMethodAuthenticator.java index dfb831fb1b..bb2a7e3575 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiLevelAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiMethodAuthenticator.java @@ -16,14 +16,14 @@ import io.dropwizard.auth.Authenticator; -public class SingularityMultiLevelAuthenticator implements Authenticator { - private static final Logger LOG = LoggerFactory.getLogger(SingularityMultiLevelAuthenticator.class); +public class SingularityMultiMethodAuthenticator implements Authenticator { + private static final Logger LOG = LoggerFactory.getLogger(SingularityMultiMethodAuthenticator.class); private final Set authenticators; private final SingularityConfiguration configuration; @javax.inject.Inject - public SingularityMultiLevelAuthenticator(Set authenticators, SingularityConfiguration configuration) { + public SingularityMultiMethodAuthenticator(Set authenticators, SingularityConfiguration configuration) { this.authenticators = authenticators; this.configuration = configuration; } From b11dc5c1f81b75052d8fe952819c59fe739b1efb Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 27 Oct 2017 14:20:34 -0400 Subject: [PATCH 41/79] 401s returning properly, don't need this anymore --- SingularityUI/app/initialize.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/SingularityUI/app/initialize.jsx b/SingularityUI/app/initialize.jsx index 86a721a577..6e557c6a0b 100644 --- a/SingularityUI/app/initialize.jsx +++ b/SingularityUI/app/initialize.jsx @@ -70,9 +70,6 @@ document.addEventListener('DOMContentLoaded', () => { window.app = {}; window.app.setupUser = () => store.dispatch(FetchUser.trigger()); window.app.setupUser().then(() => { - if (store.getState().api.user.data.authEnabled && !store.getState().api.user.data.authenticated) { // redirect to login - window.location.href = config.redirectOnUnauthorizedUrl.replace('{URL}', encodeURIComponent(window.location.href)); - } if (!store.getState().api.user.data.user) { return renderUserIdForm(); } else { From 4db8a6ddebeee5a0d92ccea74c41f08c07f5a5dc Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Mon, 30 Oct 2017 09:17:44 -0400 Subject: [PATCH 42/79] auth check on current user, not id --- .../singularity/client/SingularityClient.java | 25 +++++++++++++++++-- .../singularity/resources/AuthResource.java | 10 ++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/SingularityClient/src/main/java/com/hubspot/singularity/client/SingularityClient.java b/SingularityClient/src/main/java/com/hubspot/singularity/client/SingularityClient.java index 90d9eedde4..3c8a6b62c3 100644 --- a/SingularityClient/src/main/java/com/hubspot/singularity/client/SingularityClient.java +++ b/SingularityClient/src/main/java/com/hubspot/singularity/client/SingularityClient.java @@ -104,7 +104,8 @@ public class SingularityClient { private static final String BASE_API_FORMAT = "%s://%s/%s"; - private static final String AUTH_CHECK_FORMAT = "%s/auth/%s/auth-check/%s"; + private static final String AUTH_CHECK_FORMAT = "%s/auth/%s/auth-check"; + private static final String AUTH_CHECK_USER_FORMAT = AUTH_CHECK_FORMAT + "/%s"; private static final String STATE_FORMAT = "%s/state"; private static final String TASK_RECONCILIATION_FORMAT = STATE_FORMAT + "/task-reconciliation"; @@ -1446,7 +1447,27 @@ public void deletePriorityFreeze() { * true if the user is authorized for scope, false otherwise */ public boolean isUserAuthorized(String requestId, String userId, SingularityAuthorizationScope scope) { - final Function requestUri = (host) -> String.format(AUTH_CHECK_FORMAT, getApiBase(host), requestId, userId); + final Function requestUri = (host) -> String.format(AUTH_CHECK_USER_FORMAT, getApiBase(host), requestId, userId); + Map params = Collections.singletonMap("scope", scope.name()); + HttpResponse response = executeGetSingleWithParams(requestUri, "auth check", "", Optional.of(params)); + return response.isSuccess(); + } + + /** + * Check if the current client's user is authorized for the specified scope on the specified request + * + * @param requestId + * The request to check authorization on + * @param userId + * The user whose authorization will be checked + * @param scope + * The scope to check that `user` has + * + * @return + * true if the user is authorized for scope, false otherwise + */ + public boolean isUserAuthorized(String requestId, SingularityAuthorizationScope scope) { + final Function requestUri = (host) -> String.format(AUTH_CHECK_FORMAT, getApiBase(host), requestId); Map params = Collections.singletonMap("scope", scope.name()); HttpResponse response = executeGetSingleWithParams(requestUri, "auth check", "", Optional.of(params)); return response.isSuccess(); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java index 4cf20a85f3..8273b0d283 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java @@ -59,4 +59,14 @@ public Response checkReadOnlyAuth(@PathParam("requestId") String requestId, @Pat authorizationHelper.checkForAuthorizationByRequestId(requestId, authDatastore.getUser(userId).orElse(SingularityUser.defaultUser()), scope.or(SingularityAuthorizationScope.READ)); return Response.ok().build(); } + + @GET + @Path("/{requestId}/auth-check}") + @ApiOperation("Check if the specified user is authorized for a request") + public Response checkReadOnlyAuth(@Auth SingularityUser user, + @PathParam("requestId") String requestId, @PathParam("userId") String userId, + @QueryParam("scope") Optional scope) { + authorizationHelper.checkForAuthorizationByRequestId(requestId, user, scope.or(SingularityAuthorizationScope.READ)); + return Response.ok().build(); + } } From da8e0867d0449299a160b69bab4f86d6f2bf8ab2 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Mon, 30 Oct 2017 09:58:51 -0400 Subject: [PATCH 43/79] auth on additional endpoints --- .../resources/HistoryResource.java | 15 +++-- .../resources/PriorityResource.java | 3 +- .../singularity/resources/RackResource.java | 6 +- .../resources/RequestGroupResource.java | 66 +++++++++---------- .../singularity/resources/SlaveResource.java | 10 ++- .../resources/TaskTrackerResource.java | 16 ++++- 6 files changed, 68 insertions(+), 48 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java index a8fdd88826..34b88ab5e9 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/HistoryResource.java @@ -69,8 +69,9 @@ public HistoryResource(HistoryManager historyManager, TaskManager taskManager, D @GET @Path("/task/{taskId}") @ApiOperation("Retrieve the history for a specific task.") - public SingularityTaskHistory getHistoryForTask(@Auth SingularityUser user, - @ApiParam("Task ID to look up") @PathParam("taskId") String taskId) { + public SingularityTaskHistory getHistoryForTask( + @Auth SingularityUser user, + @ApiParam("Task ID to look up") @PathParam("taskId") String taskId) { SingularityTaskId taskIdObj = getTaskIdObject(taskId); return getTaskHistoryRequired(taskIdObj, user); @@ -121,9 +122,10 @@ public List getTaskHistoryForRequest(@Auth Singularity @GET @Path("/request/{requestId}/deploy/{deployId}") @ApiOperation("Retrieve the history for a specific deploy.") - public SingularityDeployHistory getDeploy(@Auth SingularityUser user, - @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, - @ApiParam("Deploy ID") @PathParam("deployId") String deployId) { + public SingularityDeployHistory getDeploy( + @Auth SingularityUser user, + @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, + @ApiParam("Deploy ID") @PathParam("deployId") String deployId) { return getDeployHistory(requestId, deployId, user); } @@ -142,7 +144,8 @@ public List getActiveDeployTasks( @GET @Path("/request/{requestId}/deploy/{deployId}/tasks/inactive") @ApiOperation("Retrieve the task history for a specific deploy.") - public List getInactiveDeployTasks(@Auth SingularityUser user, + public List getInactiveDeployTasks( + @Auth SingularityUser user, @ApiParam("Request ID for deploy") @PathParam("requestId") String requestId, @ApiParam("Deploy ID") @PathParam("deployId") String deployId, @ApiParam("Maximum number of items to return") @QueryParam("count") Integer count, diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java index bd7a58be34..d5241e108f 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/PriorityResource.java @@ -48,7 +48,8 @@ public PriorityResource(SingularityAuthorizationHelper authorizationHelper, Sing @ApiResponse(code=200, message="The active priority freeze."), @ApiResponse(code=404, message="There was no active priority freeze.") }) - public Optional getActivePriorityFreeze() { + public Optional getActivePriorityFreeze(@Auth SingularityUser user) { + authorizationHelper.checkAdminAuthorization(user); return priorityManager.getActivePriorityFreeze(); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java index c50a0801fe..6914eeed94 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/RackResource.java @@ -48,14 +48,16 @@ protected String getObjectTypeString() { @GET @Path("/") @ApiOperation("Retrieve the list of all known racks, optionally filtering by a particular state") - public List getRacks(@ApiParam("Optionally specify a particular state to filter racks by") @QueryParam("state") Optional filterState) { + public List getRacks(@Auth SingularityUser user, @ApiParam("Optionally specify a particular state to filter racks by") @QueryParam("state") Optional filterState) { + authorizationHelper.checkAdminAuthorization(user); return manager.getObjectsFiltered(filterState); } @GET @Path("/rack/{rackId}") @ApiOperation("Retrieve the history of a given rack") - public List getRackHistory(@ApiParam("Rack ID") @PathParam("rackId") String rackId) { + public List getRackHistory(@Auth SingularityUser user, @ApiParam("Rack ID") @PathParam("rackId") String rackId) { + authorizationHelper.checkAdminAuthorization(user); return manager.getHistory(rackId); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestGroupResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestGroupResource.java index 5777556099..523aa88394 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestGroupResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/RequestGroupResource.java @@ -21,45 +21,45 @@ import com.wordnik.swagger.annotations.ApiOperation; @Path(ApiPaths.REQUEST_GROUP_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -@Api(description="Manages Singularity Request Groups, which are collections of one or more Singularity Requests", value=ApiPaths.REQUEST_GROUP_RESOURCE_PATH, position=1) +@Produces({MediaType.APPLICATION_JSON}) +@Api(description = "Manages Singularity Request Groups, which are collections of one or more Singularity Requests", value = ApiPaths.REQUEST_GROUP_RESOURCE_PATH, position = 1) public class RequestGroupResource { - private final RequestGroupManager requestGroupManager; - private final SingularityValidator validator; + private final RequestGroupManager requestGroupManager; + private final SingularityValidator validator; - @Inject - public RequestGroupResource(RequestGroupManager requestGroupManager, SingularityValidator validator) { - this.requestGroupManager = requestGroupManager; - this.validator = validator; - } + @Inject + public RequestGroupResource(RequestGroupManager requestGroupManager, SingularityValidator validator) { + this.requestGroupManager = requestGroupManager; + this.validator = validator; + } - @GET - @ApiOperation(value="Get a list of Singularity request groups") - public List getRequestGroupIds(@QueryParam("useWebCache") Boolean useWebCache) { - return requestGroupManager.getRequestGroups(useWebCache != null && useWebCache); - } + @GET + @ApiOperation(value = "Get a list of Singularity request groups") + public List getRequestGroupIds(@QueryParam("useWebCache") Boolean useWebCache) { + return requestGroupManager.getRequestGroups(useWebCache != null && useWebCache); + } - @GET - @Path("/group/{requestGroupId}") - @ApiOperation(value="Get a specific Singularity request group by ID") - public Optional getRequestGroup(@PathParam("requestGroupId") String requestGroupId) { - return requestGroupManager.getRequestGroup(requestGroupId); - } + @GET + @Path("/group/{requestGroupId}") + @ApiOperation(value = "Get a specific Singularity request group by ID") + public Optional getRequestGroup(@PathParam("requestGroupId") String requestGroupId) { + return requestGroupManager.getRequestGroup(requestGroupId); + } - @DELETE - @Path("/group/{requestGroupId}") - @ApiOperation(value="Delete a specific Singularity request group by ID") - public void deleteRequestGroup(@PathParam("requestGroupId") String requestGroupId) { - requestGroupManager.deleteRequestGroup(requestGroupId); - } + @DELETE + @Path("/group/{requestGroupId}") + @ApiOperation(value = "Delete a specific Singularity request group by ID") + public void deleteRequestGroup(@PathParam("requestGroupId") String requestGroupId) { + requestGroupManager.deleteRequestGroup(requestGroupId); + } - @POST - @ApiOperation(value="Create a Singularity request group") - public SingularityRequestGroup saveRequestGroup(SingularityRequestGroup requestGroup) { - validator.checkRequestGroup(requestGroup); + @POST + @ApiOperation(value = "Create a Singularity request group") + public SingularityRequestGroup saveRequestGroup(SingularityRequestGroup requestGroup) { + validator.checkRequestGroup(requestGroup); - requestGroupManager.saveRequestGroup(requestGroup); + requestGroupManager.saveRequestGroup(requestGroup); - return requestGroup; - } + return requestGroup; + } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java index 0406381f85..aba56eec3e 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/SlaveResource.java @@ -47,21 +47,25 @@ protected String getObjectTypeString() { @GET @Path("/") @ApiOperation("Retrieve the list of all known slaves, optionally filtering by a particular state") - public List getSlaves(@ApiParam("Optionally specify a particular state to filter slaves by") @QueryParam("state") Optional filterState) { + public List getSlaves(@Auth SingularityUser user, + @ApiParam("Optionally specify a particular state to filter slaves by") @QueryParam("state") Optional filterState) { + authorizationHelper.checkAdminAuthorization(user); return manager.getObjectsFiltered(filterState); } @GET @Path("/slave/{slaveId}") @ApiOperation("Retrieve the history of a given slave") - public List getSlaveHistory(@ApiParam("Slave ID") @PathParam("slaveId") String slaveId) { + public List getSlaveHistory(@Auth SingularityUser user, @ApiParam("Slave ID") @PathParam("slaveId") String slaveId) { + authorizationHelper.checkAdminAuthorization(user); return manager.getHistory(slaveId); } @GET @Path("/slave/{slaveId}/details") @ApiOperation("Get information about a particular slave") - public Optional getSlave(@ApiParam("Slave ID") @PathParam("slaveId") String slaveId) { + public Optional getSlave(@Auth SingularityUser user, @ApiParam("Slave ID") @PathParam("slaveId") String slaveId) { + authorizationHelper.checkAdminAuthorization(user); return manager.getObject(slaveId); } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskTrackerResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskTrackerResource.java index 147affa3b3..790dbbdf40 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskTrackerResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/TaskTrackerResource.java @@ -10,10 +10,13 @@ import com.google.common.base.Optional; import com.google.inject.Inject; +import com.hubspot.singularity.SingularityAuthorizationScope; import com.hubspot.singularity.SingularityPendingTask; import com.hubspot.singularity.SingularityTaskHistory; import com.hubspot.singularity.SingularityTaskId; import com.hubspot.singularity.SingularityTaskState; +import com.hubspot.singularity.SingularityUser; +import com.hubspot.singularity.auth.SingularityAuthorizationHelper; import com.hubspot.singularity.config.ApiPaths; import com.hubspot.singularity.data.TaskManager; import com.hubspot.singularity.data.history.HistoryManager; @@ -22,17 +25,21 @@ import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; +import io.dropwizard.auth.Auth; + @Path(ApiPaths.TASK_TRACKER_RESOURCE_PATH) @Produces({MediaType.APPLICATION_JSON}) @Api(description="Find a task by taskId or runId", value=ApiPaths.TASK_TRACKER_RESOURCE_PATH) public class TaskTrackerResource { private final TaskManager taskManager; private final HistoryManager historyManager; + private final SingularityAuthorizationHelper authorizationHelper; @Inject - public TaskTrackerResource(TaskManager taskManager, HistoryManager historyManager) { + public TaskTrackerResource(TaskManager taskManager, HistoryManager historyManager, SingularityAuthorizationHelper authorizationHelper) { this.taskManager = taskManager; this.historyManager = historyManager; + this.authorizationHelper = authorizationHelper; } @GET @@ -41,7 +48,8 @@ public TaskTrackerResource(TaskManager taskManager, HistoryManager historyManage @ApiResponses({ @ApiResponse(code=404, message="Task with this id does not exist") }) - public Optional getTaskState(@PathParam("taskId") String taskId) { + public Optional getTaskState(@Auth SingularityUser user, @PathParam("taskId") String taskId) { + authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); return getTaskStateFromId(SingularityTaskId.valueOf(taskId)); } @@ -51,7 +59,9 @@ public Optional getTaskState(@PathParam("taskId") String t @ApiResponses({ @ApiResponse(code=404, message="Task with this runId does not exist") }) - public Optional getTaskStateByRunId(@PathParam("requestId") String requestId, @PathParam("runId") String runId) { + public Optional getTaskStateByRunId(@Auth SingularityUser user, @PathParam("requestId") String requestId, @PathParam("runId") String runId) { + authorizationHelper.checkForAuthorizationByRequestId(requestId, user, SingularityAuthorizationScope.READ); + // Check if it's active or inactive Optional maybeTaskId = taskManager.getTaskByRunId(requestId, runId); if (maybeTaskId.isPresent()) { From d646b74e663ffd2217e5d0aa875d26f4a475d403 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Mon, 30 Oct 2017 14:19:42 -0400 Subject: [PATCH 44/79] change auth checks in utilization resource --- .../com/hubspot/singularity/resources/UsageResource.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java index 746c53626e..a3a5c0753d 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/UsageResource.java @@ -11,6 +11,7 @@ import com.google.common.base.Optional; import com.google.inject.Inject; +import com.hubspot.singularity.SingularityAuthorizationScope; import com.hubspot.singularity.SingularityClusterUtilization; import com.hubspot.singularity.SingularityService; import com.hubspot.singularity.SingularitySlave; @@ -86,14 +87,14 @@ public List getSlaveUsageHistory(@Auth SingularityUser us @GET @Path("/tasks/{taskId}/history") public List getTaskUsageHistory(@Auth SingularityUser user, @PathParam("taskId") String taskId) { - authorizationHelper.checkAdminAuthorization(user); + authorizationHelper.checkForAuthorizationByTaskId(taskId, user, SingularityAuthorizationScope.READ); return usageManager.getTaskUsage(taskId); } @GET @Path("/cluster/utilization") public SingularityClusterUtilization getClusterUtilization(@Auth SingularityUser user) { - authorizationHelper.checkAdminAuthorization(user); + //authorizationHelper.checkAdminAuthorization(user); Needed for ui pages outside single request WebExceptions.checkNotFound(usageManager.getClusterUtilization().isPresent(), "No cluster utilization has been saved yet"); return usageManager.getClusterUtilization().get(); From dd996bf1dac9fec5a6d29e510ca03ff80314e235 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Mon, 30 Oct 2017 14:36:37 -0400 Subject: [PATCH 45/79] don't need this --- .../java/com/hubspot/singularity/SingularityAuthModule.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java index 669f2cbf19..c406989cdd 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityAuthModule.java @@ -27,7 +27,5 @@ public void configure(Binder binder) { binder.bind(SingularityMultiMethodAuthenticator.class); binder.bind(SingularityAuthDatastore.class).to(getConfiguration().getAuthConfiguration().getDatastore().getAuthDatastoreClass()); binder.bind(SingularityAuthorizationHelper.class).in(Scopes.SINGLETON); - - getEnvironment().jersey().register(SingularityAuthFeature.class); } } From 59f41b8ba6d6357ff17d50391a532fcb8b52bfef Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Thu, 2 Nov 2017 10:45:59 -0400 Subject: [PATCH 46/79] remove the cluster coordinator module, won't work with newer auth --- .../singularity/SingularityRequest.java | 4 +- .../SingularityRequestBuilder.java | 3 + SingularityClusterCoordinator/.blazar.yaml | 3 - .../.build-executable | 0 SingularityClusterCoordinator/pom.xml | 133 -------- .../CoordinatorDropwizardHealthcheck.java | 23 -- .../SingularityClusterCoordinator.java | 46 --- .../SingularityClusterCoordinatorModule.java | 19 -- .../ClusterCoordinatorConfiguration.java | 174 ---------- .../singularity/config/DataCenter.java | 74 ---- .../DataCenterConflictException.java | 9 - .../DataCenterNotFoundException.java | 9 - .../exceptions/NotImplemenedException.java | 9 - .../singularity/proxy/AuthResource.java | 36 -- .../singularity/proxy/DataCenterLocator.java | 206 ----------- .../singularity/proxy/DeployResource.java | 50 --- .../singularity/proxy/DisastersResource.java | 105 ------ .../singularity/proxy/HistoryResource.java | 145 -------- .../proxy/InactiveSlaveResource.java | 38 --- .../singularity/proxy/PriorityResource.java | 43 --- .../singularity/proxy/ProxyResource.java | 323 ------------------ .../singularity/proxy/RackResource.java | 74 ---- .../proxy/RequestGroupResource.java | 49 --- .../singularity/proxy/RequestResource.java | 272 --------------- .../singularity/proxy/S3LogResource.java | 53 --- .../singularity/proxy/SandboxResource.java | 36 -- ...arityClusterCoodinatorResourcesModule.java | 111 ------ .../singularity/proxy/SlaveResource.java | 77 ----- .../singularity/proxy/StateResource.java | 47 --- .../singularity/proxy/TaskResource.java | 144 -------- .../proxy/TaskTrackerResource.java | 35 -- .../singularity/proxy/UsageResource.java | 44 --- .../singularity/proxy/UserResource.java | 48 --- .../singularity/proxy/WebhookResource.java | 94 ----- pom.xml | 1 - 35 files changed, 6 insertions(+), 2531 deletions(-) delete mode 100644 SingularityClusterCoordinator/.blazar.yaml delete mode 100644 SingularityClusterCoordinator/.build-executable delete mode 100644 SingularityClusterCoordinator/pom.xml delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/CoordinatorDropwizardHealthcheck.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinator.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinatorModule.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/ClusterCoordinatorConfiguration.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/DataCenter.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterConflictException.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterNotFoundException.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/NotImplemenedException.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/AuthResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DataCenterLocator.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DeployResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DisastersResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/HistoryResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/InactiveSlaveResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/PriorityResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/ProxyResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RackResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestGroupResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/S3LogResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SandboxResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SingularityClusterCoodinatorResourcesModule.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SlaveResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/StateResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskTrackerResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UsageResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UserResource.java delete mode 100644 SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/WebhookResource.java diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequest.java b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequest.java index 4c22e545d5..e1b3f24090 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequest.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequest.java @@ -61,6 +61,7 @@ public class SingularityRequest { private final Optional maxTasksPerOffer; private final Optional allowBounceToSameHost; + @Deprecated private final Optional dataCenter; @JsonCreator @@ -361,7 +362,8 @@ public Optional getTaskPriorityLevel() { return taskPriorityLevel; } - @ApiModelProperty(required=false, value="When using the cluster coordinator, the data center associated with this request") + @Deprecated + @ApiModelProperty(required=false, value="the data center associated with this request") public Optional getDataCenter() { return dataCenter; } diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequestBuilder.java b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequestBuilder.java index 93194c7853..f2a08522cd 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequestBuilder.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityRequestBuilder.java @@ -51,6 +51,7 @@ public class SingularityRequestBuilder { private Optional taskPriorityLevel; private Optional maxTasksPerOffer; private Optional allowBounceToSameHost; + @Deprecated private Optional dataCenter; public SingularityRequestBuilder(String id, RequestType requestType) { @@ -357,10 +358,12 @@ public SingularityRequestBuilder setAllowBounceToSameHost(Optional allo return this; } + @Deprecated public Optional getDataCenter() { return dataCenter; } + @Deprecated public SingularityRequestBuilder setDataCenter(Optional dataCenter) { this.dataCenter = dataCenter; return this; diff --git a/SingularityClusterCoordinator/.blazar.yaml b/SingularityClusterCoordinator/.blazar.yaml deleted file mode 100644 index 0748966170..0000000000 --- a/SingularityClusterCoordinator/.blazar.yaml +++ /dev/null @@ -1,3 +0,0 @@ -env: - MAVEN_ARGS: "-T 8 -Dsingularity.jar.name.format=\\${project.artifactId} -Dgpg.skip=true -DforkCount=2C" - SET_VERSION_OVERRIDE: "0.18.0-$GIT_BRANCH-SNAPSHOT" diff --git a/SingularityClusterCoordinator/.build-executable b/SingularityClusterCoordinator/.build-executable deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/SingularityClusterCoordinator/pom.xml b/SingularityClusterCoordinator/pom.xml deleted file mode 100644 index 4ae4d34375..0000000000 --- a/SingularityClusterCoordinator/pom.xml +++ /dev/null @@ -1,133 +0,0 @@ - - - 4.0.0 - - - com.hubspot - Singularity - 0.18.0-SNAPSHOT - - - SingularityClusterCoordinator - - - com.hubspot.singularity.SingularityClusterCoordinator - - - - - com.hubspot - SingularityBase - - - - com.hubspot - SingularityServiceBase - - - - com.hubspot - SingularityClient - - - - com.hubspot.dropwizard - dropwizard-guicier - - - - io.dropwizard - dropwizard-core - - - - io.dropwizard - dropwizard-lifecycle - - - - io.dropwizard - dropwizard-assets - - - - com.hubspot - HorizonCore - - - - com.hubspot - HorizonNing - - - - io.dropwizard - dropwizard-views - - - - io.dropwizard.metrics - metrics-healthchecks - - - - com.fasterxml.jackson.datatype - jackson-datatype-guava - - - - com.fasterxml.jackson.core - jackson-core - - - - javax.validation - validation-api - - - - com.fasterxml.jackson.core - jackson-databind - - - - com.google.inject - guice - - - - javax.ws.rs - javax.ws.rs-api - - - - com.fasterxml.jackson.core - jackson-annotations - - - - org.hibernate - hibernate-validator - - - - com.hubspot.jackson - jackson-datatype-protobuf - - - - com.google.guava - guava - - - - javax.servlet - javax.servlet-api - - - - org.slf4j - slf4j-api - - - diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/CoordinatorDropwizardHealthcheck.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/CoordinatorDropwizardHealthcheck.java deleted file mode 100644 index f905ef7e61..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/CoordinatorDropwizardHealthcheck.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.hubspot.singularity; - -import com.codahale.metrics.health.HealthCheck; -import com.google.inject.Inject; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; - -public class CoordinatorDropwizardHealthcheck extends HealthCheck { - private final ClusterCoordinatorConfiguration configuration; - - @Inject - public CoordinatorDropwizardHealthcheck(ClusterCoordinatorConfiguration configuration) { - this.configuration = configuration; - } - - @Override - protected Result check() throws Exception { - if (configuration.getDataCenters().isEmpty()) { - return Result.unhealthy("No configured data centers"); - } else { - return Result.healthy("Valid data centers", configuration.getDataCenters()); - } - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinator.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinator.java deleted file mode 100644 index 044c88a441..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinator.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.hubspot.singularity; - -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.datatype.guava.GuavaModule; -import com.hubspot.dropwizard.guicier.GuiceBundle; -import com.hubspot.jackson.datatype.protobuf.ProtobufModule; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; - -import io.dropwizard.Application; -import io.dropwizard.assets.AssetsBundle; -import io.dropwizard.setup.Bootstrap; -import io.dropwizard.setup.Environment; -import io.dropwizard.views.ViewBundle; - -public class SingularityClusterCoordinator extends Application { - @Override - public void initialize(final Bootstrap bootstrap) { - - final GuiceBundle guiceBundle = GuiceBundle.defaultBuilder(ClusterCoordinatorConfiguration.class) - .modules(new SingularityClusterCoordinatorModule()) - .build(); - bootstrap.addBundle(guiceBundle); - - bootstrap.addBundle(new ViewBundle<>()); - bootstrap.addBundle(new AssetsBundle("/assets/static/", "/static/")); - bootstrap.addBundle(new AssetsBundle("/assets/api-docs/", "/api-docs/", "index.html", "api-docs")); - - bootstrap.getObjectMapper().registerModule(new ProtobufModule()); - bootstrap.getObjectMapper().registerModule(new GuavaModule()); - bootstrap.getObjectMapper().setSerializationInclusion(Include.NON_NULL); - bootstrap.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - } - - @Override - public void run(final ClusterCoordinatorConfiguration configuration, final Environment environment) throws Exception {} - - public static void main(final String[] args) throws Exception { - try { - new SingularityClusterCoordinator().run(args); - } catch (final Throwable t) { - t.printStackTrace(); - System.exit(1); - } - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinatorModule.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinatorModule.java deleted file mode 100644 index 563e6eb55e..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/SingularityClusterCoordinatorModule.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.hubspot.singularity; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.inject.Binder; -import com.google.inject.Scopes; -import com.hubspot.dropwizard.guicier.DropwizardAwareModule; -import com.hubspot.mesos.JavaUtils; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; -import com.hubspot.singularity.proxy.SingularityClusterCoodinatorResourcesModule; - -public class SingularityClusterCoordinatorModule extends DropwizardAwareModule { - - @Override - public void configure(Binder binder) { - binder.bind(ObjectMapper.class).toInstance(JavaUtils.newObjectMapper()); - binder.bind(CoordinatorDropwizardHealthcheck.class).in(Scopes.SINGLETON); - binder.install(new SingularityClusterCoodinatorResourcesModule(getConfiguration())); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/ClusterCoordinatorConfiguration.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/ClusterCoordinatorConfiguration.java deleted file mode 100644 index 4bd54e44e7..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/ClusterCoordinatorConfiguration.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.hubspot.singularity.config; - -import java.util.List; - -import javax.validation.Valid; - -import org.hibernate.validator.constraints.NotEmpty; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.Optional; -import com.hubspot.singularity.SingularityClientCredentials; - -import io.dropwizard.Configuration; - -public class ClusterCoordinatorConfiguration extends Configuration { - /* - * List of possible Singularity clusters. The first in the list will be considered the default - */ - @NotEmpty - private List dataCenters; - - @JsonProperty("ui") - @Valid - private UIConfiguration uiConfiguration = new UIConfiguration(); - - private Optional defaultClientCredentials; - - private boolean errorOnDataCenterNotSpecified = false; - - // Settings to inform the ui - private Integer defaultMemory = 64; - private Integer defaultCpus = 1; - private Integer slaveHttpPort = 5051; - private Optional slaveHttpsPort = Optional.absent(); - private int bounceExpirationMinutes = 60; - private long healthcheckIntervalSeconds = 5; - private long healthcheckTimeoutSeconds = 5; - private Optional healthcheckMaxRetries = Optional.absent(); - private int startupTimeoutSeconds = 45; - private boolean loadBalancingEnabled = false; - private Optional commonHostnameSuffixToOmit = Optional.absent(); - private Integer warnIfScheduledJobIsRunningPastNextRunPct = 200; - - - - public List getDataCenters() { - return dataCenters; - } - - public void setDataCenters(List dataCenters) { - this.dataCenters = dataCenters; - } - - public UIConfiguration getUiConfiguration() { - return uiConfiguration; - } - - public void setUiConfiguration(UIConfiguration uiConfiguration) { - this.uiConfiguration = uiConfiguration; - } - - public Integer getDefaultMemory() { - return defaultMemory; - } - - public void setDefaultMemory(Integer defaultMemory) { - this.defaultMemory = defaultMemory; - } - - public Integer getDefaultCpus() { - return defaultCpus; - } - - public void setDefaultCpus(Integer defaultCpus) { - this.defaultCpus = defaultCpus; - } - - public Integer getSlaveHttpPort() { - return slaveHttpPort; - } - - public void setSlaveHttpPort(Integer slaveHttpPort) { - this.slaveHttpPort = slaveHttpPort; - } - - public Optional getSlaveHttpsPort() { - return slaveHttpsPort; - } - - public void setSlaveHttpsPort(Optional slaveHttpsPort) { - this.slaveHttpsPort = slaveHttpsPort; - } - - public int getBounceExpirationMinutes() { - return bounceExpirationMinutes; - } - - public void setBounceExpirationMinutes(int bounceExpirationMinutes) { - this.bounceExpirationMinutes = bounceExpirationMinutes; - } - - public long getHealthcheckIntervalSeconds() { - return healthcheckIntervalSeconds; - } - - public void setHealthcheckIntervalSeconds(long healthcheckIntervalSeconds) { - this.healthcheckIntervalSeconds = healthcheckIntervalSeconds; - } - - public long getHealthcheckTimeoutSeconds() { - return healthcheckTimeoutSeconds; - } - - public void setHealthcheckTimeoutSeconds(long healthcheckTimeoutSeconds) { - this.healthcheckTimeoutSeconds = healthcheckTimeoutSeconds; - } - - public Optional getHealthcheckMaxRetries() { - return healthcheckMaxRetries; - } - - public void setHealthcheckMaxRetries(Optional healthcheckMaxRetries) { - this.healthcheckMaxRetries = healthcheckMaxRetries; - } - - public int getStartupTimeoutSeconds() { - return startupTimeoutSeconds; - } - - public void setStartupTimeoutSeconds(int startupTimeoutSeconds) { - this.startupTimeoutSeconds = startupTimeoutSeconds; - } - - public boolean isLoadBalancingEnabled() { - return loadBalancingEnabled; - } - - public void setLoadBalancingEnabled(boolean loadBalancingEnabled) { - this.loadBalancingEnabled = loadBalancingEnabled; - } - - public Optional getCommonHostnameSuffixToOmit() { - return commonHostnameSuffixToOmit; - } - - public void setCommonHostnameSuffixToOmit(Optional commonHostnameSuffixToOmit) { - this.commonHostnameSuffixToOmit = commonHostnameSuffixToOmit; - } - - public Integer getWarnIfScheduledJobIsRunningPastNextRunPct() { - return warnIfScheduledJobIsRunningPastNextRunPct; - } - - public void setWarnIfScheduledJobIsRunningPastNextRunPct(Integer warnIfScheduledJobIsRunningPastNextRunPct) { - this.warnIfScheduledJobIsRunningPastNextRunPct = warnIfScheduledJobIsRunningPastNextRunPct; - } - - public Optional getDefaultClientCredentials() { - return defaultClientCredentials; - } - - public void setDefaultClientCredentials(Optional defaultClientCredentials) { - this.defaultClientCredentials = defaultClientCredentials; - } - - public boolean isErrorOnDataCenterNotSpecified() { - return errorOnDataCenterNotSpecified; - } - - public ClusterCoordinatorConfiguration setErrorOnDataCenterNotSpecified(boolean errorOnDataCenterNotSpecified) { - this.errorOnDataCenterNotSpecified = errorOnDataCenterNotSpecified; - return this; - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/DataCenter.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/DataCenter.java deleted file mode 100644 index 75bd160474..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/config/DataCenter.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.hubspot.singularity.config; - -import java.util.List; - -import javax.validation.constraints.NotNull; - -import org.hibernate.validator.constraints.NotEmpty; - -import com.google.common.base.Optional; -import com.hubspot.singularity.SingularityClientCredentials; - -public class DataCenter { - @NotNull - private String name; - @NotEmpty - private List hosts; - @NotNull - private String contextPath; - // http or https - private String scheme = "http"; - - private Optional clientCredentials = Optional.absent(); - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List getHosts() { - return hosts; - } - - public void setHosts(List hosts) { - this.hosts = hosts; - } - - public String getContextPath() { - return contextPath; - } - - public void setContextPath(String contextPath) { - this.contextPath = contextPath; - } - - public String getScheme() { - return scheme; - } - - public void setScheme(String scheme) { - this.scheme = scheme; - } - - public Optional getClientCredentials() { - return clientCredentials; - } - - public void setClientCredentials(Optional clientCredentials) { - this.clientCredentials = clientCredentials; - } - - @Override - public String toString() { - return "DataCenter{" + - "name='" + name + '\'' + - ", hosts=" + hosts + - ", contextPath='" + contextPath + '\'' + - ", scheme='" + scheme + '\'' + - ", clientCredentials=" + clientCredentials + - '}'; - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterConflictException.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterConflictException.java deleted file mode 100644 index 9c08fd4f75..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterConflictException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.hubspot.singularity.exceptions; - -import javax.ws.rs.WebApplicationException; - -public class DataCenterConflictException extends WebApplicationException { - public DataCenterConflictException(String message) { - super(message, 409); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterNotFoundException.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterNotFoundException.java deleted file mode 100644 index e003980199..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/DataCenterNotFoundException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.hubspot.singularity.exceptions; - -import javax.ws.rs.WebApplicationException; - -public class DataCenterNotFoundException extends WebApplicationException { - public DataCenterNotFoundException(String message, int status) { - super(message, status); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/NotImplemenedException.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/NotImplemenedException.java deleted file mode 100644 index c8a71b6260..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/exceptions/NotImplemenedException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.hubspot.singularity.exceptions; - -import javax.ws.rs.WebApplicationException; - -public class NotImplemenedException extends WebApplicationException { - public NotImplemenedException() { - super("Not Implemented", 500); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/AuthResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/AuthResource.java deleted file mode 100644 index 075fe7a15b..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/AuthResource.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityAuthorizationScope; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.AUTH_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class AuthResource extends ProxyResource { - - @Inject - public AuthResource() {} - - @GET - @Path("/user") - public Response getUser(@Context HttpServletRequest request) { - return routeToDefaultDataCenter(request); - } - - @GET - @Path("/{requestId}/auth-check/{userId}") - public Response checkReadOnlyAuth(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("userId") String userId, @QueryParam("scope") Optional scope) { - return routeToDefaultDataCenter(request); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DataCenterLocator.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DataCenterLocator.java deleted file mode 100644 index d9700ef5ef..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DataCenterLocator.java +++ /dev/null @@ -1,206 +0,0 @@ -package com.hubspot.singularity.proxy; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableMap; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityRequestGroup; -import com.hubspot.singularity.SingularityRequestParent; -import com.hubspot.singularity.SingularitySlave; -import com.hubspot.singularity.client.SingularityClient; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; -import com.hubspot.singularity.config.DataCenter; -import com.hubspot.singularity.exceptions.DataCenterNotFoundException; - -import io.dropwizard.lifecycle.Managed; - -public class DataCenterLocator implements Managed { - private static final Logger LOG = LoggerFactory.getLogger(DataCenterLocator.class); - - private final ClusterCoordinatorConfiguration configuration; - private final Map dataCenters; - private final Map clients; - - private final Random random = new Random(); - - private final Map> requestIdsByDataCenter = new ConcurrentHashMap<>(); - private final Map> requestGroupsByDataCenter = new ConcurrentHashMap<>(); - private final Map> slaveIdsByDataCenter = new ConcurrentHashMap<>(); - private final Map> hostnamesByDataCenter = new ConcurrentHashMap<>(); - private final Map> rackIdsByDataCenter = new ConcurrentHashMap<>(); - - - @Inject - public DataCenterLocator(ClusterCoordinatorConfiguration configuration, Map clients) { - this.configuration = configuration; - this.clients = clients; - - ImmutableMap.Builder builder = ImmutableMap.builder(); - configuration.getDataCenters().forEach((dc) -> builder.put(dc.getName(), dc)); - this.dataCenters = builder.build(); - } - - String getHost(DataCenter dataCenter) { - return dataCenter.getHosts().get(random.nextInt(dataCenter.getHosts().size())); - } - - DataCenter getDataCenterForRequest(String requestId, boolean isGetRequest) { - for (Map.Entry entry : dataCenters.entrySet()) { - if (requestIdsByDataCenter.get(entry.getKey()).contains(requestId)) { - return entry.getValue(); - } - } - - // Not found, try each one - for (Map.Entry entry : dataCenters.entrySet()) { - SingularityClient client = clients.get(entry.getKey()); - Optional maybeRequest = client.getSingularityRequest(requestId); - if (maybeRequest.isPresent()) { - requestIdsByDataCenter.get(entry.getKey()).add(maybeRequest.get().getRequest().getId()); - return entry.getValue(); - } - } - throw new DataCenterNotFoundException(String.format("Could not find requestId '%s' in any data center", requestId), isGetRequest ? 404 :500); - } - - Optional getMaybeDataCenterForRequest(String requestId, boolean isGetRequest) { - try { - return Optional.of(getDataCenterForRequest(requestId, isGetRequest)); - } catch (DataCenterNotFoundException nfe) { - return Optional.absent(); - } - } - - DataCenter getDataCenterForRequestGroup(String requestGroupId, boolean isGetRequest) { - for (Map.Entry entry : dataCenters.entrySet()) { - if (requestGroupsByDataCenter.get(entry.getKey()).contains(requestGroupId)) { - return entry.getValue(); - } - } - - // Not found, try each one - for (Map.Entry entry : dataCenters.entrySet()) { - SingularityClient client = clients.get(entry.getKey()); - Optional maybeRequestGroup = client.getRequestGroup(requestGroupId); - if (maybeRequestGroup.isPresent()) { - requestGroupsByDataCenter.get(entry.getKey()).add(maybeRequestGroup.get().getId()); - return entry.getValue(); - } - } - throw new DataCenterNotFoundException(String.format("Could not find requestGroupId '%s' in any data center", requestGroupId), isGetRequest ? 404 :500); - } - - DataCenter getDataCenterForSlaveId(String slaveId, boolean isGetRequest) { - for (Map.Entry entry : dataCenters.entrySet()) { - if (slaveIdsByDataCenter.get(entry.getKey()).contains(slaveId)) { - return entry.getValue(); - } - } - - // Not found, try each one - for (Map.Entry entry : dataCenters.entrySet()) { - SingularityClient client = clients.get(entry.getKey()); - Optional maybeSlave = client.getSlave(slaveId); - if (maybeSlave.isPresent()) { - slaveIdsByDataCenter.get(entry.getKey()).add(maybeSlave.get().getId()); - hostnamesByDataCenter.get(entry.getKey()).add(maybeSlave.get().getHost()); - rackIdsByDataCenter.get(entry.getKey()).add(maybeSlave.get().getRackId()); - return entry.getValue(); - } - } - throw new DataCenterNotFoundException(String.format("Could not find slaveId '%s' in any data center", slaveId), isGetRequest ? 404 :500); - } - - DataCenter getDataCenterForSlaveHostname(String hostname, boolean isGetRequest) { - for (Map.Entry entry : dataCenters.entrySet()) { - if (hostnamesByDataCenter.get(entry.getKey()).contains(hostname)) { - return entry.getValue(); - } - } - - // Not found, try each one - for (Map.Entry entry : dataCenters.entrySet()) { - SingularityClient client = clients.get(entry.getKey()); - Collection slaves = client.getSlaves(Optional.absent()); - for (SingularitySlave slave : slaves) { - if (slave.getHost().equals(hostname)) { - slaveIdsByDataCenter.get(entry.getKey()).add(slave.getId()); - hostnamesByDataCenter.get(entry.getKey()).add(slave.getHost()); - rackIdsByDataCenter.get(entry.getKey()).add(slave.getRackId()); - return entry.getValue(); - } - } - } - throw new DataCenterNotFoundException(String.format("Could not find slave with hostname '%s' in any data center", hostname), isGetRequest ? 404 :500); - } - - DataCenter getDataCenterForRackId(String rackId, boolean isGetRequest) { - for (Map.Entry entry : dataCenters.entrySet()) { - if (rackIdsByDataCenter.get(entry.getKey()).contains(rackId)) { - return entry.getValue(); - } - } - - // Not found, try each one - for (Map.Entry entry : dataCenters.entrySet()) { - SingularityClient client = clients.get(entry.getKey()); - Collection slaves = client.getSlaves(Optional.absent()); - for (SingularitySlave slave : slaves) { - if (slave.getRackId().equals(rackId)) { - slaveIdsByDataCenter.get(entry.getKey()).add(slave.getId()); - hostnamesByDataCenter.get(entry.getKey()).add(slave.getHost()); - rackIdsByDataCenter.get(entry.getKey()).add(slave.getRackId()); - return entry.getValue(); - } - } - } - throw new DataCenterNotFoundException(String.format("Could not find rack with id '%s' in any data center", rackId), isGetRequest ? 404 :500); - } - - DataCenter getDataCenter(String name) { - if (dataCenters.containsKey(name)) { - return dataCenters.get(name); - } else { - throw new DataCenterNotFoundException(String.format("No known data center with name: %s", name), 404); - } - } - - @Override - public void start() { - loadData(); - } - - private void loadData() { - configuration.getDataCenters().forEach((dc) -> { - SingularityClient singularityClient = clients.get(dc.getName()); - - Collection requestParents = singularityClient.getSingularityRequests(); - requestIdsByDataCenter.put(dc.getName(), requestParents.stream().map((r) -> r.getRequest().getId()).collect(Collectors.toSet())); - LOG.info("Loaded {} requests for data center {}", requestParents.size(), dc.getName()); - - Collection slaves = singularityClient.getSlaves(Optional.absent()); - Set rackIds = slaves.stream().map(SingularitySlave::getRackId).collect(Collectors.toSet()); - rackIdsByDataCenter.put(dc.getName(), new HashSet<>(rackIds)); - slaveIdsByDataCenter.put(dc.getName(), slaves.stream().map(SingularitySlave::getId).collect(Collectors.toSet())); - hostnamesByDataCenter.put(dc.getName(), slaves.stream().map(SingularitySlave::getHost).collect(Collectors.toSet())); - LOG.info("Loaded {} slaves for data center {}", slaves.size(), dc.getName()); - - Collection requestGroups = singularityClient.getRequestGroups(); - requestGroupsByDataCenter.put(dc.getName(), requestGroups.stream().map(SingularityRequestGroup::getId).collect(Collectors.toSet())); - LOG.info("Loaded {} request groups for data center {}", requestGroups.size(), dc.getName()); - }); - } - - @Override - public void stop() {} -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DeployResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DeployResource.java deleted file mode 100644 index 848ee9e376..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DeployResource.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityUpdatePendingDeployRequest; -import com.hubspot.singularity.api.SingularityDeployRequest; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.DEPLOY_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class DeployResource extends ProxyResource { - - @Inject - public DeployResource() {} - - @GET - @Path("/pending") - public Response getPendingDeploys(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @POST - @Consumes({ MediaType.APPLICATION_JSON }) - public Response deploy(@Context HttpServletRequest request, SingularityDeployRequest deployRequest) { - return routeByRequestId(request, deployRequest.getDeploy().getRequestId(), deployRequest); - } - - @DELETE - @Path("/deploy/{deployId}/request/{requestId}") - public Response cancelDeploy(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) { - return routeByRequestId(request, requestId); - } - - @POST - @Path("/update") - public Response updatePendingDeploy(@Context HttpServletRequest request, SingularityUpdatePendingDeployRequest updateRequest) { - return routeByRequestId(request, updateRequest.getRequestId(), updateRequest); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DisastersResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DisastersResource.java deleted file mode 100644 index 49e8e3f836..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/DisastersResource.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.hubspot.singularity.proxy; - -import java.util.List; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityAction; -import com.hubspot.singularity.SingularityDisabledAction; -import com.hubspot.singularity.SingularityDisasterType; -import com.hubspot.singularity.SingularityDisastersData; -import com.hubspot.singularity.SingularityTaskCredits; -import com.hubspot.singularity.api.SingularityDisabledActionRequest; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.exceptions.NotImplemenedException; - -@Path(ApiPaths.DISASTERS_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class DisastersResource extends ProxyResource { - - @Inject - public DisastersResource() {} - - @GET - @Path("/stats") - public SingularityDisastersData disasterStats(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @GET - @Path("/active") - public List activeDisasters(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @POST - @Path("/disable") - public void disableAutomatedDisasterCreation(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @POST - @Path("/enable") - public void enableAutomatedDisasterCreation(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @DELETE - @Path("/active/{type}") - public void removeDisaster(@Context HttpServletRequest request, @PathParam("type") SingularityDisasterType type) { - throw new NotImplemenedException(); - } - - @POST - @Path("/active/{type}") - public void newDisaster(@Context HttpServletRequest request, @PathParam("type") SingularityDisasterType type) { - throw new NotImplemenedException(); - } - - @GET - @Path("/disabled-actions") - public List disabledActions(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @POST - @Path("/disabled-actions/{action}") - public void disableAction(@Context HttpServletRequest request, @PathParam("action") SingularityAction action, SingularityDisabledActionRequest disabledActionRequest) { - throw new NotImplemenedException(); - } - - @DELETE - @Path("/disabled-actions/{action}") - public void enableAction(@Context HttpServletRequest request, @PathParam("action") SingularityAction action) { - throw new NotImplemenedException(); - } - - @POST - @Path("/task-credits") - public void addTaskCredits(@Context HttpServletRequest request, @QueryParam("credits") Optional credits) throws Exception { - throw new NotImplemenedException(); - } - - @DELETE - @Path("/task-credits") - public void disableTaskCredits(@Context HttpServletRequest request) throws Exception { - throw new NotImplemenedException(); - } - - @GET - @Path("/task-credits") - public SingularityTaskCredits getTaskCreditData(@Context HttpServletRequest request) throws Exception { - throw new NotImplemenedException(); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/HistoryResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/HistoryResource.java deleted file mode 100644 index 6e4897020a..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/HistoryResource.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityTaskId; -import com.hubspot.singularity.config.ApiPaths; - -// Omits some or all @QueryParam annotated args, will be copied from request context -@Path(ApiPaths.HISTORY_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class HistoryResource extends ProxyResource { - - @Inject - public HistoryResource() {} - - @GET - @Path("/task/{taskId}") - public Response getHistoryForTask(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - - @GET - @Path("/request/{requestId}/tasks/active") - public Response getTaskHistoryForRequest(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploy/{deployId}") - public Response getDeploy(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploy/{deployId}/tasks/active") - public Response getActiveDeployTasks(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploy/{deployId}/tasks/inactive") - public Response getInactiveDeployTasks(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploy/{deployId}/tasks/inactive/withmetadata") - public Response getInactiveDeployTasksWithMetadata( - @Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/tasks") - public Response getTaskHistory( - @Context HttpServletRequest request, @QueryParam("requestId") Optional requestId, @QueryParam("deployId") Optional deployId, - @QueryParam("runId") Optional runId, @QueryParam("host") Optional host) { - // TODO - what if requestId not present? - return routeByRequestId(request, requestId.or("")); - } - - @GET - @Path("/tasks/withmetadata") - public Response getTaskHistoryWithMetadata( - @Context HttpServletRequest request, @QueryParam("requestId") Optional requestId, @QueryParam("deployId") Optional deployId, - @QueryParam("runId") Optional runId, @QueryParam("host") Optional host) { - // TODO - what if requestId not present? - return routeByRequestId(request, requestId.or("")); - } - - @GET - @Path("/request/{requestId}/tasks") - public Response getTaskHistoryForRequest( - @Context HttpServletRequest request, @PathParam("requestId") String requestId, @QueryParam("deployId") Optional deployId, - @QueryParam("runId") Optional runId, @QueryParam("host") Optional host) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/tasks/withmetadata") - public Response getTaskHistoryForRequestWithMetadata( - @Context HttpServletRequest request, @PathParam("requestId") String requestId, @QueryParam("deployId") Optional deployId, - @QueryParam("runId") Optional runId, @QueryParam("host") Optional host) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/run/{runId}") - public Response getTaskHistoryForRequestAndRunId( - @Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("runId") String runId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploys") - public Response getDeploys( - @Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploys/withmetadata") - public Response getDeploysWithMetadata( - @Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/requests") - public Response getRequestHistoryForRequest( - @Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/requests/withmetadata") - public Response getRequestHistoryForRequestWithMetadata( - @Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/requests/search") - public Response getRequestHistoryForRequestLike(@Context HttpServletRequest request, @QueryParam("requestIdLike") String requestIdLike) { - return getMergedListResult(request); - } - - @GET - @Path("/request/{requestId}/command-line-args") - public Response getRecentCommandLineArgs(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return getMergedListResult(request); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/InactiveSlaveResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/InactiveSlaveResource.java deleted file mode 100644 index f98a121f8d..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/InactiveSlaveResource.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.INACTIVE_SLAVES_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class InactiveSlaveResource extends ProxyResource { - - @Inject - public InactiveSlaveResource() {} - - @GET - public Response getInactiveSlaves(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @POST - public Response deactivateSlave(@Context HttpServletRequest request, @QueryParam("host") String host) { - return routeByHostname(request, host); - } - - @DELETE - public Response reactivateSlave(@Context HttpServletRequest request, @QueryParam("host") String host) { - return routeByHostname(request, host); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/PriorityResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/PriorityResource.java deleted file mode 100644 index 74d5b97917..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/PriorityResource.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityPriorityFreezeParent; -import com.hubspot.singularity.api.SingularityPriorityFreeze; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.exceptions.NotImplemenedException; - -@Path(ApiPaths.PRIORITY_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class PriorityResource extends ProxyResource { - - @Inject - public PriorityResource() {} - - @GET - @Path("/freeze") - public Optional getActivePriorityFreeze(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @DELETE - @Path("/freeze") - public void deleteActivePriorityFreeze(@Context HttpServletRequest request) { - throw new NotImplemenedException(); - } - - @POST - @Path("/freeze") - public SingularityPriorityFreezeParent createPriorityFreeze(@Context HttpServletRequest request, SingularityPriorityFreeze priorityFreezeRequest) { - throw new NotImplemenedException(); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/ProxyResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/ProxyResource.java deleted file mode 100644 index 7b6db2c99f..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/ProxyResource.java +++ /dev/null @@ -1,323 +0,0 @@ -package com.hubspot.singularity.proxy; - -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Throwables; -import com.google.common.collect.Lists; -import com.google.inject.Inject; -import com.google.inject.name.Named; -import com.hubspot.horizon.AsyncHttpClient; -import com.hubspot.horizon.Header; -import com.hubspot.horizon.HttpRequest; -import com.hubspot.horizon.HttpRequest.Method; -import com.hubspot.horizon.HttpResponse; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; -import com.hubspot.singularity.config.DataCenter; - -import io.dropwizard.server.SimpleServerFactory; - -public class ProxyResource { - private static final Logger LOG = LoggerFactory.getLogger(ProxyResource.class); - - private AsyncHttpClient httpClient; - protected ClusterCoordinatorConfiguration configuration; - private ObjectMapper objectMapper; - protected DataCenterLocator dataCenterLocator; - private String contextPath; - - public ProxyResource() {} - - @Inject - void injectProxyDeps(ClusterCoordinatorConfiguration configuration, - @Named(SingularityClusterCoodinatorResourcesModule.ASYNC_HTTP_CLIENT) AsyncHttpClient httpClient, - ObjectMapper objectMapper, - DataCenterLocator dataCenterLocator) { - this.configuration = configuration; - this.httpClient = httpClient; - this.objectMapper = objectMapper; - this.dataCenterLocator = dataCenterLocator; - String baseContextPath = ((SimpleServerFactory) configuration.getServerFactory()).getApplicationContextPath(); - if (baseContextPath.startsWith("/")) { - baseContextPath = baseContextPath.substring(1, baseContextPath.length()); - } - if (baseContextPath.endsWith("/")) { - baseContextPath = baseContextPath.substring(0, baseContextPath.length() -1); - } - this.contextPath = baseContextPath + ApiPaths.API_BASE_PATH; - } - - /* - * For items where the dataCenter is part of the object, or where a full list is desired. - * Collect and merge results from each configured dataCenter - */ - public Response getMergedListResult(HttpServletRequest request) { - return getMergedListResult(request, null); - } - - public Response getMergedListResult(HttpServletRequest request, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - // TODO - parallelize - List combined = Lists.newArrayList(); - ResponseBuilder builder = null; - - for (DataCenter dataCenter : configuration.getDataCenters()) { - try { - HttpResponse response = proxyAndGetResponse(dataCenter, request, body, headers, params); - if (builder == null) { - builder = Response.status(response.getStatusCode()); - for (Header header : response.getHeaders()) { - builder.header(header.getName(), header.getValue()); - } - } - - List content = response.getAs(new TypeReference>() {}); - LOG.trace("Data center {} had response {}", dataCenter.getName(), response.getStatusCode()); - - combined.addAll(content); - } catch (RuntimeException re) { - LOG.error("Could not get data from dataCenter {}, omitting", dataCenter.getName(), re); - } - } - - if (builder != null) { - return builder.entity(combined).build(); - } else { - throw new WebApplicationException("Got no results", 500); - } - } - - /* - * Route a request to a particular dataCenter using the requestId to locate the correct Singularity cluster - */ - public Response routeByRequestId(HttpServletRequest request, String requestId) { - return routeByRequestId(request, requestId, null); - } - - public Response routeByRequestId(HttpServletRequest request, String requestId, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenterForRequest(requestId, request.getMethod().toUpperCase().equals("GET")); - - return toResponse(proxyAndGetResponse(dataCenter, request, body, headers, params)); - } - - /* - * Route a request to a particular dataCenter using the request group Id to locate the correct Singularity cluster - */ - Response routeByRequestGroupId(HttpServletRequest request, String requestGroupId) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenterForRequestGroup(requestGroupId, request.getMethod().toUpperCase().equals("GET")); - - return toResponse(proxyAndGetResponse(dataCenter, request, null, headers, params)); - } - - /* - * Route a request to a particular dataCenter using the slaveId/hostname to locate the correct Singularity cluster - */ - Response routeBySlaveId(HttpServletRequest request, String slaveId) { - return routeBySlaveId(request, slaveId, null); - } - - Response routeBySlaveId(HttpServletRequest request, String slaveId, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenterForSlaveId(slaveId, request.getMethod().toUpperCase().equals("GET")); - - return toResponse(proxyAndGetResponse(dataCenter, request, body, headers, params)); - } - - Response routeByHostname(HttpServletRequest request, String hostname) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenterForSlaveHostname(hostname, request.getMethod().toUpperCase().equals("GET")); - - return toResponse(proxyAndGetResponse(dataCenter, request, null, headers, params)); - } - - /* - * Route a request to a particular dataCenter using the rack ID to locate the correct Singularity cluster - */ - Response routeByRackId(HttpServletRequest request, String rackId) { - return routeByRackId(request, rackId, null); - } - - Response routeByRackId(HttpServletRequest request, String rackId, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenterForRackId(rackId, request.getMethod().toUpperCase().equals("GET")); - - return toResponse(proxyAndGetResponse(dataCenter, request, body, headers, params)); - } - - /* - * Route a request to a particular dataCenter by name, failing if it is not present - */ - Response routeByDataCenter(HttpServletRequest request, String dataCenterName, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = getDataCenter(dataCenterName); - - return toResponse(proxyAndGetResponse(dataCenter, request, body, headers, params)); - } - - /* - * Route to the default Singularity cluster - */ - Response routeToDefaultDataCenter(HttpServletRequest request) { - return routeToDefaultDataCenter(request, null); - } - - Response routeToDefaultDataCenter(HttpServletRequest request, T body) { - List headers = getHeaders(request); - List params = getParams(request); - - DataCenter dataCenter = configuration.getDataCenters().get(0); - - return toResponse(proxyAndGetResponse(dataCenter, request, body, headers, params)); - } - - /* - * Working with data centers - */ - private String getHost(DataCenter dataCenter) { - return dataCenterLocator.getHost(dataCenter); - } - - private DataCenter getDataCenterForRequest(String requestId, boolean isGetRequest) { - return dataCenterLocator.getDataCenterForRequest(requestId, isGetRequest); - } - - private DataCenter getDataCenterForRequestGroup(String requestGroupId, boolean isGetRequest) { - return dataCenterLocator.getDataCenterForRequestGroup(requestGroupId, isGetRequest); - } - - private DataCenter getDataCenterForSlaveId(String slaveId, boolean isGetRequest) { - return dataCenterLocator.getDataCenterForSlaveId(slaveId, isGetRequest); - } - - private DataCenter getDataCenterForSlaveHostname(String hostname, boolean isGetRequest) { - return dataCenterLocator.getDataCenterForSlaveHostname(hostname, isGetRequest); - } - - private DataCenter getDataCenterForRackId(String rackId, boolean isGetRequest) { - return dataCenterLocator.getDataCenterForRackId(rackId, isGetRequest); - } - - private DataCenter getDataCenter(String name) { - return dataCenterLocator.getDataCenter(name); - } - - /* - * Generic methods for proxying requests - */ - private HttpResponse proxyAndGetResponse(DataCenter dc, HttpServletRequest request, T body, List headers, List params) { - String fullPath = request.getContextPath() + request.getPathInfo(); - String url = String.format("%s://%s%s", dc.getScheme(), getHost(dc), fullPath.replace(contextPath, dc.getContextPath())); - - LOG.debug("Proxying {} {} to: ({}) {}", request.getMethod(), fullPath, dc.getName(), url); - HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() - .setMethod(Method.valueOf(request.getMethod())) - .setUrl(url); - if (body != null) { - requestBuilder.setBody(body); - LOG.trace("Added body {} to request", body); - } else if (!request.getMethod().equals("GET")){ - requestBuilder.setBody(new byte[]{}); - } - headers.forEach((h) -> requestBuilder.addHeader(h.getKey(), h.getValue())); - params.forEach((h) -> requestBuilder.setQueryParam(h.getKey()).to(h.getValue())); - - try { - return httpClient.execute(requestBuilder.build()).get(); - } catch (InterruptedException|ExecutionException ioe) { - if (Throwables.getRootCause(ioe) instanceof TimeoutException) { - throw new WebApplicationException(ioe, 503); - } else { - throw new WebApplicationException(ioe); - } - } - } - - private List getHeaders(HttpServletRequest request) { - List headers = new ArrayList<>(); - Enumeration headerNames = request.getHeaderNames(); - if (headerNames != null) { - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - headers.add(new Param(headerName, request.getHeader(headerName))); - } - } - LOG.trace("Found headers: {}", headers); - return headers; - } - - private List getParams(HttpServletRequest request) { - List params = new ArrayList<>(); - for (Map.Entry entry : request.getParameterMap().entrySet()) { - for (String value : entry.getValue()) { - params.add(new Param(entry.getKey(), value)); - } - } - LOG.trace("Found params {}", params); - return params; - } - - private Response toResponse(HttpResponse original) { - LOG.trace("Got response {}", original.getStatusCode()); - ResponseBuilder builder = Response.status(original.getStatusCode()) - .entity(original.getAsString()); - original.getHeaders().forEach((h) -> builder.header(h.getName(), h.getValue())); - return builder.build(); - } - - private static class Param { - private final String key; - private final String value; - - Param(String key, String value) { - this.key = key; - this.value = value; - } - - String getKey() { - return key; - } - - String getValue() { - return value; - } - - @Override - public String toString() { - return "Param{" + - "key='" + key + '\'' + - ", value='" + value + '\'' + - '}'; - } - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RackResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RackResource.java deleted file mode 100644 index 69a291317f..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RackResource.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.api.SingularityMachineChangeRequest; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.RACK_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class RackResource extends ProxyResource { - - @Inject - public RackResource() {} - - @GET - @Path("/") - public Response getRacks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/rack/{rackId}") - public Response getRackHistory(@Context HttpServletRequest request, @PathParam("rackId") String rackId) { - return routeByRackId(request, rackId); - } - - @DELETE - @Path("/rack/{rackId}") - public Response removeRack(@Context HttpServletRequest request, @PathParam("rackId") String rackId) { - // TODO - remove from internal list as well? - return routeByRackId(request, rackId); - } - - @POST - @Path("/rack/{rackId}/decommission") - public Response decommissionRack(@Context HttpServletRequest request, @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { - return routeByRackId(request, rackId); - } - - @POST - @Path("/rack/{rackId}/freeze") - public Response freezeRack(@Context HttpServletRequest request, @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { - return routeByRackId(request, rackId, changeRequest); - } - - @POST - @Path("/rack/{rackId}/activate") - public Response activateRack(@Context HttpServletRequest request, @PathParam("rackId") String rackId, SingularityMachineChangeRequest changeRequest) { - return routeByRackId(request, rackId, changeRequest); - } - - @DELETE - @Path("/rack/{rackId}/expiring") - public Response deleteExpiringStateChange(@Context HttpServletRequest request, @PathParam("rackId") String rackId) { - return routeByRackId(request, rackId); - } - - @GET - @Path("/expiring") - public Response getExpiringStateChanges(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestGroupResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestGroupResource.java deleted file mode 100644 index 23194a58da..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestGroupResource.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityRequestGroup; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.REQUEST_GROUP_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class RequestGroupResource extends ProxyResource { - - @Inject - public RequestGroupResource() {} - - @GET - public Response getRequestGroupIds(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/group/{requestGroupId}") - public Response getRequestGroup(@Context HttpServletRequest request, @PathParam("requestGroupId") String requestGroupId) { - return routeByRequestGroupId(request, requestGroupId); - } - - @DELETE - @Path("/group/{requestGroupId}") - public Response deleteRequestGroup(@Context HttpServletRequest request, @PathParam("requestGroupId") String requestGroupId) { - return routeByRequestGroupId(request, requestGroupId); - } - - @POST - public Response saveRequestGroup(@Context HttpServletRequest request, SingularityRequestGroup requestGroup) { - // TODO - route by more than first request id? - // TODO - add to internal list of groups? - // TODO - error if list is empty? - return routeByRequestId(request, requestGroup.getRequestIds().get(0), requestGroup); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestResource.java deleted file mode 100644 index 79a663791d..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/RequestResource.java +++ /dev/null @@ -1,272 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Optional; -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityRequest; -import com.hubspot.singularity.api.SingularityBounceRequest; -import com.hubspot.singularity.api.SingularityDeleteRequestRequest; -import com.hubspot.singularity.api.SingularityExitCooldownRequest; -import com.hubspot.singularity.api.SingularityPauseRequest; -import com.hubspot.singularity.api.SingularityRunNowRequest; -import com.hubspot.singularity.api.SingularityScaleRequest; -import com.hubspot.singularity.api.SingularitySkipHealthchecksRequest; -import com.hubspot.singularity.api.SingularityUnpauseRequest; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.config.DataCenter; -import com.hubspot.singularity.exceptions.DataCenterConflictException; -import com.hubspot.singularity.exceptions.DataCenterNotFoundException; - -@Path(ApiPaths.REQUEST_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class RequestResource extends ProxyResource { - private static final Logger LOG = LoggerFactory.getLogger(RequestResource.class); - - @Inject - public RequestResource() {} - - @POST - @Consumes({ MediaType.APPLICATION_JSON }) - public Response postRequest(@Context HttpServletRequest requestContext, SingularityRequest request) { - Optional maybeDataCenter = dataCenterLocator.getMaybeDataCenterForRequest(request.getId(), false); - if (maybeDataCenter.isPresent()) { - LOG.trace("Found data center {} for request {}", maybeDataCenter.get().getName(), request.getId()); - if (request.getDataCenter().isPresent() && !request.getDataCenter().get().equals(maybeDataCenter.get().getName())) { - throw new DataCenterConflictException(String.format("Cannot create request with id %s in multiple datacenters (requested: %s), already found in %s", request.getId(), request.getDataCenter().get(), maybeDataCenter.get().getName())); - } - return routeByDataCenter(requestContext, maybeDataCenter.get().getName(), request); - } - - LOG.trace("Check request {}", request); - if (request.getDataCenter().isPresent()) { - return routeByDataCenter(requestContext, request.getDataCenter().get(), request); - } - if (configuration.isErrorOnDataCenterNotSpecified()) { - throw new DataCenterNotFoundException(String.format("No data center specified in request %s, and no existing request found in any data center", request.getId()), 500); - } else { - return routeToDefaultDataCenter(requestContext, request); - } - } - - @POST - @Path("/request/{requestId}/bounce") - public Response bounce(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext) { - return bounce(requestId, requestContext, null); - } - - @POST - @Path("/request/{requestId}/bounce") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response bounce(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityBounceRequest bounceRequest) { - return routeByRequestId(requestContext, requestId, bounceRequest); - } - - @POST - @Path("/request/{requestId}/run") - public Response scheduleImmediately(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return scheduleImmediately(request, requestId, null); - } - - @POST - @Path("/request/{requestId}/run") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response scheduleImmediately(@Context HttpServletRequest requestContext, @PathParam("requestId") String requestId, - SingularityRunNowRequest runNowRequest) { - return routeByRequestId(requestContext, requestId, runNowRequest); - } - - @GET - @Path("/request/{requestId}/run/{runId}") - public Response getTaskByRunId(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("runId") String runId) { - return routeByRequestId(request, requestId); - } - - @POST - @Path("/request/{requestId}/pause") - public Response pause(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext) { - return pause(requestId, requestContext, null); - } - - @POST - @Path("/request/{requestId}/pause") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response pause(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityPauseRequest pauseRequest) { - return routeByRequestId(requestContext, requestId, pauseRequest); - } - - @POST - @Path("/request/{requestId}/unpause") - public Response unpauseNoBody(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext) { - return unpause(requestId, requestContext, null); - } - - @POST - @Path("/request/{requestId}/unpause") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response unpause(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityUnpauseRequest unpauseRequest) { - return routeByRequestId(requestContext, requestId, unpauseRequest); - } - - @POST - @Path("/request/{requestId}/exit-cooldown") - public Response exitCooldown(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext) { - return exitCooldown(requestId, requestContext, null); - } - - @POST - @Path("/request/{requestId}/exit-cooldown") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response exitCooldown(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityExitCooldownRequest exitCooldownRequest) { - return routeByRequestId(requestContext, requestId, exitCooldownRequest); - } - - @GET - @Path("/active") - public Response getActiveRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/paused") - public Response getPausedRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/cooldown") - public Response getCooldownRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/finished") - public Response getFinishedRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - public Response getRequests(@Context HttpServletRequest request) { - // TODO - update internal cache list? - return getMergedListResult(request); - } - - @GET - @Path("/queued/pending") - public Response getPendingRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/queued/cleanup") - public Response getCleanupRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/request/{requestId}") - public Response getRequest(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @DELETE - @Path("/request/{requestId}") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response deleteRequest(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityDeleteRequestRequest deleteRequest) { - // TODO - update internal list? - return routeByRequestId(requestContext, requestId, deleteRequest); - } - - @PUT - @Path("/request/{requestId}/scale") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response scale(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularityScaleRequest scaleRequest) { - return routeByRequestId(requestContext, requestId, scaleRequest); - } - - @DELETE - @Path("/request/{requestId}/scale") - public Response deleteExpiringScale(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @Deprecated - @DELETE - @Path("/request/{requestId}/skipHealthchecks") - public Response deleteExpiringSkipHealthchecksDeprecated(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @DELETE - @Path("/request/{requestId}/skip-healthchecks") - public Response deleteExpiringSkipHealthchecks(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @DELETE - @Path("/request/{requestId}/pause") - public Response deleteExpiringPause(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @DELETE - @Path("/request/{requestId}/bounce") - public Response deleteExpiringBounce(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @Deprecated - @PUT - @Path("/request/{requestId}/skipHealthchecks") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response skipHealthchecksDeprecated(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularitySkipHealthchecksRequest skipHealthchecksRequest) { - return skipHealthchecks(requestId, requestContext, skipHealthchecksRequest); - } - - @PUT - @Path("/request/{requestId}/skip-healthchecks") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response skipHealthchecks(@PathParam("requestId") String requestId, - @Context HttpServletRequest requestContext, - SingularitySkipHealthchecksRequest skipHealthchecksRequest) { - return routeByRequestId(requestContext, requestId, skipHealthchecksRequest); - } - - @GET - @Path("/lbcleanup") - public Response getLbCleanupRequests(@Context HttpServletRequest request) { - return getMergedListResult(request); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/S3LogResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/S3LogResource.java deleted file mode 100644 index 82ff94c3d4..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/S3LogResource.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityTaskId; -import com.hubspot.singularity.api.SingularityS3SearchRequest; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.exceptions.NotImplemenedException; - -@Path(ApiPaths.S3_LOG_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class S3LogResource extends ProxyResource { - - @Inject - public S3LogResource() {} - - @GET - @Path("/task/{taskId}") - public Response getS3LogsForTask(@Context HttpServletRequest request, @PathParam("taskId") String taskId) throws Exception { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/request/{requestId}") - public Response getS3LogsForRequest(@Context HttpServletRequest request, @PathParam("requestId") String requestId) throws Exception { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/request/{requestId}/deploy/{deployId}") - public Response getS3LogsForDeploy(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("deployId") String deployId) throws Exception { - return routeByRequestId(request, requestId); - } - - @POST - @Path("/search") - @Consumes(MediaType.APPLICATION_JSON) - public Response getPaginatedS3Logs(@Context HttpServletRequest request, SingularityS3SearchRequest search) throws Exception { - // TODO - merge search results from multiple data centers, route if request id set - throw new NotImplemenedException(); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SandboxResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SandboxResource.java deleted file mode 100644 index 1b5349b03e..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SandboxResource.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityTaskId; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.SANDBOX_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class SandboxResource extends ProxyResource { - - @Inject - public SandboxResource() {} - - @GET - @Path("/{taskId}/browse") - public Response browse(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/{taskId}/read") - public Response read(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SingularityClusterCoodinatorResourcesModule.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SingularityClusterCoodinatorResourcesModule.java deleted file mode 100644 index 94e6b59010..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SingularityClusterCoodinatorResourcesModule.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.hubspot.singularity.proxy; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Optional; -import com.google.inject.AbstractModule; -import com.google.inject.Provides; -import com.google.inject.Scopes; -import com.google.inject.Singleton; -import com.google.inject.name.Named; -import com.hubspot.horizon.AsyncHttpClient; -import com.hubspot.horizon.HttpClient; -import com.hubspot.horizon.HttpConfig; -import com.hubspot.horizon.ning.NingAsyncHttpClient; -import com.hubspot.horizon.ning.NingHttpClient; -import com.hubspot.singularity.SingularityClientCredentials; -import com.hubspot.singularity.SingularityServiceBaseModule; -import com.hubspot.singularity.client.SingularityClient; -import com.hubspot.singularity.client.SingularityClientProvider; -import com.hubspot.singularity.config.ClusterCoordinatorConfiguration; -import com.hubspot.singularity.config.IndexViewConfiguration; - -import io.dropwizard.server.SimpleServerFactory; - -public class SingularityClusterCoodinatorResourcesModule extends AbstractModule { - public static final String ASYNC_HTTP_CLIENT = "singularity.async.http.client"; - - private final ClusterCoordinatorConfiguration configuration; - - public SingularityClusterCoodinatorResourcesModule(ClusterCoordinatorConfiguration configuration) { - this.configuration = configuration; - } - - @Override - public void configure() { - bind(DataCenterLocator.class).in(Scopes.SINGLETON); - - bind(DeployResource.class); - bind(HistoryResource.class); - bind(RackResource.class); - bind(RequestResource.class); - bind(S3LogResource.class); - bind(SandboxResource.class); - bind(SlaveResource.class); - bind(StateResource.class); - bind(TaskResource.class); - bind(WebhookResource.class); - bind(AuthResource.class); - bind(UserResource.class); - bind(DisastersResource.class); - bind(PriorityResource.class); - bind(UsageResource.class); - bind(RequestGroupResource.class); - bind(InactiveSlaveResource.class); - bind(TaskTrackerResource.class); - - install(new SingularityServiceBaseModule(configuration.getUiConfiguration())); - } - - @Provides - @Singleton - public IndexViewConfiguration provideIndexViewConfiguration() { - return new IndexViewConfiguration( - configuration.getUiConfiguration(), - configuration.getDefaultMemory(), - configuration.getDefaultCpus(), - configuration.getSlaveHttpPort(), - configuration.getSlaveHttpsPort(), - configuration.getBounceExpirationMinutes(), - configuration.getHealthcheckIntervalSeconds(), - configuration.getHealthcheckTimeoutSeconds(), - configuration.getHealthcheckMaxRetries(), - configuration.getStartupTimeoutSeconds(), - configuration.isLoadBalancingEnabled(), - configuration.getCommonHostnameSuffixToOmit(), - configuration.getWarnIfScheduledJobIsRunningPastNextRunPct() - ); - } - - @Provides - @Named(SingularityServiceBaseModule.SINGULARITY_URI_BASE) - public String getSingularityUriBase() { - final String singularityUiPrefix = configuration.getUiConfiguration().getBaseUrl().or(((SimpleServerFactory) configuration.getServerFactory()).getApplicationContextPath()); - return (singularityUiPrefix.endsWith("/")) ? singularityUiPrefix.substring(0, singularityUiPrefix.length() - 1) : singularityUiPrefix; - } - - @Provides - @Singleton - @Named(ASYNC_HTTP_CLIENT) - public AsyncHttpClient provideAsyncHttpClient(ObjectMapper objectMapper) { - return new NingAsyncHttpClient(HttpConfig.newBuilder().setObjectMapper(objectMapper).build()); - } - - @Provides - @Singleton - public Map provideClients(ObjectMapper objectMapper) { - HttpClient httpClient = new NingHttpClient(HttpConfig.newBuilder().setObjectMapper(objectMapper).build()); - SingularityClientProvider clientProvider = new SingularityClientProvider(httpClient); - Map clients = new HashMap<>(); - configuration.getDataCenters().forEach((dc) -> { - clientProvider.setHosts(dc.getHosts()); - clientProvider.setContextPath(dc.getContextPath()); - clientProvider.setSsl(dc.getScheme().equals("https")); - Optional maybeCredentials = dc.getClientCredentials().or(configuration.getDefaultClientCredentials()); - clients.put(dc.getName(), clientProvider.get(maybeCredentials)); - }); - return clients; - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SlaveResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SlaveResource.java deleted file mode 100644 index c485af5dc7..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/SlaveResource.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.api.SingularityMachineChangeRequest; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.SLAVE_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class SlaveResource extends ProxyResource { - - @Inject - public SlaveResource() {} - - @GET - public Response getSlaves(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/slave/{slaveId}") - public Response getSlaveHistory(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @GET - @Path("/slave/{slaveId}/details") - public Response getSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @DELETE - @Path("/slave/{slaveId}") - public Response removeSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @POST - @Path("/slave/{slaveId}/decommission") - public Response decommissionSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { - return routeBySlaveId(request, slaveId, changeRequest); - } - - @POST - @Path("/slave/{slaveId}/freeze") - public Response freezeSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { - return routeBySlaveId(request, slaveId, changeRequest); - } - - @POST - @Path("/slave/{slaveId}/activate") - public Response activateSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId, SingularityMachineChangeRequest changeRequest) { - return routeBySlaveId(request, slaveId, changeRequest); - } - - @DELETE - @Path("/slave/{slaveId}/expiring") - public Response deleteExpiringStateChange(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @GET - @Path("/expiring") - public Response getExpiringStateChanges(@Context HttpServletRequest request) { - return getMergedListResult(request); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/StateResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/StateResource.java deleted file mode 100644 index d0fd72ccbb..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/StateResource.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityState; -import com.hubspot.singularity.config.ApiPaths; -import com.hubspot.singularity.exceptions.NotImplemenedException; - -@Path(ApiPaths.STATE_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class StateResource extends ProxyResource { - - @Inject - public StateResource() {} - - @GET - public SingularityState getState(@Context HttpServletRequest request) { - // TODO - merge this result? - throw new NotImplemenedException(); - } - - @GET - @Path("/requests/under-provisioned") - public Response getUnderProvisionedRequestIds(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/requests/over-provisioned") - public Response getOverProvisionedRequestIds(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/task-reconciliation") - public Response getTaskReconciliationStatistics(@Context HttpServletRequest request) { - // TODO - merge this result? - throw new NotImplemenedException(); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskResource.java deleted file mode 100644 index 60a807da6e..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskResource.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityPendingTaskId; -import com.hubspot.singularity.SingularityShellCommand; -import com.hubspot.singularity.SingularityTaskId; -import com.hubspot.singularity.api.SingularityKillTaskRequest; -import com.hubspot.singularity.api.SingularityTaskMetadataRequest; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.TASK_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class TaskResource extends ProxyResource { - - @Inject - public TaskResource() {} - - @GET - @Path("/scheduled") - public Response getScheduledTasks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/scheduled/ids") - public Response getScheduledTaskIds(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/scheduled/task/{pendingTaskId}") - public Response getPendingTask(@Context HttpServletRequest request, @PathParam("pendingTaskId") String pendingTaskIdStr) { - SingularityPendingTaskId parsedId = SingularityPendingTaskId.valueOf(pendingTaskIdStr); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/scheduled/request/{requestId}") - public Response getScheduledTasksForRequest(@Context HttpServletRequest request, @PathParam("requestId") String requestId) { - return routeByRequestId(request, requestId); - } - - @GET - @Path("/active/slave/{slaveId}") - public Response getTasksForSlave(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @GET - @Path("/active") - public Response getActiveTasks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/cleaning") - public Response getCleaningTasks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/killed") - public Response getKilledTasks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/lbcleanup") - public Response getLbCleanupTasks(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/task/{taskId}") - public Response getActiveTask(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/task/{taskId}/statistics") - public Response getTaskStatistics(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/task/{taskId}/cleanup") - public Response getTaskCleanup(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @DELETE - @Path("/task/{taskId}") - public Response killTask(@Context HttpServletRequest request, @PathParam("taskId") String taskId, @Context HttpServletRequest requestContext) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @DELETE - @Path("/task/{taskId}") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response killTask(@PathParam("taskId") String taskId, - @Context HttpServletRequest requestContext, - SingularityKillTaskRequest killTaskRequest) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(requestContext, parsedId.getRequestId(), killTaskRequest); - } - - @GET - @Path("/commands/queued") - public Response getQueuedShellCommands(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @POST - @Path("/task/{taskId}/metadata") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response postTaskMetadata(@Context HttpServletRequest request, @PathParam("taskId") String taskId, final SingularityTaskMetadataRequest taskMetadataRequest) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId(), taskMetadataRequest); - } - - @POST - @Path("/task/{taskId}/command") - @Consumes({ MediaType.APPLICATION_JSON }) - public Response runShellCommand(@Context HttpServletRequest request, @PathParam("taskId") String taskId, final SingularityShellCommand shellCommand) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId(), shellCommand); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskTrackerResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskTrackerResource.java deleted file mode 100644 index 1b64d16372..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/TaskTrackerResource.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityTaskId; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.TASK_TRACKER_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class TaskTrackerResource extends ProxyResource { - - @Inject - public TaskTrackerResource() {} - - @GET - @Path("/task/{taskId}") - public Response getTaskState(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - - @GET - @Path("/run/{requestId}/{runId}") - public Response getTaskStateByRunId(@Context HttpServletRequest request, @PathParam("requestId") String requestId, @PathParam("runId") String runId) { - return routeByRequestId(request, requestId); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UsageResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UsageResource.java deleted file mode 100644 index eb6516b474..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UsageResource.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityTaskId; - -@Path("/api/usage") -public class UsageResource extends ProxyResource { - - @Inject - public UsageResource() {} - - @GET - @Path("/slaves") - public Response getSlavesWithUsage(@Context HttpServletRequest request) { - return getMergedListResult(request); - } - - @GET - @Path("/slaves/{slaveId}/tasks/current") - public Response getSlaveCurrentTaskUsage(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @GET - @Path("/slaves/{slaveId}/history") - public Response getSlaveUsageHistory(@Context HttpServletRequest request, @PathParam("slaveId") String slaveId) { - return routeBySlaveId(request, slaveId); - } - - @GET - @Path("/tasks/{taskId}/history") - public Response getTaskUsageHistory(@Context HttpServletRequest request, @PathParam("taskId") String taskId) { - SingularityTaskId parsedId = SingularityTaskId.valueOf(taskId); - return routeByRequestId(request, parsedId.getRequestId()); - } - -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UserResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UserResource.java deleted file mode 100644 index 9f21e27bb2..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/UserResource.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityUserSettings; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.USER_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class UserResource extends ProxyResource { - // TODO - replicate to all data centers? - - @Inject - public UserResource() {} - - @GET - @Path("/settings") - public Response getUserSettings(@Context HttpServletRequest request) { - return routeToDefaultDataCenter(request); - } - - @POST - @Path("/settings") - public Response setUserSettings(@Context HttpServletRequest request, SingularityUserSettings settings) { - return routeToDefaultDataCenter(request, settings); - } - - @POST - @Path("/settings/starred-requests") - public Response addStarredRequests(@Context HttpServletRequest request, SingularityUserSettings settings) { - return routeToDefaultDataCenter(request, settings); - } - - @DELETE - @Path("/settings/starred-requests") - public Response deleteStarredRequests(@Context HttpServletRequest request, SingularityUserSettings settings) { - return routeToDefaultDataCenter(request, settings); - } -} diff --git a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/WebhookResource.java b/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/WebhookResource.java deleted file mode 100644 index 1d35c01f2d..0000000000 --- a/SingularityClusterCoordinator/src/main/java/com/hubspot/singularity/proxy/WebhookResource.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.hubspot.singularity.proxy; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import com.google.inject.Inject; -import com.hubspot.singularity.SingularityWebhook; -import com.hubspot.singularity.config.ApiPaths; - -@Path(ApiPaths.WEBHOOK_RESOURCE_PATH) -@Produces({ MediaType.APPLICATION_JSON }) -public class WebhookResource extends ProxyResource { - // TODO - better routing here, route to all? - - @Inject - public WebhookResource() {} - - @GET - public Response getActiveWebhooks(@Context HttpServletRequest request) { - return routeToDefaultDataCenter(request); - } - - @GET - @Path("/summary") - public Response getWebhooksWithQueueSize(@Context HttpServletRequest request) { - return routeToDefaultDataCenter(request); - } - - @POST - public Response addWebhook(@Context HttpServletRequest request, SingularityWebhook webhook) { - return routeToDefaultDataCenter(request, webhook); - } - - @DELETE - @Deprecated - @Path("/{webhookId}") - public Response deleteWebhookDeprecated(@Context HttpServletRequest request, @PathParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Deprecated - @Path("/deploy/{webhookId}") - public Response getQueuedDeployUpdatesDeprecated(@Context HttpServletRequest request, @PathParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Deprecated - @Path("/request/{webhookId}") - public Response getQueuedRequestUpdatesDeprecated(@Context HttpServletRequest request, @PathParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Deprecated - @Path("/task/{webhookId}") - public Response getQueuedTaskUpdatesDeprecated(@Context HttpServletRequest request, @PathParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @DELETE - public Response deleteWebhook(@Context HttpServletRequest request, @QueryParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Path("/deploy") - public Response getQueuedDeployUpdates(@Context HttpServletRequest request, @QueryParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Path("/request") - public Response getQueuedRequestUpdates(@Context HttpServletRequest request, @QueryParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - - @GET - @Path("/task") - public Response getQueuedTaskUpdates(@Context HttpServletRequest request, @QueryParam("webhookId") String webhookId) { - return routeToDefaultDataCenter(request); - } - -} diff --git a/pom.xml b/pom.xml index f5a53811d3..39589ce238 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,6 @@ SingularityServiceIntegrationTests SingularityUI SingularityServiceBase - SingularityClusterCoordinator From e2f53e764bac8d7f901f228772c604a619dd031b Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Thu, 2 Nov 2017 10:53:38 -0400 Subject: [PATCH 47/79] configurable auth token key --- .../hubspot/singularity/SingularityServiceModule.java | 4 +++- .../singularity/config/IndexViewConfiguration.java | 9 ++++++++- .../hubspot/singularity/config/UIConfiguration.java | 10 +++++----- .../java/com/hubspot/singularity/views/IndexView.java | 9 ++++++++- SingularityUI/app/assets/index.mustache | 3 ++- SingularityUI/app/utils.es6 | 2 +- SingularityUI/gulpfile.js | 2 +- 7 files changed, 28 insertions(+), 11 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java b/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java index c0cf1d028a..9f52833006 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/SingularityServiceModule.java @@ -6,6 +6,7 @@ import com.google.inject.Singleton; import com.hubspot.dropwizard.guicier.DropwizardAwareModule; import com.hubspot.mesos.client.SingularityMesosClientModule; +import com.hubspot.singularity.auth.SingularityAuthenticatorClass; import com.hubspot.singularity.config.IndexViewConfiguration; import com.hubspot.singularity.config.SingularityConfiguration; import com.hubspot.singularity.data.SingularityDataModule; @@ -56,7 +57,8 @@ public IndexViewConfiguration provideIndexViewConfiguration() { configuration.getStartupTimeoutSeconds(), !Strings.isNullOrEmpty(configuration.getLoadBalancerUri()), configuration.getCommonHostnameSuffixToOmit(), - configuration.getWarnIfScheduledJobIsRunningPastNextRunPct() + configuration.getWarnIfScheduledJobIsRunningPastNextRunPct(), + configuration.getAuthConfiguration().isEnabled() && configuration.getAuthConfiguration().getAuthenticators().contains(SingularityAuthenticatorClass.WEBHOOK) ); } } diff --git a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/IndexViewConfiguration.java b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/IndexViewConfiguration.java index 5c8e6f3a5f..3ca768f62a 100644 --- a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/IndexViewConfiguration.java +++ b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/IndexViewConfiguration.java @@ -16,6 +16,7 @@ public class IndexViewConfiguration { private final boolean loadBalancingEnabled; private final Optional commonHostnameSuffixToOmit; private final Integer warnIfScheduledJobIsRunningPastNextRunPct; + private final boolean generateAuthHeader; public IndexViewConfiguration(UIConfiguration uiConfiguration, Integer defaultMemory, @@ -28,7 +29,8 @@ public IndexViewConfiguration(UIConfiguration uiConfiguration, Optional healthcheckMaxRetries, int startupTimeoutSeconds, boolean loadBalancingEnabled, - Optional commonHostnameSuffixToOmit, Integer warnIfScheduledJobIsRunningPastNextRunPct) { + Optional commonHostnameSuffixToOmit, Integer warnIfScheduledJobIsRunningPastNextRunPct, + boolean generateAuthHeader) { this.uiConfiguration = uiConfiguration; this.defaultMemory = defaultMemory; this.defaultCpus = defaultCpus; @@ -42,6 +44,7 @@ public IndexViewConfiguration(UIConfiguration uiConfiguration, this.loadBalancingEnabled = loadBalancingEnabled; this.commonHostnameSuffixToOmit = commonHostnameSuffixToOmit; this.warnIfScheduledJobIsRunningPastNextRunPct = warnIfScheduledJobIsRunningPastNextRunPct; + this.generateAuthHeader = generateAuthHeader; } public UIConfiguration getUiConfiguration() { @@ -95,4 +98,8 @@ public Optional getCommonHostnameSuffixToOmit() { public Integer getWarnIfScheduledJobIsRunningPastNextRunPct() { return warnIfScheduledJobIsRunningPastNextRunPct; } + + public boolean isGenerateAuthHeader() { + return generateAuthHeader; + } } diff --git a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java index 625bdd9b45..2e8d680c63 100644 --- a/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java +++ b/SingularityServiceBase/src/main/java/com/hubspot/singularity/config/UIConfiguration.java @@ -91,7 +91,7 @@ public static RootUrlMode parse(String value) { private Optional extraScript = Optional.absent(); @JsonProperty - private boolean generateAuthHeader = false; + private String authTokenKey = "token"; @JsonProperty @NotNull @@ -242,12 +242,12 @@ public void setExtraScript(Optional extraScript) { this.extraScript = extraScript; } - public boolean isGenerateAuthHeader() { - return generateAuthHeader; + public String getAuthTokenKey() { + return authTokenKey; } - public void setGenerateAuthHeader(boolean generateAuthHeader) { - this.generateAuthHeader = generateAuthHeader; + public void setAuthTokenKey(String authTokenKey) { + this.authTokenKey = authTokenKey; } public String getAuthCookieName() { diff --git a/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java b/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java index e6b25c19a5..c07b296655 100644 --- a/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java +++ b/SingularityServiceBase/src/main/java/com/hubspot/singularity/views/IndexView.java @@ -63,6 +63,7 @@ public class IndexView extends View { private final boolean generateAuthHeader; private final String authCookieName; + private final String authTokenKey; public IndexView(String singularityUriBase, String appRoot, IndexViewConfiguration configuration, ObjectMapper mapper) { super("index.mustache"); @@ -126,8 +127,9 @@ public IndexView(String singularityUriBase, String appRoot, IndexViewConfigurati this.extraScript = uiConfiguration.getExtraScript().orNull(); - this.generateAuthHeader = uiConfiguration.isGenerateAuthHeader(); + this.generateAuthHeader = configuration.isGenerateAuthHeader(); this.authCookieName = uiConfiguration.getAuthCookieName(); + this.authTokenKey = uiConfiguration.getAuthTokenKey(); } public String getAppRoot() { @@ -258,6 +260,10 @@ public String getAuthCookieName() { return authCookieName; } + public String getAuthTokenKey() { + return authTokenKey; + } + @Override public String toString() { return "IndexView{" + @@ -293,6 +299,7 @@ public String toString() { ", extraScript='" + extraScript + '\'' + ", generateAuthHeader=" + generateAuthHeader + ", authCookieName='" + authCookieName + '\'' + + ", authTokenKey='" + authTokenKey + '\'' + "} " + super.toString(); } } diff --git a/SingularityUI/app/assets/index.mustache b/SingularityUI/app/assets/index.mustache index 00d52f1e2b..dd2b9980bb 100644 --- a/SingularityUI/app/assets/index.mustache +++ b/SingularityUI/app/assets/index.mustache @@ -49,7 +49,8 @@ globalRefreshInterval: 60000, sentryDsn: "{{{sentryDsn}}}", generateAuthHeader: {{{generateAuthHeader}}}, - authCookieName: "{{{ authCookieName }}}" + authCookieName: "{{{ authCookieName }}}", + authTokenKey: "{{{ authTokenKey }}}" }; diff --git a/SingularityUI/app/utils.es6 b/SingularityUI/app/utils.es6 index 59b3166cf2..37bfdd778a 100644 --- a/SingularityUI/app/utils.es6 +++ b/SingularityUI/app/utils.es6 @@ -484,7 +484,7 @@ const Utils = { if (!authCookie) { return ''; } - const authToken = JSON.parse(authCookie).token; + const authToken = JSON.parse(authCookie)[config.authTokenKey]; return `Bearer ${ authToken }`; } }; diff --git a/SingularityUI/gulpfile.js b/SingularityUI/gulpfile.js index 65f0602571..f271b0c219 100644 --- a/SingularityUI/gulpfile.js +++ b/SingularityUI/gulpfile.js @@ -45,7 +45,7 @@ var templateData = { redirectOnUnauthorizedUrl: process.env.SINGULARITY_REDIRECT_ON_UNAUTHORIZED_URL || '', extraScript: process.env.SINGULARITY_EXTRA_SCRIPT || '', sentryDsn: process.env.SINGULARITY_SENTRY_DSN || '', - generateAuthHeader: process.env.SINGULARITY_GENERATE_AUTH_HEADER || 'false', + authTokenKey: process.env.SINGULARITY_AUTH_TOKEN_KEY || 'token', authCookieName: process.env.SINGULARITY_AUTH_COOKIE_NAME || '' }; From 251d71d2e24525af9886632cc042f30a4295e4a6 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Thu, 2 Nov 2017 11:10:23 -0400 Subject: [PATCH 48/79] avoid optional get on empty --- .../auth/authenticator/SingularityWebhookAuthenticator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java index b5a68a92eb..15f70a7462 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityWebhookAuthenticator.java @@ -74,12 +74,12 @@ private SingularityUserPermissionsResponse verify(String authHeaderValue) { } else { String responseBody = response.getResponseBody(); SingularityUserPermissionsResponse permissionsResponse = objectMapper.readValue(responseBody, SingularityUserPermissionsResponse.class); - if (!permissionsResponse.getUser().isPresent() && permissionsResponse.getUser().get().isAuthenticated()) { - throw WebExceptions.unauthorized(String.format("(Webhook) User not authenticated (response: %s)", permissionsResponse)); - } if (!permissionsResponse.getUser().isPresent()) { throw WebExceptions.unauthorized(String.format("(Webhook) No user present in response %s", permissionsResponse)); } + if (!permissionsResponse.getUser().get().isAuthenticated()) { + throw WebExceptions.unauthorized(String.format("(Webhook) User not authenticated (response: %s)", permissionsResponse)); + } permissionsCache.put(authHeaderValue, permissionsResponse); return permissionsResponse; } From 8bc7f55bf9e6650c676ae63d4952045f4726b57c Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Thu, 2 Nov 2017 13:06:02 -0400 Subject: [PATCH 49/79] Shade protobuf for SingularityClient --- SingularityClient/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SingularityClient/pom.xml b/SingularityClient/pom.xml index f2b8aa5256..efcc54c351 100644 --- a/SingularityClient/pom.xml +++ b/SingularityClient/pom.xml @@ -145,6 +145,10 @@ com.github.rholder com.hubspot.singularity.shaded.com.github.rholder + + com.google.protobuf + com.hubspot.singularity.shaded.com.google.protobuf + From 35109a71ccf8fdcf654e02c5a3b3733fcf3a4a28 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 3 Nov 2017 08:36:51 -0400 Subject: [PATCH 50/79] also shade in SingularityBase --- SingularityBase/pom.xml | 51 +++++++++++++++++++++++++++++++++++++++ SingularityClient/pom.xml | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/SingularityBase/pom.xml b/SingularityBase/pom.xml index 5c8f2fa8dd..6d6bb9f353 100644 --- a/SingularityBase/pom.xml +++ b/SingularityBase/pom.xml @@ -67,6 +67,57 @@ + + + + + org.apache.maven.plugins + maven-shade-plugin + + true + true + shaded + + + com.hubspot:SingularityBase + com.google.guava:guava + + + + + com.google.guava:guava + + com/google/common/base/Optional.class + + + + + + com.google.protobuf + com.hubspot.singularity.shaded.com.google.protobuf.twosixone + + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + + + + + diff --git a/SingularityClient/pom.xml b/SingularityClient/pom.xml index efcc54c351..f7fb9d6290 100644 --- a/SingularityClient/pom.xml +++ b/SingularityClient/pom.xml @@ -147,7 +147,7 @@ com.google.protobuf - com.hubspot.singularity.shaded.com.google.protobuf + com.hubspot.singularity.shaded.com.google.protobuf.twosixone From 7c747dc963e40ba4d966ba7ead875fc64e344051 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Fri, 3 Nov 2017 08:49:41 -0400 Subject: [PATCH 51/79] attempt two at shading mesos --- SingularityBase/pom.xml | 4 ++-- SingularityClient/pom.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SingularityBase/pom.xml b/SingularityBase/pom.xml index 6d6bb9f353..4cfdd57a54 100644 --- a/SingularityBase/pom.xml +++ b/SingularityBase/pom.xml @@ -93,8 +93,8 @@ - com.google.protobuf - com.hubspot.singularity.shaded.com.google.protobuf.twosixone + org.apache.mesos + com.hubspot.singularity.shaded.org.apache.mesos.oneonetwo diff --git a/SingularityClient/pom.xml b/SingularityClient/pom.xml index f7fb9d6290..4297f2be65 100644 --- a/SingularityClient/pom.xml +++ b/SingularityClient/pom.xml @@ -146,8 +146,8 @@ com.hubspot.singularity.shaded.com.github.rholder - com.google.protobuf - com.hubspot.singularity.shaded.com.google.protobuf.twosixone + org.apache.mesos + com.hubspot.singularity.shaded.org.apache.mesos.oneonetwo From c2a55071f0669abac39d31ce40cc75e3304a3538 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Mon, 6 Nov 2017 09:09:44 -0500 Subject: [PATCH 52/79] make this a constant --- .../main/java/com/hubspot/singularity/SingularityUser.java | 4 +--- .../singularity/auth/SingularityAuthedUserFactory.java | 2 +- .../authenticator/SingularityMultiMethodAuthenticator.java | 2 +- .../java/com/hubspot/singularity/resources/AuthResource.java | 2 +- .../singularity/SingularityAuthorizationHelperTest.java | 2 +- .../singularity/scheduler/SingularitySchedulerTestBase.java | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java index 65005e8224..62c4416267 100644 --- a/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java +++ b/SingularityBase/src/main/java/com/hubspot/singularity/SingularityUser.java @@ -18,9 +18,7 @@ public class SingularityUser implements Principal { private final Set groups; private final boolean authenticated; - public static SingularityUser defaultUser() { - return new SingularityUser("singularity", Optional.absent(), Optional.absent(), Collections.emptySet(), false); - } + public static SingularityUser DEFAULT_USER = new SingularityUser("singularity", Optional.absent(), Optional.absent(), Collections.emptySet(), false); public SingularityUser(String id, Optional name, Optional email, Set groups) { this(id, name, email, groups, true); diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java index 02cbe0f86e..2d7244872d 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/SingularityAuthedUserFactory.java @@ -50,6 +50,6 @@ public SingularityUser provide() { } // Auth is disabled, return a dummy/default user - return SingularityUser.defaultUser(); + return SingularityUser.DEFAULT_USER; } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiMethodAuthenticator.java b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiMethodAuthenticator.java index bb2a7e3575..d35d2e7c49 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiMethodAuthenticator.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/auth/authenticator/SingularityMultiMethodAuthenticator.java @@ -52,6 +52,6 @@ public Optional authenticate(ContainerRequestContext context) { } // Auth is disabled, return a dummy/default user - return Optional.of(SingularityUser.defaultUser()); + return Optional.of(SingularityUser.DEFAULT_USER); } } diff --git a/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java b/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java index 8273b0d283..0c52f7d603 100644 --- a/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java +++ b/SingularityService/src/main/java/com/hubspot/singularity/resources/AuthResource.java @@ -56,7 +56,7 @@ public SingularityUserHolder getUser(@Auth SingularityUser user) { @ApiOperation("Check if the specified user is authorized for a request") public Response checkReadOnlyAuth(@PathParam("requestId") String requestId, @PathParam("userId") String userId, @QueryParam("scope") Optional scope) { - authorizationHelper.checkForAuthorizationByRequestId(requestId, authDatastore.getUser(userId).orElse(SingularityUser.defaultUser()), scope.or(SingularityAuthorizationScope.READ)); + authorizationHelper.checkForAuthorizationByRequestId(requestId, authDatastore.getUser(userId).orElse(SingularityUser.DEFAULT_USER), scope.or(SingularityAuthorizationScope.READ)); return Response.ok().build(); } diff --git a/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java b/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java index e51b0d0152..369e08f38c 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/SingularityAuthorizationHelperTest.java @@ -58,7 +58,7 @@ public static SingularityConfiguration buildAuthEnabledConfig(Set requir public static final SingularityRequest REQUEST_WITH_GROUP_B = new SingularityRequestBuilder("test_b", RequestType.SERVICE).setGroup(Optional.of("b")).build(); - public static final SingularityUser NOT_LOGGED_IN = SingularityUser.defaultUser(); + public static final SingularityUser NOT_LOGGED_IN = SingularityUser.DEFAULT_USER; public static final SingularityUser USER_GROUP_A = new SingularityUser("test1", Optional.of("test user1"), Optional.of("test1@test.com"), ImmutableSet.of("a")); public static final SingularityUser USER_GROUP_AB = new SingularityUser("test2", Optional.of("test user2"), Optional.of("test2@test.com"), ImmutableSet.of("a", "b")); public static final SingularityUser USER_GROUP_B = new SingularityUser("test3", Optional.of("test user3"), Optional.of("test3@test.com"), ImmutableSet.of("b")); diff --git a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java index 9db66cbba3..31409d605d 100644 --- a/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java +++ b/SingularityService/src/test/java/com/hubspot/singularity/scheduler/SingularitySchedulerTestBase.java @@ -189,7 +189,7 @@ public class SingularitySchedulerTestBase extends SingularityCuratorTestBase { protected Optional user = Optional.absent(); - protected SingularityUser singularityUser = SingularityUser.defaultUser(); + protected SingularityUser singularityUser = SingularityUser.DEFAULT_USER; public SingularitySchedulerTestBase(boolean useDBTests) { super(useDBTests); From ce4390eade420dcab7d9e7d21aad696b15b55489 Mon Sep 17 00:00:00 2001 From: Stephen Salinas Date: Tue, 7 Nov 2017 12:22:51 -0500 Subject: [PATCH 53/79] missing line in gulpfile --- SingularityUI/gulpfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/SingularityUI/gulpfile.js b/SingularityUI/gulpfile.js index f271b0c219..a6b2dbd4bf 100644 --- a/SingularityUI/gulpfile.js +++ b/SingularityUI/gulpfile.js @@ -45,6 +45,7 @@ var templateData = { redirectOnUnauthorizedUrl: process.env.SINGULARITY_REDIRECT_ON_UNAUTHORIZED_URL || '', extraScript: process.env.SINGULARITY_EXTRA_SCRIPT || '', sentryDsn: process.env.SINGULARITY_SENTRY_DSN || '', + generateAuthHeader: process.env.SINGULARITY_GENERATE_AUTH_HEADER || 'false', authTokenKey: process.env.SINGULARITY_AUTH_TOKEN_KEY || 'token', authCookieName: process.env.SINGULARITY_AUTH_COOKIE_NAME || '' }; From ff56e60660e9ca8a374bc92d2e4d0c62dfc923c9 Mon Sep 17 00:00:00 2001 From: Kevin Moses Date: Tue, 7 Nov 2017 14:10:43 -0500 Subject: [PATCH 54/79] Split s3 files based on top level directory --- .../app/components/taskDetail/TaskS3Logs.jsx | 165 ++++++++++++------ 1 file changed, 108 insertions(+), 57 deletions(-) diff --git a/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx b/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx index 0076812e21..15ccddc18b 100644 --- a/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx +++ b/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx @@ -1,67 +1,118 @@ -import React, { PropTypes } from 'react'; +import React, { PropTypes, Component } from 'react'; import Utils from '../../utils'; import Section from '../common/Section'; import UITable from '../common/table/UITable'; import Column from '../common/table/Column'; import { Glyphicon } from 'react-bootstrap'; +import { groupBy } from 'underscore'; -function TaskS3Logs (props) { - return ( -
- s3File.key} - rowChunkSize={5} - paginated={true} - > - ( - - {Utils.trimS3File(s3File.key.substring(s3File.key.lastIndexOf('/') + 1), props.taskId)} - - )} - /> - Utils.humanizeFileSize(s3File.size)} - /> - Utils.absoluteTimestampWithSeconds(s3File.lastModified)} - /> - (s3File.startTime) ? Utils.absoluteTimestampWithSeconds(s3File.startTime) : Utils.absoluteTimestampWithSeconds(props.taskStartedAt)} - /> - (s3File.endTime) ? Utils.absoluteTimestampWithSeconds(s3File.endTime) : Utils.absoluteTimestampWithSeconds(s3File.lastModified)} - /> - ( - - +class TaskS3Logs extends Component { + constructor(props) { + super(props); + this.state = { + viewingGroup: null + }; + this.getFileType = this.getFileType.bind(this); + } + + getFileType(s3File) { + return s3File.key.split('/')[0]; + } + + renderTable(s3Files) { + const { taskStartedAt, taskId } = this.props; + return ( +
+ this.setState({ viewingGroup: null })}> +
+ + Back +
+
+ s3File.key} + rowChunkSize={5} + paginated={true} + > + ( + + {Utils.trimS3File(s3File.key.substring(s3File.key.lastIndexOf('/') + 1), taskId)} + + )} + /> + Utils.humanizeFileSize(s3File.size)} + /> + Utils.absoluteTimestampWithSeconds(s3File.lastModified)} + /> + (s3File.startTime) ? Utils.absoluteTimestampWithSeconds(s3File.startTime) : Utils.absoluteTimestampWithSeconds(taskStartedAt)} + /> + (s3File.endTime) ? Utils.absoluteTimestampWithSeconds(s3File.endTime) : Utils.absoluteTimestampWithSeconds(s3File.lastModified)} + /> + ( + + + + )} + /> + +
+ ); + } + + renderFolders(groups) { + return ( +
- ); + + ))} + + ); + } + + render() { + const { s3Files } = this.props; + const groupedFiles = groupBy(s3Files, this.getFileType); + + return ( +
+ {this.state.viewingGroup + ? this.renderTable(groupedFiles[this.state.viewingGroup]) + : this.renderFolders(Object.keys(groupedFiles)) + } +
+ ); + } } TaskS3Logs.propTypes = { From a0803cc5750fb6419f0c5926657ab134216facd3 Mon Sep 17 00:00:00 2001 From: Kevin Moses Date: Tue, 7 Nov 2017 14:12:11 -0500 Subject: [PATCH 55/79] Add key --- SingularityUI/app/components/taskDetail/TaskS3Logs.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx b/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx index 15ccddc18b..570dc3948d 100644 --- a/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx +++ b/SingularityUI/app/components/taskDetail/TaskS3Logs.jsx @@ -89,7 +89,7 @@ class TaskS3Logs extends Component { return (