diff --git a/airbyte-commons/src/main/java/io/airbyte/commons/auth/AuthRole.java b/airbyte-commons/src/main/java/io/airbyte/commons/auth/AuthRole.java
new file mode 100644
index 000000000000..b2ff02702545
--- /dev/null
+++ b/airbyte-commons/src/main/java/io/airbyte/commons/auth/AuthRole.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2022 Airbyte, Inc., all rights reserved.
+ */
+
+package io.airbyte.commons.auth;
+
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * This enum describes the standard auth levels for a given resource. It currently is only used for
+ * 2 resources Workspace and Instance (i.e. the entire instance or deployment of Airbyte).
+ *
+ * In the context of a workspace, there is a 1:1 mapping.
+ *
+ * - OWNER => WORKSPACE OWNER. Superadmin of the instance (typically the person that created it),
+ * has all the rights on the instance including deleting it.
+ * - ADMIN => WORKSPACE ADMIN. Admin of the instance, can invite other users, update their
+ * permission and change settings of the instance.
+ * - EDITOR => WORKSPACE EDITOR
+ * - READER => WORKSPACE READER
+ * - AUTHENTICATED_USER => INVALID
+ * - NONE => NONE (does not have access to this resource)
+ *
+ * In the context of the instance, there are currently only 3 levels.
+ *
+ * - ADMIN => INSTANCE ADMIN
+ * - AUTHENTICATED_USER => Denotes that all that is required for access is an active Airbyte
+ * account. This should only ever be used when the associated resource is an INSTANCE. All other
+ * uses are invalid. It is a special value in the enum to handle a case that only applies to
+ * instances and no other resources.
+ * - NONE => NONE (not applicable. anyone being checked in our auth stack already has an account
+ * so by definition they have some access to the instance.)
+ *
+ */
+public enum AuthRole {
+
+ OWNER(500, AuthRoleConstants.OWNER),
+ ADMIN(400, AuthRoleConstants.ADMIN),
+ EDITOR(300, AuthRoleConstants.EDITOR),
+ READER(200, AuthRoleConstants.READER),
+ AUTHENTICATED_USER(100, AuthRoleConstants.AUTHENTICATED_USER), // ONLY USE WITH INSTANCE RESOURCE!
+ NONE(0, AuthRoleConstants.NONE);
+
+ private final int authority;
+ private final String label;
+
+ AuthRole(final int authority, final String label) {
+ this.authority = authority;
+ this.label = label;
+ }
+
+ public int getAuthority() {
+ return authority;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * Builds the set of roles based on the provided {@link AuthRole} value.
+ *
+ * The generated set of auth roles contains the provided {@link AuthRole} (if not {@code null}) and
+ * any other authentication roles with a lesser {@link #getAuthority()} value.
+ *
+ *
+ * @param authRole An {@link AuthRole} (may be {@code null}).
+ * @return The set of {@link AuthRole}s based on the provided {@link AuthRole}.
+ */
+ public static Set buildAuthRolesSet(final AuthRole authRole) {
+ final Set authRoles = new HashSet<>();
+
+ if (authRole != null) {
+ authRoles.add(authRole);
+ authRoles.addAll(Stream.of(values())
+ .filter(role -> !NONE.equals(role))
+ .filter(role -> role.getAuthority() < authRole.getAuthority())
+ .collect(Collectors.toSet()));
+ }
+
+ // Sort final set by descending authority order
+ return authRoles.stream()
+ .sorted(Comparator.comparingInt(AuthRole::getAuthority))
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ }
+
+}
diff --git a/airbyte-commons/src/main/java/io/airbyte/commons/auth/AuthRoleConstants.java b/airbyte-commons/src/main/java/io/airbyte/commons/auth/AuthRoleConstants.java
new file mode 100644
index 000000000000..6a206ee0e89e
--- /dev/null
+++ b/airbyte-commons/src/main/java/io/airbyte/commons/auth/AuthRoleConstants.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2022 Airbyte, Inc., all rights reserved.
+ */
+
+package io.airbyte.commons.auth;
+
+/**
+ * Collection of constants that defines authorization roles.
+ */
+public final class AuthRoleConstants {
+
+ public static final String ADMIN = "ADMIN";
+ public static final String AUTHENTICATED_USER = "AUTHENTICATED_USER";
+ public static final String EDITOR = "EDITOR";
+ public static final String OWNER = "OWNER";
+ public static final String NONE = "NONE";
+ public static final String READER = "READER";
+
+ private AuthRoleConstants() {}
+
+}
diff --git a/airbyte-commons/src/test/java/io/airbyte/commons/auth/AuthRoleTest.java b/airbyte-commons/src/test/java/io/airbyte/commons/auth/AuthRoleTest.java
new file mode 100644
index 000000000000..835488fdd84a
--- /dev/null
+++ b/airbyte-commons/src/test/java/io/airbyte/commons/auth/AuthRoleTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022 Airbyte, Inc., all rights reserved.
+ */
+
+package io.airbyte.commons.auth;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Set;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test suite for the {@link AuthRole} enumeration.
+ */
+class AuthRoleTest {
+
+ @Test
+ void testBuildingAuthRoleSet() {
+ final Set ownerResult = AuthRole.buildAuthRolesSet(AuthRole.OWNER);
+ assertEquals(5, ownerResult.size());
+ assertEquals(Set.of(AuthRole.OWNER, AuthRole.ADMIN, AuthRole.EDITOR, AuthRole.READER, AuthRole.AUTHENTICATED_USER), ownerResult);
+
+ final Set adminResult = AuthRole.buildAuthRolesSet(AuthRole.ADMIN);
+ assertEquals(4, adminResult.size());
+ assertEquals(Set.of(AuthRole.ADMIN, AuthRole.EDITOR, AuthRole.READER, AuthRole.AUTHENTICATED_USER), adminResult);
+
+ final Set editorResult = AuthRole.buildAuthRolesSet(AuthRole.EDITOR);
+ assertEquals(3, editorResult.size());
+ assertEquals(Set.of(AuthRole.EDITOR, AuthRole.READER, AuthRole.AUTHENTICATED_USER), editorResult);
+
+ final Set readerResult = AuthRole.buildAuthRolesSet(AuthRole.READER);
+ assertEquals(2, readerResult.size());
+ assertEquals(Set.of(AuthRole.READER, AuthRole.AUTHENTICATED_USER), readerResult);
+
+ final Set authenticatedUserResult = AuthRole.buildAuthRolesSet(AuthRole.AUTHENTICATED_USER);
+ assertEquals(1, authenticatedUserResult.size());
+ assertEquals(Set.of(AuthRole.AUTHENTICATED_USER), authenticatedUserResult);
+
+ final Set noneResult = AuthRole.buildAuthRolesSet(AuthRole.NONE);
+ assertEquals(1, noneResult.size());
+ assertEquals(Set.of(AuthRole.NONE), noneResult);
+
+ final Set nullResult = AuthRole.buildAuthRolesSet(null);
+ assertEquals(0, nullResult.size());
+ }
+
+}
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/AttemptApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/AttemptApiController.java
index d50e731d5e8d..c7c112405b44 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/AttemptApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/AttemptApiController.java
@@ -4,6 +4,8 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.ADMIN;
+
import io.airbyte.api.generated.AttemptApi;
import io.airbyte.api.model.generated.InternalOperationResult;
import io.airbyte.api.model.generated.SaveStatsRequestBody;
@@ -14,10 +16,13 @@
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/attempt/")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class AttemptApiController implements AttemptApi {
private final AttemptHandler attemptHandler;
@@ -36,6 +41,7 @@ public InternalOperationResult saveStats(final SaveStatsRequestBody requestBody)
@Override
@Post(uri = "/set_workflow_in_attempt",
processes = MediaType.APPLICATION_JSON)
+ @Secured({ADMIN})
public InternalOperationResult setWorkflowInAttempt(@Body final SetWorkflowInAttemptRequestBody requestBody) {
return ApiHelper.execute(() -> attemptHandler.setWorkflowInAttempt(requestBody));
}
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/ConnectionApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/ConnectionApiController.java
index f0de78d03d31..f61b89e41447 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/ConnectionApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/ConnectionApiController.java
@@ -4,6 +4,9 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+import static io.airbyte.commons.auth.AuthRoleConstants.READER;
+
import io.airbyte.api.generated.ConnectionApi;
import io.airbyte.api.model.generated.ConnectionCreate;
import io.airbyte.api.model.generated.ConnectionIdRequestBody;
@@ -21,11 +24,14 @@
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/connections")
@Context()
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class ConnectionApiController implements ConnectionApi {
private final ConnectionsHandler connectionsHandler;
@@ -42,24 +48,28 @@ public ConnectionApiController(final ConnectionsHandler connectionsHandler,
@Override
@Post(uri = "/create")
+ @Secured({EDITOR})
public ConnectionRead createConnection(@Body final ConnectionCreate connectionCreate) {
return ApiHelper.execute(() -> connectionsHandler.createConnection(connectionCreate));
}
@Override
@Post(uri = "/update")
+ @Secured({EDITOR})
public ConnectionRead updateConnection(@Body final ConnectionUpdate connectionUpdate) {
return ApiHelper.execute(() -> connectionsHandler.updateConnection(connectionUpdate));
}
@Override
@Post(uri = "/list")
+ @Secured({READER})
public ConnectionReadList listConnectionsForWorkspace(@Body final WorkspaceIdRequestBody workspaceIdRequestBody) {
return ApiHelper.execute(() -> connectionsHandler.listConnectionsForWorkspace(workspaceIdRequestBody));
}
@Override
@Post(uri = "/list_all")
+ @Secured({READER})
public ConnectionReadList listAllConnectionsForWorkspace(@Body final WorkspaceIdRequestBody workspaceIdRequestBody) {
return ApiHelper.execute(() -> connectionsHandler.listAllConnectionsForWorkspace(workspaceIdRequestBody));
}
@@ -72,12 +82,14 @@ public ConnectionReadList searchConnections(@Body final ConnectionSearch connect
@Override
@Post(uri = "/get")
+ @Secured({READER})
public ConnectionRead getConnection(@Body final ConnectionIdRequestBody connectionIdRequestBody) {
return ApiHelper.execute(() -> connectionsHandler.getConnection(connectionIdRequestBody.getConnectionId()));
}
@Override
@Post(uri = "/delete")
+ @Secured({EDITOR})
public void deleteConnection(@Body final ConnectionIdRequestBody connectionIdRequestBody) {
ApiHelper.execute(() -> {
operationsHandler.deleteOperationsForConnection(connectionIdRequestBody);
@@ -88,12 +100,14 @@ public void deleteConnection(@Body final ConnectionIdRequestBody connectionIdReq
@Override
@Post(uri = "/sync")
+ @Secured({EDITOR})
public JobInfoRead syncConnection(@Body final ConnectionIdRequestBody connectionIdRequestBody) {
return ApiHelper.execute(() -> schedulerHandler.syncConnection(connectionIdRequestBody));
}
@Override
@Post(uri = "/reset")
+ @Secured({EDITOR})
public JobInfoRead resetConnection(@Body final ConnectionIdRequestBody connectionIdRequestBody) {
return ApiHelper.execute(() -> schedulerHandler.resetConnection(connectionIdRequestBody));
}
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationApiController.java
index 6c16cec4a596..7a20184b0e4d 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationApiController.java
@@ -4,6 +4,9 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+import static io.airbyte.commons.auth.AuthRoleConstants.READER;
+
import io.airbyte.api.generated.DestinationApi;
import io.airbyte.api.model.generated.CheckConnectionRead;
import io.airbyte.api.model.generated.DestinationCloneRequestBody;
@@ -20,24 +23,32 @@
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
-import lombok.AllArgsConstructor;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/destinations")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
-@AllArgsConstructor
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class DestinationApiController implements DestinationApi {
private final DestinationHandler destinationHandler;
private final SchedulerHandler schedulerHandler;
+ public DestinationApiController(final DestinationHandler destinationHandler, final SchedulerHandler schedulerHandler) {
+ this.destinationHandler = destinationHandler;
+ this.schedulerHandler = schedulerHandler;
+ }
+
@Post(uri = "/check_connection")
+ @Secured({EDITOR})
@Override
public CheckConnectionRead checkConnectionToDestination(@Body final DestinationIdRequestBody destinationIdRequestBody) {
return ApiHelper.execute(() -> schedulerHandler.checkDestinationConnectionFromDestinationId(destinationIdRequestBody));
}
@Post(uri = "/check_connection_for_update")
+ @Secured({EDITOR})
@Override
public CheckConnectionRead checkConnectionToDestinationForUpdate(@Body final DestinationUpdate destinationUpdate) {
return ApiHelper.execute(() -> schedulerHandler.checkDestinationConnectionFromDestinationIdForUpdate(destinationUpdate));
@@ -50,12 +61,14 @@ public DestinationRead cloneDestination(@Body final DestinationCloneRequestBody
}
@Post(uri = "/create")
+ @Secured({EDITOR})
@Override
public DestinationRead createDestination(@Body final DestinationCreate destinationCreate) {
return ApiHelper.execute(() -> destinationHandler.createDestination(destinationCreate));
}
@Post(uri = "/delete")
+ @Secured({EDITOR})
@Override
public void deleteDestination(@Body final DestinationIdRequestBody destinationIdRequestBody) {
ApiHelper.execute(() -> {
@@ -65,12 +78,14 @@ public void deleteDestination(@Body final DestinationIdRequestBody destinationId
}
@Post(uri = "/get")
+ @Secured({READER})
@Override
public DestinationRead getDestination(@Body final DestinationIdRequestBody destinationIdRequestBody) {
return ApiHelper.execute(() -> destinationHandler.getDestination(destinationIdRequestBody));
}
@Post(uri = "/list")
+ @Secured({READER})
@Override
public DestinationReadList listDestinationsForWorkspace(@Body final WorkspaceIdRequestBody workspaceIdRequestBody) {
return ApiHelper.execute(() -> destinationHandler.listDestinationsForWorkspace(workspaceIdRequestBody));
@@ -83,6 +98,7 @@ public DestinationReadList searchDestinations(@Body final DestinationSearch dest
}
@Post(uri = "/update")
+ @Secured({EDITOR})
@Override
public DestinationRead updateDestination(@Body final DestinationUpdate destinationUpdate) {
return ApiHelper.execute(() -> destinationHandler.updateDestination(destinationUpdate));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationDefinitionApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationDefinitionApiController.java
index 54409d281021..ab559f6b3b09 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationDefinitionApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationDefinitionApiController.java
@@ -4,6 +4,11 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.ADMIN;
+import static io.airbyte.commons.auth.AuthRoleConstants.AUTHENTICATED_USER;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+import static io.airbyte.commons.auth.AuthRoleConstants.READER;
+
import io.airbyte.api.generated.DestinationDefinitionApi;
import io.airbyte.api.model.generated.CustomDestinationDefinitionCreate;
import io.airbyte.api.model.generated.DestinationDefinitionIdRequestBody;
@@ -19,11 +24,14 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/destination_definitions")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
@Context
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class DestinationDefinitionApiController implements DestinationDefinitionApi {
private final DestinationDefinitionsHandler destinationDefinitionsHandler;
@@ -33,12 +41,14 @@ public DestinationDefinitionApiController(final DestinationDefinitionsHandler de
}
@Post(uri = "/create_custom")
+ @Secured({EDITOR})
@Override
public DestinationDefinitionRead createCustomDestinationDefinition(final CustomDestinationDefinitionCreate customDestinationDefinitionCreate) {
return ApiHelper.execute(() -> destinationDefinitionsHandler.createCustomDestinationDefinition(customDestinationDefinitionCreate));
}
@Post(uri = "/delete")
+ @Secured({ADMIN})
@Override
public void deleteDestinationDefinition(final DestinationDefinitionIdRequestBody destinationDefinitionIdRequestBody) {
ApiHelper.execute(() -> {
@@ -48,18 +58,21 @@ public void deleteDestinationDefinition(final DestinationDefinitionIdRequestBody
}
@Post(uri = "/get")
+ @Secured({AUTHENTICATED_USER})
@Override
public DestinationDefinitionRead getDestinationDefinition(final DestinationDefinitionIdRequestBody destinationDefinitionIdRequestBody) {
return ApiHelper.execute(() -> destinationDefinitionsHandler.getDestinationDefinition(destinationDefinitionIdRequestBody));
}
@Post(uri = "/get_for_workspace")
+ @Secured({READER})
@Override
public DestinationDefinitionRead getDestinationDefinitionForWorkspace(final DestinationDefinitionIdWithWorkspaceId destinationDefinitionIdWithWorkspaceId) {
return ApiHelper.execute(() -> destinationDefinitionsHandler.getDestinationDefinitionForWorkspace(destinationDefinitionIdWithWorkspaceId));
}
@Post(uri = "/grant_definition")
+ @Secured({ADMIN})
@Override
public PrivateDestinationDefinitionRead grantDestinationDefinitionToWorkspace(final DestinationDefinitionIdWithWorkspaceId destinationDefinitionIdWithWorkspaceId) {
return ApiHelper
@@ -67,30 +80,35 @@ public PrivateDestinationDefinitionRead grantDestinationDefinitionToWorkspace(fi
}
@Post(uri = "/list")
+ @Secured({AUTHENTICATED_USER})
@Override
public DestinationDefinitionReadList listDestinationDefinitions() {
return ApiHelper.execute(destinationDefinitionsHandler::listDestinationDefinitions);
}
@Post(uri = "/list_for_workspace")
+ @Secured({READER})
@Override
public DestinationDefinitionReadList listDestinationDefinitionsForWorkspace(final WorkspaceIdRequestBody workspaceIdRequestBody) {
return ApiHelper.execute(() -> destinationDefinitionsHandler.listDestinationDefinitionsForWorkspace(workspaceIdRequestBody));
}
@Post(uri = "/list_latest")
+ @Secured({AUTHENTICATED_USER})
@Override
public DestinationDefinitionReadList listLatestDestinationDefinitions() {
return ApiHelper.execute(destinationDefinitionsHandler::listLatestDestinationDefinitions);
}
@Post(uri = "/list_private")
+ @Secured({ADMIN})
@Override
public PrivateDestinationDefinitionReadList listPrivateDestinationDefinitions(final WorkspaceIdRequestBody workspaceIdRequestBody) {
return ApiHelper.execute(() -> destinationDefinitionsHandler.listPrivateDestinationDefinitions(workspaceIdRequestBody));
}
@Post(uri = "/revoke_definition")
+ @Secured({ADMIN})
@Override
public void revokeDestinationDefinitionFromWorkspace(final DestinationDefinitionIdWithWorkspaceId destinationDefinitionIdWithWorkspaceId) {
ApiHelper.execute(() -> {
@@ -100,6 +118,7 @@ public void revokeDestinationDefinitionFromWorkspace(final DestinationDefinition
}
@Post(uri = "/update")
+ @Secured({AUTHENTICATED_USER})
@Override
public DestinationDefinitionRead updateDestinationDefinition(final DestinationDefinitionUpdate destinationDefinitionUpdate) {
return ApiHelper.execute(() -> destinationDefinitionsHandler.updateDestinationDefinition(destinationDefinitionUpdate));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationDefinitionSpecificationApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationDefinitionSpecificationApiController.java
index 54492c4a905f..d93bc6dcbe99 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationDefinitionSpecificationApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationDefinitionSpecificationApiController.java
@@ -4,6 +4,8 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.AUTHENTICATED_USER;
+
import io.airbyte.api.generated.DestinationDefinitionSpecificationApi;
import io.airbyte.api.model.generated.DestinationDefinitionIdWithWorkspaceId;
import io.airbyte.api.model.generated.DestinationDefinitionSpecificationRead;
@@ -11,10 +13,13 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/destination_definition_specifications")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class DestinationDefinitionSpecificationApiController implements DestinationDefinitionSpecificationApi {
private final SchedulerHandler schedulerHandler;
@@ -24,6 +29,7 @@ public DestinationDefinitionSpecificationApiController(final SchedulerHandler sc
}
@Post("/get")
+ @Secured({AUTHENTICATED_USER})
@Override
public DestinationDefinitionSpecificationRead getDestinationDefinitionSpecification(final DestinationDefinitionIdWithWorkspaceId destinationDefinitionIdWithWorkspaceId) {
return ApiHelper.execute(() -> schedulerHandler.getDestinationSpecification(destinationDefinitionIdWithWorkspaceId));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationOauthApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationOauthApiController.java
index 33318e14efcb..58286dfacd48 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationOauthApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/DestinationOauthApiController.java
@@ -4,6 +4,9 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.ADMIN;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+
import io.airbyte.api.generated.DestinationOauthApi;
import io.airbyte.api.model.generated.CompleteDestinationOAuthRequest;
import io.airbyte.api.model.generated.DestinationOauthConsentRequest;
@@ -14,12 +17,15 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
import java.util.Map;
@Controller("/api/v1/destination_oauths")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
@Context
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class DestinationOauthApiController implements DestinationOauthApi {
private final OAuthHandler oAuthHandler;
@@ -29,18 +35,21 @@ public DestinationOauthApiController(final OAuthHandler oAuthHandler) {
}
@Post("/complete_oauth")
+ @Secured({EDITOR})
@Override
public Map completeDestinationOAuth(final CompleteDestinationOAuthRequest completeDestinationOAuthRequest) {
return ApiHelper.execute(() -> oAuthHandler.completeDestinationOAuth(completeDestinationOAuthRequest));
}
@Post("/get_consent_url")
+ @Secured({EDITOR})
@Override
public OAuthConsentRead getDestinationOAuthConsent(final DestinationOauthConsentRequest destinationOauthConsentRequest) {
return ApiHelper.execute(() -> oAuthHandler.getDestinationOAuthConsent(destinationOauthConsentRequest));
}
@Post("/oauth_params/create")
+ @Secured({ADMIN})
@Override
public void setInstancewideDestinationOauthParams(final SetInstancewideDestinationOauthParamsRequestBody setInstancewideDestinationOauthParamsRequestBody) {
ApiHelper.execute(() -> {
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/HealthApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/HealthApiController.java
index a5b46d39742b..3ffb6851cbc5 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/HealthApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/HealthApiController.java
@@ -11,10 +11,13 @@
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/health")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_ANONYMOUS)
public class HealthApiController implements HealthApi {
private final HealthCheckHandler healthCheckHandler;
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/JobsApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/JobsApiController.java
index 9fadce045d05..b930472e5d23 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/JobsApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/JobsApiController.java
@@ -4,6 +4,10 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.ADMIN;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+import static io.airbyte.commons.auth.AuthRoleConstants.READER;
+
import io.airbyte.api.generated.JobsApi;
import io.airbyte.api.model.generated.AttemptNormalizationStatusReadList;
import io.airbyte.api.model.generated.ConnectionIdRequestBody;
@@ -20,11 +24,14 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/jobs")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
@Context
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class JobsApiController implements JobsApi {
private final JobHistoryHandler jobHistoryHandler;
@@ -36,42 +43,49 @@ public JobsApiController(final JobHistoryHandler jobHistoryHandler, final Schedu
}
@Post("/cancel")
+ @Secured({EDITOR})
@Override
public JobInfoRead cancelJob(final JobIdRequestBody jobIdRequestBody) {
return ApiHelper.execute(() -> schedulerHandler.cancelJob(jobIdRequestBody));
}
@Post("/get_normalization_status")
+ @Secured({ADMIN})
@Override
public AttemptNormalizationStatusReadList getAttemptNormalizationStatusesForJob(final JobIdRequestBody jobIdRequestBody) {
return ApiHelper.execute(() -> jobHistoryHandler.getAttemptNormalizationStatuses(jobIdRequestBody));
}
@Post("/get_debug_info")
+ @Secured({READER})
@Override
public JobDebugInfoRead getJobDebugInfo(final JobIdRequestBody jobIdRequestBody) {
return ApiHelper.execute(() -> jobHistoryHandler.getJobDebugInfo(jobIdRequestBody));
}
@Post("/get")
+ @Secured({READER})
@Override
public JobInfoRead getJobInfo(final JobIdRequestBody jobIdRequestBody) {
return ApiHelper.execute(() -> jobHistoryHandler.getJobInfo(jobIdRequestBody));
}
@Post("/get_light")
+ @Secured({READER})
@Override
public JobInfoLightRead getJobInfoLight(final JobIdRequestBody jobIdRequestBody) {
return ApiHelper.execute(() -> jobHistoryHandler.getJobInfoLight(jobIdRequestBody));
}
@Post("/get_last_replication_job")
+ @Secured({READER})
@Override
public JobOptionalRead getLastReplicationJob(final ConnectionIdRequestBody connectionIdRequestBody) {
return ApiHelper.execute(() -> jobHistoryHandler.getLastReplicationJob(connectionIdRequestBody));
}
@Post("/list")
+ @Secured({READER})
@Override
public JobReadList listJobsFor(final JobListRequestBody jobListRequestBody) {
return ApiHelper.execute(() -> jobHistoryHandler.listJobsFor(jobListRequestBody));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/LogsApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/LogsApiController.java
index 126ed8202693..13257412cf04 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/LogsApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/LogsApiController.java
@@ -4,6 +4,8 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.ADMIN;
+
import io.airbyte.api.generated.LogsApi;
import io.airbyte.api.model.generated.LogsRequestBody;
import io.airbyte.server.handlers.LogsHandler;
@@ -11,12 +13,15 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
import java.io.File;
@Controller("/api/v1/logs")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
@Context
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class LogsApiController implements LogsApi {
private final LogsHandler logsHandler;
@@ -26,6 +31,7 @@ public LogsApiController(final LogsHandler logsHandler) {
}
@Post("/get")
+ @Secured({ADMIN})
@Override
public File getLogs(final LogsRequestBody logsRequestBody) {
return ApiHelper.execute(() -> logsHandler.getLogs(logsRequestBody));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/NotFoundController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/NotFoundController.java
index a59dea440b05..0a089a21b762 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/NotFoundController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/NotFoundController.java
@@ -11,6 +11,8 @@
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Error;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
/**
* Custom controller that handles global 404 responses for unknown/unmapped paths.
@@ -18,6 +20,7 @@
@Controller("/api/notfound")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_ANONYMOUS)
public class NotFoundController {
@Error(status = HttpStatus.NOT_FOUND,
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/NotificationsApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/NotificationsApiController.java
index 6af6a1cdf242..c81799b9ac92 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/NotificationsApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/NotificationsApiController.java
@@ -4,6 +4,8 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.AUTHENTICATED_USER;
+
import io.airbyte.api.generated.NotificationsApi;
import io.airbyte.api.model.generated.Notification;
import io.airbyte.api.model.generated.NotificationRead;
@@ -12,10 +14,13 @@
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/notifications/try")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class NotificationsApiController implements NotificationsApi {
private final WorkspacesHandler workspacesHandler;
@@ -25,6 +30,7 @@ public NotificationsApiController(final WorkspacesHandler workspacesHandler) {
}
@Post
+ @Secured({AUTHENTICATED_USER})
@Override
public NotificationRead tryNotificationConfig(@Body final Notification notification) {
return ApiHelper.execute(() -> workspacesHandler.tryNotification(notification));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/OpenapiApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/OpenapiApiController.java
index 3aa984d7b2c7..5e574a23deb9 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/OpenapiApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/OpenapiApiController.java
@@ -4,16 +4,21 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.AUTHENTICATED_USER;
+
import io.airbyte.api.generated.OpenapiApi;
import io.airbyte.server.handlers.OpenApiConfigHandler;
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
import java.io.File;
@Controller("/api/v1/openapi")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class OpenapiApiController implements OpenapiApi {
private final OpenApiConfigHandler openApiConfigHandler;
@@ -23,6 +28,7 @@ public OpenapiApiController(final OpenApiConfigHandler openApiConfigHandler) {
}
@Get(produces = "text/plain")
+ @Secured({AUTHENTICATED_USER})
@Override
public File getOpenApiSpec() {
return ApiHelper.execute(openApiConfigHandler::getFile);
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/OperationApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/OperationApiController.java
index a892ca03d47b..172fbb76e40a 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/OperationApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/OperationApiController.java
@@ -4,6 +4,10 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.AUTHENTICATED_USER;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+import static io.airbyte.commons.auth.AuthRoleConstants.READER;
+
import io.airbyte.api.generated.OperationApi;
import io.airbyte.api.model.generated.CheckOperationRead;
import io.airbyte.api.model.generated.ConnectionIdRequestBody;
@@ -18,10 +22,13 @@
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/operations")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class OperationApiController implements OperationApi {
private final OperationsHandler operationsHandler;
@@ -31,6 +38,7 @@ public OperationApiController(final OperationsHandler operationsHandler) {
}
@Post("/check")
+ @Secured({AUTHENTICATED_USER})
@Override
public CheckOperationRead checkOperation(@Body final OperatorConfiguration operatorConfiguration) {
return ApiHelper.execute(() -> operationsHandler.checkOperation(operatorConfiguration));
@@ -38,11 +46,13 @@ public CheckOperationRead checkOperation(@Body final OperatorConfiguration opera
@Post("/create")
@Override
+ @Secured({EDITOR})
public OperationRead createOperation(@Body final OperationCreate operationCreate) {
return ApiHelper.execute(() -> operationsHandler.createOperation(operationCreate));
}
@Post("/delete")
+ @Secured({EDITOR})
@Override
public void deleteOperation(@Body final OperationIdRequestBody operationIdRequestBody) {
ApiHelper.execute(() -> {
@@ -52,18 +62,21 @@ public void deleteOperation(@Body final OperationIdRequestBody operationIdReques
}
@Post("/get")
+ @Secured({READER})
@Override
public OperationRead getOperation(@Body final OperationIdRequestBody operationIdRequestBody) {
return ApiHelper.execute(() -> operationsHandler.getOperation(operationIdRequestBody));
}
@Post("/list")
+ @Secured({READER})
@Override
public OperationReadList listOperationsForConnection(@Body final ConnectionIdRequestBody connectionIdRequestBody) {
return ApiHelper.execute(() -> operationsHandler.listOperationsForConnection(connectionIdRequestBody));
}
@Post("/update")
+ @Secured({EDITOR})
@Override
public OperationRead updateOperation(@Body final OperationUpdate operationUpdate) {
return ApiHelper.execute(() -> operationsHandler.updateOperation(operationUpdate));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/SchedulerApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/SchedulerApiController.java
index 53019774aeca..365dc04afc46 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/SchedulerApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/SchedulerApiController.java
@@ -4,6 +4,9 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.AUTHENTICATED_USER;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+
import io.airbyte.api.generated.SchedulerApi;
import io.airbyte.api.model.generated.CheckConnectionRead;
import io.airbyte.api.model.generated.DestinationCoreConfig;
@@ -13,10 +16,13 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/scheduler")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class SchedulerApiController implements SchedulerApi {
private final SchedulerHandler schedulerHandler;
@@ -26,18 +32,21 @@ public SchedulerApiController(final SchedulerHandler schedulerHandler) {
}
@Post("/destinations/check_connection")
+ @Secured({AUTHENTICATED_USER})
@Override
public CheckConnectionRead executeDestinationCheckConnection(final DestinationCoreConfig destinationCoreConfig) {
return ApiHelper.execute(() -> schedulerHandler.checkDestinationConnectionFromDestinationCreate(destinationCoreConfig));
}
@Post("/sources/check_connection")
+ @Secured({AUTHENTICATED_USER})
@Override
public CheckConnectionRead executeSourceCheckConnection(final SourceCoreConfig sourceCoreConfig) {
return ApiHelper.execute(() -> schedulerHandler.checkSourceConnectionFromSourceCreate(sourceCoreConfig));
}
@Post("/sources/discover_schema")
+ @Secured({EDITOR})
@Override
public SourceDiscoverSchemaRead executeSourceDiscoverSchema(final SourceCoreConfig sourceCoreConfig) {
return ApiHelper.execute(() -> schedulerHandler.discoverSchemaForSourceFromSourceCreate(sourceCoreConfig));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/SourceApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/SourceApiController.java
index 64cf7c58dfd6..0c680f742989 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/SourceApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/SourceApiController.java
@@ -4,6 +4,9 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+import static io.airbyte.commons.auth.AuthRoleConstants.READER;
+
import io.airbyte.api.generated.SourceApi;
import io.airbyte.api.model.generated.ActorCatalogWithUpdatedAt;
import io.airbyte.api.model.generated.CheckConnectionRead;
@@ -22,10 +25,13 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/sources")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class SourceApiController implements SourceApi {
private final SchedulerHandler schedulerHandler;
@@ -37,12 +43,14 @@ public SourceApiController(final SchedulerHandler schedulerHandler, final Source
}
@Post("/check_connection")
+ @Secured({EDITOR})
@Override
public CheckConnectionRead checkConnectionToSource(final SourceIdRequestBody sourceIdRequestBody) {
return ApiHelper.execute(() -> schedulerHandler.checkSourceConnectionFromSourceId(sourceIdRequestBody));
}
@Post("/check_connection_for_update")
+ @Secured({EDITOR})
@Override
public CheckConnectionRead checkConnectionToSourceForUpdate(final SourceUpdate sourceUpdate) {
return ApiHelper.execute(() -> schedulerHandler.checkSourceConnectionFromSourceIdForUpdate(sourceUpdate));
@@ -55,12 +63,14 @@ public SourceRead cloneSource(final SourceCloneRequestBody sourceCloneRequestBod
}
@Post("/create")
+ @Secured({EDITOR})
@Override
public SourceRead createSource(final SourceCreate sourceCreate) {
return ApiHelper.execute(() -> sourceHandler.createSource(sourceCreate));
}
@Post("/delete")
+ @Secured({EDITOR})
@Override
public void deleteSource(final SourceIdRequestBody sourceIdRequestBody) {
ApiHelper.execute(() -> {
@@ -70,24 +80,28 @@ public void deleteSource(final SourceIdRequestBody sourceIdRequestBody) {
}
@Post("/discover_schema")
+ @Secured({EDITOR})
@Override
public SourceDiscoverSchemaRead discoverSchemaForSource(final SourceDiscoverSchemaRequestBody sourceDiscoverSchemaRequestBody) {
return ApiHelper.execute(() -> schedulerHandler.discoverSchemaForSourceFromSourceId(sourceDiscoverSchemaRequestBody));
}
@Post("/get")
+ @Secured({READER})
@Override
public SourceRead getSource(final SourceIdRequestBody sourceIdRequestBody) {
return ApiHelper.execute(() -> sourceHandler.getSource(sourceIdRequestBody));
}
@Post("/most_recent_source_actor_catalog")
+ @Secured({READER})
@Override
public ActorCatalogWithUpdatedAt getMostRecentSourceActorCatalog(final SourceIdRequestBody sourceIdRequestBody) {
return ApiHelper.execute(() -> sourceHandler.getMostRecentSourceActorCatalogWithUpdatedAt(sourceIdRequestBody));
}
@Post("/list")
+ @Secured({READER})
@Override
public SourceReadList listSourcesForWorkspace(final WorkspaceIdRequestBody workspaceIdRequestBody) {
return ApiHelper.execute(() -> sourceHandler.listSourcesForWorkspace(workspaceIdRequestBody));
@@ -100,6 +114,7 @@ public SourceReadList searchSources(final SourceSearch sourceSearch) {
}
@Post("/update")
+ @Secured({EDITOR})
@Override
public SourceRead updateSource(final SourceUpdate sourceUpdate) {
return ApiHelper.execute(() -> sourceHandler.updateSource(sourceUpdate));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/SourceDefinitionApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/SourceDefinitionApiController.java
index 71222c7873c2..97a030dc7f70 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/SourceDefinitionApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/SourceDefinitionApiController.java
@@ -4,6 +4,11 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.ADMIN;
+import static io.airbyte.commons.auth.AuthRoleConstants.AUTHENTICATED_USER;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+import static io.airbyte.commons.auth.AuthRoleConstants.READER;
+
import io.airbyte.api.generated.SourceDefinitionApi;
import io.airbyte.api.model.generated.CustomSourceDefinitionCreate;
import io.airbyte.api.model.generated.PrivateSourceDefinitionRead;
@@ -19,11 +24,14 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/source_definitions")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
@Context
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class SourceDefinitionApiController implements SourceDefinitionApi {
private final SourceDefinitionsHandler sourceDefinitionsHandler;
@@ -33,12 +41,14 @@ public SourceDefinitionApiController(final SourceDefinitionsHandler sourceDefini
}
@Post("/create_custom")
+ @Secured({EDITOR})
@Override
public SourceDefinitionRead createCustomSourceDefinition(final CustomSourceDefinitionCreate customSourceDefinitionCreate) {
return ApiHelper.execute(() -> sourceDefinitionsHandler.createCustomSourceDefinition(customSourceDefinitionCreate));
}
@Post("/delete")
+ @Secured({ADMIN})
@Override
public void deleteSourceDefinition(final SourceDefinitionIdRequestBody sourceDefinitionIdRequestBody) {
ApiHelper.execute(() -> {
@@ -48,48 +58,56 @@ public void deleteSourceDefinition(final SourceDefinitionIdRequestBody sourceDef
}
@Post("/get")
+ @Secured({AUTHENTICATED_USER})
@Override
public SourceDefinitionRead getSourceDefinition(final SourceDefinitionIdRequestBody sourceDefinitionIdRequestBody) {
return ApiHelper.execute(() -> sourceDefinitionsHandler.getSourceDefinition(sourceDefinitionIdRequestBody));
}
@Post("/get_for_workspace")
+ @Secured({READER})
@Override
public SourceDefinitionRead getSourceDefinitionForWorkspace(final SourceDefinitionIdWithWorkspaceId sourceDefinitionIdWithWorkspaceId) {
return ApiHelper.execute(() -> sourceDefinitionsHandler.getSourceDefinitionForWorkspace(sourceDefinitionIdWithWorkspaceId));
}
@Post("/grant_definition")
+ @Secured({ADMIN})
@Override
public PrivateSourceDefinitionRead grantSourceDefinitionToWorkspace(final SourceDefinitionIdWithWorkspaceId sourceDefinitionIdWithWorkspaceId) {
return ApiHelper.execute(() -> sourceDefinitionsHandler.grantSourceDefinitionToWorkspace(sourceDefinitionIdWithWorkspaceId));
}
@Post("/list_latest")
+ @Secured({AUTHENTICATED_USER})
@Override
public SourceDefinitionReadList listLatestSourceDefinitions() {
return ApiHelper.execute(sourceDefinitionsHandler::listLatestSourceDefinitions);
}
@Post("/list_private")
+ @Secured({ADMIN})
@Override
public PrivateSourceDefinitionReadList listPrivateSourceDefinitions(final WorkspaceIdRequestBody workspaceIdRequestBody) {
return ApiHelper.execute(() -> sourceDefinitionsHandler.listPrivateSourceDefinitions(workspaceIdRequestBody));
}
@Post("/list")
+ @Secured({AUTHENTICATED_USER})
@Override
public SourceDefinitionReadList listSourceDefinitions() {
return ApiHelper.execute(sourceDefinitionsHandler::listSourceDefinitions);
}
@Post("/list_for_workspace")
+ @Secured({READER})
@Override
public SourceDefinitionReadList listSourceDefinitionsForWorkspace(final WorkspaceIdRequestBody workspaceIdRequestBody) {
return ApiHelper.execute(() -> sourceDefinitionsHandler.listSourceDefinitionsForWorkspace(workspaceIdRequestBody));
}
@Post("/revoke_definition")
+ @Secured({ADMIN})
@Override
public void revokeSourceDefinitionFromWorkspace(final SourceDefinitionIdWithWorkspaceId sourceDefinitionIdWithWorkspaceId) {
ApiHelper.execute(() -> {
@@ -99,6 +117,7 @@ public void revokeSourceDefinitionFromWorkspace(final SourceDefinitionIdWithWork
}
@Post("/update")
+ @Secured({AUTHENTICATED_USER})
@Override
public SourceDefinitionRead updateSourceDefinition(final SourceDefinitionUpdate sourceDefinitionUpdate) {
return ApiHelper.execute(() -> sourceDefinitionsHandler.updateSourceDefinition(sourceDefinitionUpdate));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/SourceDefinitionSpecificationApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/SourceDefinitionSpecificationApiController.java
index 590f7340b61b..e87b5c28bddc 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/SourceDefinitionSpecificationApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/SourceDefinitionSpecificationApiController.java
@@ -4,6 +4,8 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.AUTHENTICATED_USER;
+
import io.airbyte.api.generated.SourceDefinitionSpecificationApi;
import io.airbyte.api.model.generated.SourceDefinitionIdWithWorkspaceId;
import io.airbyte.api.model.generated.SourceDefinitionSpecificationRead;
@@ -11,10 +13,13 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/source_definition_specifications")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class SourceDefinitionSpecificationApiController implements SourceDefinitionSpecificationApi {
private final SchedulerHandler schedulerHandler;
@@ -24,6 +29,7 @@ public SourceDefinitionSpecificationApiController(final SchedulerHandler schedul
}
@Post("/get")
+ @Secured({AUTHENTICATED_USER})
@Override
public SourceDefinitionSpecificationRead getSourceDefinitionSpecification(final SourceDefinitionIdWithWorkspaceId sourceDefinitionIdWithWorkspaceId) {
return ApiHelper.execute(() -> schedulerHandler.getSourceDefinitionSpecification(sourceDefinitionIdWithWorkspaceId));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/SourceOauthApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/SourceOauthApiController.java
index 2bba632cf638..52cbc82beb97 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/SourceOauthApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/SourceOauthApiController.java
@@ -4,6 +4,9 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.ADMIN;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+
import io.airbyte.api.generated.SourceOauthApi;
import io.airbyte.api.model.generated.CompleteSourceOauthRequest;
import io.airbyte.api.model.generated.OAuthConsentRead;
@@ -14,11 +17,14 @@
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
import java.util.Map;
@Controller("/api/v1/source_oauths")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class SourceOauthApiController implements SourceOauthApi {
private final OAuthHandler oAuthHandler;
@@ -28,18 +34,21 @@ public SourceOauthApiController(final OAuthHandler oAuthHandler) {
}
@Post("/complete_oauth")
+ @Secured({EDITOR})
@Override
public Map completeSourceOAuth(@Body final CompleteSourceOauthRequest completeSourceOauthRequest) {
return ApiHelper.execute(() -> oAuthHandler.completeSourceOAuth(completeSourceOauthRequest));
}
@Post("/get_consent_url")
+ @Secured({EDITOR})
@Override
public OAuthConsentRead getSourceOAuthConsent(@Body final SourceOauthConsentRequest sourceOauthConsentRequest) {
return ApiHelper.execute(() -> oAuthHandler.getSourceOAuthConsent(sourceOauthConsentRequest));
}
@Post("/oauth_params/create")
+ @Secured({ADMIN})
@Override
public void setInstancewideSourceOauthParams(@Body final SetInstancewideSourceOauthParamsRequestBody setInstancewideSourceOauthParamsRequestBody) {
ApiHelper.execute(() -> {
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/StateApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/StateApiController.java
index b1b62373aaa1..870a499d5999 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/StateApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/StateApiController.java
@@ -4,6 +4,9 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.ADMIN;
+import static io.airbyte.commons.auth.AuthRoleConstants.READER;
+
import io.airbyte.api.generated.StateApi;
import io.airbyte.api.model.generated.ConnectionIdRequestBody;
import io.airbyte.api.model.generated.ConnectionState;
@@ -12,10 +15,13 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/state")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class StateApiController implements StateApi {
private final StateHandler stateHandler;
@@ -25,12 +31,14 @@ public StateApiController(final StateHandler stateHandler) {
}
@Post("/create_or_update")
+ @Secured({ADMIN})
@Override
public ConnectionState createOrUpdateState(final ConnectionStateCreateOrUpdate connectionStateCreateOrUpdate) {
return ApiHelper.execute(() -> stateHandler.createOrUpdateState(connectionStateCreateOrUpdate));
}
@Post("/get")
+ @Secured({READER})
@Override
public ConnectionState getState(final ConnectionIdRequestBody connectionIdRequestBody) {
return ApiHelper.execute(() -> stateHandler.getState(connectionIdRequestBody));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/WebBackendApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/WebBackendApiController.java
index ac13e02015c1..af8c5a3ad316 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/WebBackendApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/WebBackendApiController.java
@@ -4,6 +4,10 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.AUTHENTICATED_USER;
+import static io.airbyte.commons.auth.AuthRoleConstants.EDITOR;
+import static io.airbyte.commons.auth.AuthRoleConstants.READER;
+
import io.airbyte.api.generated.WebBackendApi;
import io.airbyte.api.model.generated.ConnectionIdRequestBody;
import io.airbyte.api.model.generated.ConnectionStateType;
@@ -23,10 +27,13 @@
import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/web_backend")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
public class WebBackendApiController implements WebBackendApi {
private final WebBackendConnectionsHandler webBackendConnectionsHandler;
@@ -42,48 +49,56 @@ public WebBackendApiController(final WebBackendConnectionsHandler webBackendConn
}
@Post("/state/get_type")
+ @Secured({READER})
@Override
public ConnectionStateType getStateType(final ConnectionIdRequestBody connectionIdRequestBody) {
return ApiHelper.execute(() -> webBackendConnectionsHandler.getStateType(connectionIdRequestBody));
}
@Post("/check_updates")
+ @Secured({READER})
@Override
public WebBackendCheckUpdatesRead webBackendCheckUpdates() {
return ApiHelper.execute(webBackendCheckUpdatesHandler::checkUpdates);
}
@Post("/connections/create")
+ @Secured({EDITOR})
@Override
public WebBackendConnectionRead webBackendCreateConnection(final WebBackendConnectionCreate webBackendConnectionCreate) {
return ApiHelper.execute(() -> webBackendConnectionsHandler.webBackendCreateConnection(webBackendConnectionCreate));
}
@Post("/connections/get")
+ @Secured({READER})
@Override
public WebBackendConnectionRead webBackendGetConnection(final WebBackendConnectionRequestBody webBackendConnectionRequestBody) {
return ApiHelper.execute(() -> webBackendConnectionsHandler.webBackendGetConnection(webBackendConnectionRequestBody));
}
@Post("/workspace/state")
+ @Secured({READER})
@Override
public WebBackendWorkspaceStateResult webBackendGetWorkspaceState(final WebBackendWorkspaceState webBackendWorkspaceState) {
return ApiHelper.execute(() -> webBackendConnectionsHandler.getWorkspaceState(webBackendWorkspaceState));
}
@Post("/connections/list")
+ @Secured({READER})
@Override
public WebBackendConnectionReadList webBackendListConnectionsForWorkspace(final WebBackendConnectionListRequestBody webBackendConnectionListRequestBody) {
return ApiHelper.execute(() -> webBackendConnectionsHandler.webBackendListConnectionsForWorkspace(webBackendConnectionListRequestBody));
}
@Post("/geographies/list")
+ @Secured({AUTHENTICATED_USER})
@Override
public WebBackendGeographiesListResult webBackendListGeographies() {
return ApiHelper.execute(webBackendGeographiesHandler::listGeographiesOSS);
}
@Post("/connections/update")
+ @Secured({EDITOR})
@Override
public WebBackendConnectionRead webBackendUpdateConnection(final WebBackendConnectionUpdate webBackendConnectionUpdate) {
return ApiHelper.execute(() -> webBackendConnectionsHandler.webBackendUpdateConnection(webBackendConnectionUpdate));
diff --git a/airbyte-server/src/main/java/io/airbyte/server/apis/WorkspaceApiController.java b/airbyte-server/src/main/java/io/airbyte/server/apis/WorkspaceApiController.java
index 14d3fdc90ad8..1942a4d03afb 100644
--- a/airbyte-server/src/main/java/io/airbyte/server/apis/WorkspaceApiController.java
+++ b/airbyte-server/src/main/java/io/airbyte/server/apis/WorkspaceApiController.java
@@ -4,6 +4,9 @@
package io.airbyte.server.apis;
+import static io.airbyte.commons.auth.AuthRoleConstants.AUTHENTICATED_USER;
+import static io.airbyte.commons.auth.AuthRoleConstants.OWNER;
+
import io.airbyte.api.generated.WorkspaceApi;
import io.airbyte.api.model.generated.ConnectionIdRequestBody;
import io.airbyte.api.model.generated.SlugRequestBody;
@@ -19,10 +22,14 @@
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
+import io.micronaut.security.annotation.Secured;
+import io.micronaut.security.rules.SecurityRule;
@Controller("/api/v1/workspaces")
@Requires(property = "airbyte.deployment-mode",
value = "OSS")
+@Secured(SecurityRule.IS_AUTHENTICATED)
+@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public class WorkspaceApiController implements WorkspaceApi {
private final WorkspacesHandler workspacesHandler;
@@ -32,12 +39,14 @@ public WorkspaceApiController(final WorkspacesHandler workspacesHandler) {
}
@Post("/create")
+ @Secured({AUTHENTICATED_USER})
@Override
public WorkspaceRead createWorkspace(@Body final WorkspaceCreate workspaceCreate) {
return ApiHelper.execute(() -> workspacesHandler.createWorkspace(workspaceCreate));
}
@Post("/delete")
+ @Secured({OWNER})
@Override
public void deleteWorkspace(@Body final WorkspaceIdRequestBody workspaceIdRequestBody) {
ApiHelper.execute(() -> {
@@ -47,30 +56,35 @@ public void deleteWorkspace(@Body final WorkspaceIdRequestBody workspaceIdReques
}
@Post("/get")
+ @Secured({OWNER})
@Override
public WorkspaceRead getWorkspace(@Body final WorkspaceIdRequestBody workspaceIdRequestBody) {
return ApiHelper.execute(() -> workspacesHandler.getWorkspace(workspaceIdRequestBody));
}
@Post("/get_by_slug")
+ @Secured({OWNER})
@Override
public WorkspaceRead getWorkspaceBySlug(@Body final SlugRequestBody slugRequestBody) {
return ApiHelper.execute(() -> workspacesHandler.getWorkspaceBySlug(slugRequestBody));
}
@Post("/list")
+ @Secured({AUTHENTICATED_USER})
@Override
public WorkspaceReadList listWorkspaces() {
return ApiHelper.execute(workspacesHandler::listWorkspaces);
}
@Post("/update")
+ @Secured({OWNER})
@Override
public WorkspaceRead updateWorkspace(@Body final WorkspaceUpdate workspaceUpdate) {
return ApiHelper.execute(() -> workspacesHandler.updateWorkspace(workspaceUpdate));
}
@Post("/tag_feedback_status_as_done")
+ @Secured({OWNER})
@Override
public void updateWorkspaceFeedback(@Body final WorkspaceGiveFeedback workspaceGiveFeedback) {
ApiHelper.execute(() -> {
@@ -80,12 +94,14 @@ public void updateWorkspaceFeedback(@Body final WorkspaceGiveFeedback workspaceG
}
@Post("/update_name")
+ @Secured({OWNER})
@Override
public WorkspaceRead updateWorkspaceName(@Body final WorkspaceUpdateName workspaceUpdateName) {
return ApiHelper.execute(() -> workspacesHandler.updateWorkspaceName(workspaceUpdateName));
}
@Post("/get_by_connection_id")
+ @Secured({AUTHENTICATED_USER})
@Override
public WorkspaceRead getWorkspaceByConnectionId(@Body final ConnectionIdRequestBody connectionIdRequestBody) {
return ApiHelper.execute(() -> workspacesHandler.getWorkspaceByConnectionId(connectionIdRequestBody));
diff --git a/airbyte-server/src/main/resources/application.yml b/airbyte-server/src/main/resources/application.yml
index 53e670c2c523..1c58a63ef546 100644
--- a/airbyte-server/src/main/resources/application.yml
+++ b/airbyte-server/src/main/resources/application.yml
@@ -2,19 +2,8 @@ micronaut:
application:
name: airbyte-server
security:
- intercept-url-map:
- - pattern: /**
- httpMethod: GET
- access:
- - isAnonymous()
- - pattern: /**
- httpMethod: POST
- access:
- - isAnonymous()
- - pattern: /**
- httpMethod: HEAD
- access:
- - isAnonymous()
+ authentication-provider-strategy: ALL
+ enabled: ${API_AUTHORIZATION_ENABLED:false}
server:
port: 8001
cors:
@@ -118,6 +107,32 @@ datasources:
username: ${DATABASE_USER}
password: ${DATABASE_PASSWORD}
+endpoints:
+ beans:
+ enabled: true
+ sensitive: false
+ env:
+ enabled: true
+ sensitive: false
+ health:
+ enabled: true
+ sensitive: false
+ info:
+ enabled: true
+ sensitive: true
+ loggers:
+ enabled: true
+ sensitive: true
+ refresh:
+ enabled: false
+ sensitive: true
+ routes:
+ enabled: true
+ sensitive: false
+ threaddump:
+ enabled: true
+ sensitive: true
+
flyway:
enabled: true
datasources:
@@ -138,3 +153,10 @@ jooq:
jobs:
jackson-converter-enabled: true
sql-dialect: POSTGRES
+
+logger:
+ levels:
+ # Uncomment to help resolve issues with conditional beans
+ # io.micronaut.context.condition: DEBUG
+ # Uncomment to help resolve issues with security beans
+ # io.micronaut.security: DEBUG
diff --git a/deps.toml b/deps.toml
index f3a642d9bab4..0fa2bd639d69 100644
--- a/deps.toml
+++ b/deps.toml
@@ -24,6 +24,7 @@ micronaut = "3.8.1"
micronaut-test = "3.8.0"
platform-testcontainers = "1.17.3"
postgresql = "42.3.5"
+reactor = "3.5.2"
slf4j = "1.7.36"
temporal = "1.17.0"
@@ -101,6 +102,7 @@ platform-testcontainers-jdbc = { module = "org.testcontainers:jdbc", version.ref
platform-testcontainers-postgresql = { module = "org.testcontainers:postgresql", version.ref = "platform-testcontainers" }
postgresql = { module = "org.postgresql:postgresql", version.ref = "postgresql" }
quartz-scheduler = { module = "org.quartz-scheduler:quartz", version = "2.3.2" }
+reactor-core = { module = "io.projectreactor:reactor-core", version.ref = "reactor" }
s3 = { module = "software.amazon.awssdk:s3", version = "2.16.84" }
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
spotbugs-annotations = { module = "com.github.spotbugs:spotbugs-annotations", version = "4.7.3" }