Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove project-specific Pet Service Accounts #1539

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class SamProviderSpec
private def mockGetArbitraryPetServiceAccountToken(): IO[Unit] = for {
_ <- IO(
when {
googleExt.getArbitraryPetServiceAccountToken(any[SamUser], any[Set[String]], any[SamRequestContext])
googleExt.getSingletonPetServiceAccountToken(any[SamUser], any[Set[String]], any[SamRequestContext])
} thenReturn {
IO.pure("aToken")
}
Expand Down
33 changes: 17 additions & 16 deletions render_config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ ENV=${1:-dev}
SERVICE_OUTPUT_LOCATION="$(dirname "$0")/src/main/resources/rendered"
SECRET_ENV_VARS_LOCATION="${SERVICE_OUTPUT_LOCATION}/secrets.env"

gcloud container clusters get-credentials --zone us-central1-a --project broad-dsde-dev terra-dev
gcloud container clusters get-credentials --zone us-central1-a --project broad-dsde-"${ENV}" terra-"${ENV}"

kubectl -n terra-dev get secret sam-sa-secret -o 'go-template={{index .data "sam-account.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/sam-account.json
kubectl -n terra-dev get secret sam-sa-secret -o 'go-template={{index .data "sam-account.pem"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/sam-account.pem
kubectl -n terra-"${ENV}" get secret sam-sa-secret -o 'go-template={{index .data "sam-account.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/sam-account.json
kubectl -n terra-"${ENV}" get secret sam-sa-secret -o 'go-template={{index .data "sam-account.pem"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/sam-account.pem

kubectl -n terra-dev get secret admin-one-sa-secret -o 'go-template={{index .data "admin-service-account-1.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/admin-service-account-1.json
kubectl -n terra-dev get secret admin-two-sa-secret -o 'go-template={{index .data "admin-service-account-2.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/admin-service-account-2.json
kubectl -n terra-dev get secret admin-three-sa-secret -o 'go-template={{index .data "admin-service-account-3.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/admin-service-account-3.json
kubectl -n terra-dev get secret admin-four-sa-secret -o 'go-template={{index .data "admin-service-account-4.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/admin-service-account-4.json
kubectl -n terra-dev get secret admin-five-sa-secret -o 'go-template={{index .data "admin-service-account-5.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/admin-service-account-5.json
kubectl -n terra-"${ENV}" get secret admin-one-sa-secret -o 'go-template={{index .data "admin-service-account-1.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/admin-service-account-1.json
kubectl -n terra-"${ENV}" get secret admin-two-sa-secret -o 'go-template={{index .data "admin-service-account-2.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/admin-service-account-2.json
kubectl -n terra-"${ENV}" get secret admin-three-sa-secret -o 'go-template={{index .data "admin-service-account-3.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/admin-service-account-3.json
kubectl -n terra-"${ENV}" get secret admin-four-sa-secret -o 'go-template={{index .data "admin-service-account-4.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/admin-service-account-4.json
kubectl -n terra-"${ENV}" get secret admin-five-sa-secret -o 'go-template={{index .data "admin-service-account-5.json"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/admin-service-account-5.json

kubectl -n terra-dev get configmap sam-oauth2-configmap -o 'go-template={{index .data "oauth2-config"}}' > ${SERVICE_OUTPUT_LOCATION}/oauth2.conf
# Local dev uses a macOS-specific docker replacement hostname for locahost, so replace all instances in the proxy config.
kubectl -n terra-dev get configmap sam-proxy-configmap -o 'go-template={{index .data "apache-httpd-proxy-config"}}' | sed 's/localhost/host\.docker\.internal/g' > ${SERVICE_OUTPUT_LOCATION}/site.conf
kubectl -n terra-"${ENV}" get configmap sam-oauth2-configmap -o 'go-template={{index .data "oauth2-config"}}' > ${SERVICE_OUTPUT_LOCATION}/oauth2.conf
# Local dev uses a macOS-specific docker replacement hostname for localhost, so replace all instances in the proxy config.
kubectl -n terra-"${ENV}" get configmap sam-proxy-configmap -o 'go-template={{index .data "apache-httpd-proxy-config"}}' | sed 's/localhost/host\.docker\.internal/g' > ${SERVICE_OUTPUT_LOCATION}/site.conf

gcloud container clusters get-credentials --zone us-central1-a --project broad-dsde-dev terra-dev
kubectl -n local-dev get secrets local-dev-cert -o 'go-template={{index .data "tls.crt"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/server.crt
kubectl -n local-dev get secrets local-dev-cert -o 'go-template={{index .data "tls.key"}}' | base64 --decode > ${SERVICE_OUTPUT_LOCATION}/server.key

Expand All @@ -25,11 +26,11 @@ if [ -f "${SECRET_ENV_VARS_LOCATION}" ]; then
fi

{
echo export AZURE_MANAGED_APP_CLIENT_ID="$(vault read -field=client-id secret/dsde/terra/azure/dev/sam/managed-app-publisher)";
echo export AZURE_MANAGED_APP_CLIENT_SECRET="$(vault read -field=client-secret secret/dsde/terra/azure/dev/sam/managed-app-publisher)";
echo export AZURE_MANAGED_APP_TENANT_ID="$(vault read -field=tenant-id secret/dsde/terra/azure/dev/sam/managed-app-publisher)";
echo export LEGACY_GOOGLE_CLIENT_ID="$(vault read -format=json -field=data secret/dsde/firecloud/dev/common/refresh-token-oauth-credential.json | jq -r '.web.client_id')";
echo export OIDC_CLIENT_ID="$(vault read -field=value secret/dsde/terra/azure/dev/b2c/application_id)";
echo export AZURE_MANAGED_APP_CLIENT_ID="$(vault read -field=client-id secret/dsde/terra/azure/"${ENV}"/sam/managed-app-publisher)";
echo export AZURE_MANAGED_APP_CLIENT_SECRET="$(vault read -field=client-secret secret/dsde/terra/azure/"${ENV}"/sam/managed-app-publisher)";
echo export AZURE_MANAGED_APP_TENANT_ID="$(vault read -field=tenant-id secret/dsde/terra/azure/"${ENV}"/sam/managed-app-publisher)";
echo export LEGACY_GOOGLE_CLIENT_ID="$(vault read -format=json -field=data secret/dsde/firecloud/"${ENV}"/common/refresh-token-oauth-credential.json | jq -r '.web.client_id')";
echo export OIDC_CLIENT_ID="$(vault read -field=value secret/dsde/terra/azure/"${ENV}"/b2c/application_id)";

echo export SERVICE_ACCOUNT_CLIENT_EMAIL="$(cat ${SERVICE_OUTPUT_LOCATION}/sam-account.json | jq .client_email)";
echo export SERVICE_ACCOUNT_CLIENT_ID="$(cat ${SERVICE_OUTPUT_LOCATION}/sam-account.json | jq .client_id)";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@
<include file="changesets/20240701_add_group_version_and_last_synchronized_version.xml" relativeToChangelogFile="true"/>
<include file="changesets/20240809_sam_user_favorite_resources_table.xml" relativeToChangelogFile="true"/>
<include file="changesets/20240820_descendant_auth_domains.xml" relativeToChangelogFile="true"/>
<include file="changesets/20240911_sam_pet_service_agents_table.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog logicalFilePath="dummy"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">

<changeSet logicalFilePath="dummy" author="tlangs" id="add_sam_pet_service_agents_table">
<createTable tableName="SAM_PET_SERVICE_AGENTS">
<column name="sam_user_id" type="VARCHAR">
<constraints primaryKey="true"/>
</column>
<column name="pet_service_account_project" type="VARCHAR">
<constraints primaryKey="true"/>
</column>
<column name="destination_project" type="VARCHAR">
<constraints primaryKey="true"/>
</column>
<column name="destination_project_number" type="VARCHAR">
<constraints primaryKey="false" nullable="false"/>
</column>
<column name="service_agents" type="VARCHAR []">
<constraints primaryKey="false" nullable="false"/>
</column>
<column name="updated_at" type="timestamptz"/>
</createTable>

<addForeignKeyConstraint baseTableName="SAM_PET_SERVICE_AGENTS" baseColumnNames="SAM_USER_ID, PET_SERVICE_ACCOUNT_PROJECT" constraintName="FK_PSA_SERVICE_AGENT"
referencedTableName="SAM_PET_SERVICE_ACCOUNT"
referencedColumnNames="USER_ID, PROJECT" deleteCascade="true" onDelete="CASCADE"/>

</changeSet>
</databaseChangeLog>
2 changes: 2 additions & 0 deletions src/main/resources/sam.conf
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ googleServices {
samplingProbability = ${?OPENCENSUS_SAMPLING_PROBABILITY} # for backwards compatibility
samplingProbability = ${?GOOGLE_TRACE_SAMPLING_PROBABILITY}
}

serviceAgents = ${?SERVICE_AGENTS}
}

db {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,10 @@ trait AdminRoutes extends SecurityDirectives with SamRequestContextDirectives wi
val googleProject = GoogleProject(project)
deleteWithTelemetry(samRequestContext, userIdParam(workbenchUserId), googleProjectParam(googleProject)) {
complete {
cloudExtensions
.deleteUserPetServiceAccount(workbenchUserId, googleProject, samRequestContext)
.map(_ => NoContent)
for {
_ <- cloudExtensions.removeProjectServiceAgents(workbenchUserId, GoogleProject(project), samRequestContext)
_ <- cloudExtensions.deleteUserPetServiceAccount(workbenchUserId, GoogleProject(project), samRequestContext)
} yield StatusCodes.NoContent
}
}
}
Expand Down Expand Up @@ -161,40 +162,53 @@ trait AdminRoutes extends SecurityDirectives with SamRequestContextDirectives wi
}

def adminResourcesRoutes(user: SamUser, samRequestContext: SamRequestContext): server.Route =
pathPrefix("resources" / Segment / Segment / "policies") { case (resourceTypeName, resourceId) =>
pathPrefix("resources" / Segment / Segment) { case (resourceTypeName, resourceId) =>
withNonAdminResourceType(ResourceTypeName(resourceTypeName)) { resourceType =>
val resource = FullyQualifiedResourceId(resourceType.name, ResourceId(resourceId))
pathEndOrSingleSlash {
getWithTelemetry(samRequestContext, resourceParams(resource): _*) {
requireAdminResourceAction(adminReadPolicies, resourceType, user, samRequestContext) {
asWorkbenchAdmin(user) {
delete {
complete {
resourceService
.listResourcePolicies(resource, samRequestContext)
.map(response => OK -> response.toSet)
.deleteResource(resource, samRequestContext)
.map(_ => NoContent)
}
}
}
} ~
pathPrefix(Segment / "memberEmails" / Segment) { case (policyName, userEmail) =>
val policyId = FullyQualifiedPolicyId(resource, AccessPolicyName(policyName))
val workbenchEmail = WorkbenchEmail(userEmail)
pathPrefix("policies") {
pathEndOrSingleSlash {
withSubject(workbenchEmail, samRequestContext) { subject =>
putWithTelemetry(samRequestContext, policyParams(policyId).appended(emailParam(workbenchEmail)): _*) {
requireAdminResourceAction(adminAddMember, resourceType, user, samRequestContext) {
complete {
resourceService
.addSubjectToPolicy(policyId, subject, samRequestContext)
.as(NoContent)
}
getWithTelemetry(samRequestContext, resourceParams(resource): _*) {
requireAdminResourceAction(adminReadPolicies, resourceType, user, samRequestContext) {
complete {
resourceService
.listResourcePolicies(resource, samRequestContext)
.map(response => OK -> response.toSet)
}
} ~
deleteWithTelemetry(samRequestContext, policyParams(policyId).appended(emailParam(workbenchEmail)): _*) {
requireAdminResourceAction(adminRemoveMember, resourceType, user, samRequestContext) {
complete {
resourceService
.removeSubjectFromPolicy(policyId, subject, samRequestContext)
.as(NoContent)
}
}
} ~
pathPrefix(Segment / "memberEmails" / Segment) { case (policyName, userEmail) =>
val policyId = FullyQualifiedPolicyId(resource, AccessPolicyName(policyName))
val workbenchEmail = WorkbenchEmail(userEmail)
pathEndOrSingleSlash {
withSubject(workbenchEmail, samRequestContext) { subject =>
putWithTelemetry(samRequestContext, policyParams(policyId).appended(emailParam(workbenchEmail)): _*) {
requireAdminResourceAction(adminAddMember, resourceType, user, samRequestContext) {
complete {
resourceService
.addSubjectToPolicy(policyId, subject, samRequestContext)
.as(NoContent)
}
}
} ~
deleteWithTelemetry(samRequestContext, policyParams(policyId).appended(emailParam(workbenchEmail)): _*) {
requireAdminResourceAction(adminRemoveMember, resourceType, user, samRequestContext) {
complete {
resourceService
.removeSubjectFromPolicy(policyId, subject, samRequestContext)
.as(NoContent)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ final case class GoogleServicesConfig(
adminSdkServiceAccountPaths: Option[NonEmptyList[String]],
googleKms: GoogleKmsConfig,
terraGoogleOrgNumber: String,
traceExporter: TraceExporterConfig
traceExporter: TraceExporterConfig,
serviceAgents: Set[String]
)

object GoogleServicesConfig {
Expand Down Expand Up @@ -98,7 +99,8 @@ object GoogleServicesConfig {
config.as[Option[NonEmptyList[String]]]("adminSdkServiceAccountPaths"),
config.as[GoogleKmsConfig]("kms"),
config.getString("terraGoogleOrgNumber"),
config.as[TraceExporterConfig]("traceExporter")
config.as[TraceExporterConfig]("traceExporter"),
config.as[Option[String]]("serviceAgents").map(_.split(",").toSet).getOrElse(Set.empty)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package org.broadinstitute.dsde.workbench.sam.dataAccess

import cats.effect.IO
import org.broadinstitute.dsde.workbench.model._
import org.broadinstitute.dsde.workbench.model.google.ServiceAccountSubjectId
import org.broadinstitute.dsde.workbench.model.google.{GoogleProject, ServiceAccountSubjectId}
import org.broadinstitute.dsde.workbench.sam.azure.{
ActionManagedIdentity,
ActionManagedIdentityId,
Expand All @@ -12,7 +12,15 @@ import org.broadinstitute.dsde.workbench.sam.azure.{
PetManagedIdentityId
}
import org.broadinstitute.dsde.workbench.sam.model.api.{AdminUpdateUserRequest, SamUser, SamUserAttributes}
import org.broadinstitute.dsde.workbench.sam.model.{BasicWorkbenchGroup, FullyQualifiedResourceId, ResourceAction, ResourceTypeName, SamUserTos}
import org.broadinstitute.dsde.workbench.sam.model.{
BasicWorkbenchGroup,
FullyQualifiedResourceId,
PetServiceAgents,
ResourceAction,
ResourceTypeName,
SamUserTos,
ServiceAgent
}
import org.broadinstitute.dsde.workbench.sam.util.SamRequestContext

import java.time.Instant
Expand Down Expand Up @@ -190,4 +198,29 @@ trait DirectoryDAO {
resourceTypeName: ResourceTypeName,
samRequestContext: SamRequestContext
): IO[Set[FullyQualifiedResourceId]]

def getPetServiceAgents(
userId: WorkbenchUserId,
petServiceAccountProject: GoogleProject,
destinationProject: GoogleProject,
samRequestContext: SamRequestContext
): IO[Option[PetServiceAgents]]

def addPetServiceAgent(
userId: WorkbenchUserId,
petServiceAccountProject: GoogleProject,
destinationProject: GoogleProject,
destinationProjectNumber: Long,
serviceAgent: String,
samRequestContext: SamRequestContext
): IO[Set[ServiceAgent]]

def removePetServiceAgent(
userId: WorkbenchUserId,
petServiceAccountProject: GoogleProject,
destinationProject: GoogleProject,
destinationProjectNumber: Long,
serviceAgent: String,
samRequestContext: SamRequestContext
): IO[Set[ServiceAgent]]
}
Loading
Loading