Skip to content

feat: implement service accounts #442

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

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c2fa106
chore(ee/rbac): refresh protos
hamir-suspect Jul 18, 2025
f5f4408
chore(rbac/ce): refresh protos
hamir-suspect Jul 18, 2025
f051e6c
chore(guard): refresh protos and implement service account service
hamir-suspect Jul 18, 2025
f45d5a5
fix: add proper migration, start service account api
hamir-suspect Jul 18, 2025
4d11a11
fix: remove timestamps from service account schema
hamir-suspect Jul 18, 2025
f14534c
fix: failing tests
hamir-suspect Jul 21, 2025
687c797
fix: linting errors
hamir-suspect Jul 21, 2025
387a413
fix: start service account grpc api in tests
hamir-suspect Jul 21, 2025
02e90e2
fix: formatting and remove redundant transaction
hamir-suspect Jul 21, 2025
0494393
fix: set env var for base domain for synthetic service account email …
hamir-suspect Jul 22, 2025
444952c
fix: set correct subject type for rbac user
hamir-suspect Jul 22, 2025
e61ab39
chore(github_hooks): fix security vunerability
hamir-suspect Jul 22, 2025
857bfdc
fix(guard): typespec for subject type
hamir-suspect Jul 22, 2025
0e8ba56
fix(guard): passtrough argument number
hamir-suspect Jul 22, 2025
128dd07
Revert "chore(github_hooks): fix security vunerability"
hamir-suspect Jul 22, 2025
807488f
fix(guard): unused vars warning
hamir-suspect Jul 22, 2025
6fb7ebf
feat(rbac_ce): add service account subject type
hamir-suspect Jul 23, 2025
cf0e91c
fix: return created_at and updated_at from service account store
hamir-suspect Jul 23, 2025
109eda6
feat: add role_id to assign to the service account
hamir-suspect Jul 24, 2025
f63a839
fix: add migration for rbac ce subject type
hamir-suspect Jul 24, 2025
f83a985
fix: add role_id to pattern matching in create request
hamir-suspect Jul 24, 2025
657f14f
fix: map creation source to service_account
hamir-suspect Jul 25, 2025
4356393
fix: failing test
hamir-suspect Jul 29, 2025
f6e9868
chore(guard): refresh protos
hamir-suspect Jul 30, 2025
9494329
feat: deactivate and reactivate service account
hamir-suspect Jul 30, 2025
47ff0c7
feat: do not authenticate deactivated users, deactivate and reactivat…
hamir-suspect Jul 30, 2025
9fb3b71
fix: remove redundant comments
hamir-suspect Jul 30, 2025
a56d9f0
Merge branch 'main' into has/service-accounts
hamir-suspect Jul 31, 2025
c15da28
chore(guard): refresh protos
hamir-suspect Aug 1, 2025
8b1d24f
chore: refresh protos
hamir-suspect Aug 4, 2025
94afa9b
feat: add DescribeMany and remove role assignment from service_accoun…
hamir-suspect Aug 4, 2025
88c7867
fix: linting and credo warnings
hamir-suspect Aug 4, 2025
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
1 change: 1 addition & 0 deletions ee/rbac/lib/internal_api/audit.pb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ defmodule InternalApi.Audit.Event.Resource do
field(:Okta, 17)
field(:FlakyTests, 18)
field(:RBACRole, 19)
field(:ServiceAccount, 20)
end

defmodule InternalApi.Audit.Event.Operation do
Expand Down
22 changes: 22 additions & 0 deletions ee/rbac/lib/internal_api/organization.pb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ defmodule InternalApi.Organization.DescribeRequest do
field(:org_id, 1, type: :string, json_name: "orgId")
field(:org_username, 2, type: :string, json_name: "orgUsername")
field(:include_quotas, 3, type: :bool, json_name: "includeQuotas")
field(:soft_deleted, 4, type: :bool, json_name: "softDeleted")
end

defmodule InternalApi.Organization.DescribeResponse do
Expand All @@ -63,6 +64,7 @@ defmodule InternalApi.Organization.DescribeManyRequest do
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:org_ids, 1, repeated: true, type: :string, json_name: "orgIds")
field(:soft_deleted, 2, type: :bool, json_name: "softDeleted")
end

defmodule InternalApi.Organization.DescribeManyResponse do
Expand All @@ -83,6 +85,7 @@ defmodule InternalApi.Organization.ListRequest do
field(:order, 4, type: InternalApi.Organization.ListRequest.Order, enum: true)
field(:page_size, 5, type: :int32, json_name: "pageSize")
field(:page_token, 6, type: :string, json_name: "pageToken")
field(:soft_deleted, 7, type: :bool, json_name: "softDeleted")
end

defmodule InternalApi.Organization.ListResponse do
Expand Down Expand Up @@ -369,6 +372,14 @@ defmodule InternalApi.Organization.DestroyRequest do
field(:org_id, 1, type: :string, json_name: "orgId")
end

defmodule InternalApi.Organization.RestoreRequest do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:org_id, 1, type: :string, json_name: "orgId")
end

defmodule InternalApi.Organization.Organization do
@moduledoc false

Expand Down Expand Up @@ -620,6 +631,15 @@ defmodule InternalApi.Organization.OrganizationDailyUpdate do
field(:timestamp, 11, type: Google.Protobuf.Timestamp)
end

defmodule InternalApi.Organization.OrganizationRestored do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:org_id, 1, type: :string, json_name: "orgId")
field(:timestamp, 2, type: Google.Protobuf.Timestamp)
end

defmodule InternalApi.Organization.OrganizationService.Service do
@moduledoc false

Expand Down Expand Up @@ -701,6 +721,8 @@ defmodule InternalApi.Organization.OrganizationService.Service do

rpc(:Destroy, InternalApi.Organization.DestroyRequest, Google.Protobuf.Empty)

rpc(:Restore, InternalApi.Organization.RestoreRequest, Google.Protobuf.Empty)

rpc(
:RepositoryIntegrators,
InternalApi.Organization.RepositoryIntegratorsRequest,
Expand Down
59 changes: 59 additions & 0 deletions ee/rbac/lib/internal_api/projecthub.pb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ defmodule InternalApi.Projecthub.Project.Spec.Repository.RunType do
field(:TAGS, 1)
field(:PULL_REQUESTS, 2)
field(:FORKED_PULL_REQUESTS, 3)
field(:DRAFT_PULL_REQUESTS, 4)
end

defmodule InternalApi.Projecthub.Project.Spec.Repository.Status.PipelineFile.Level do
Expand Down Expand Up @@ -391,6 +392,7 @@ defmodule InternalApi.Projecthub.ListRequest do
field(:pagination, 2, type: InternalApi.Projecthub.PaginationRequest)
field(:owner_id, 3, type: :string, json_name: "ownerId")
field(:repo_url, 4, type: :string, json_name: "repoUrl")
field(:soft_deleted, 5, type: :bool, json_name: "softDeleted")
end

defmodule InternalApi.Projecthub.ListResponse do
Expand Down Expand Up @@ -437,6 +439,7 @@ defmodule InternalApi.Projecthub.DescribeRequest do
field(:id, 2, type: :string)
field(:name, 3, type: :string)
field(:detailed, 4, type: :bool)
field(:soft_deleted, 5, type: :bool, json_name: "softDeleted")
end

defmodule InternalApi.Projecthub.DescribeResponse do
Expand All @@ -455,6 +458,7 @@ defmodule InternalApi.Projecthub.DescribeManyRequest do

field(:metadata, 1, type: InternalApi.Projecthub.RequestMeta)
field(:ids, 2, repeated: true, type: :string)
field(:soft_deleted, 3, type: :bool, json_name: "softDeleted")
end

defmodule InternalApi.Projecthub.DescribeManyResponse do
Expand Down Expand Up @@ -522,6 +526,23 @@ defmodule InternalApi.Projecthub.DestroyResponse do
field(:metadata, 1, type: InternalApi.Projecthub.ResponseMeta)
end

defmodule InternalApi.Projecthub.RestoreRequest do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:metadata, 1, type: InternalApi.Projecthub.RequestMeta)
field(:id, 2, type: :string)
end

defmodule InternalApi.Projecthub.RestoreResponse do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:metadata, 1, type: InternalApi.Projecthub.ResponseMeta)
end

defmodule InternalApi.Projecthub.UsersRequest do
@moduledoc false

Expand Down Expand Up @@ -557,6 +578,7 @@ defmodule InternalApi.Projecthub.CheckDeployKeyResponse.DeployKey do
field(:title, 1, type: :string)
field(:fingerprint, 2, type: :string)
field(:created_at, 3, type: Google.Protobuf.Timestamp, json_name: "createdAt")
field(:public_key, 4, type: :string, json_name: "publicKey")
end

defmodule InternalApi.Projecthub.CheckDeployKeyResponse do
Expand Down Expand Up @@ -589,6 +611,7 @@ defmodule InternalApi.Projecthub.RegenerateDeployKeyResponse.DeployKey do
field(:title, 1, type: :string)
field(:fingerprint, 2, type: :string)
field(:created_at, 3, type: Google.Protobuf.Timestamp, json_name: "createdAt")
field(:public_key, 4, type: :string, json_name: "publicKey")
end

defmodule InternalApi.Projecthub.RegenerateDeployKeyResponse do
Expand Down Expand Up @@ -718,6 +741,24 @@ defmodule InternalApi.Projecthub.FinishOnboardingResponse do
field(:metadata, 1, type: InternalApi.Projecthub.ResponseMeta)
end

defmodule InternalApi.Projecthub.RegenerateWebhookSecretRequest do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:metadata, 1, type: InternalApi.Projecthub.RequestMeta)
field(:id, 2, type: :string)
end

defmodule InternalApi.Projecthub.RegenerateWebhookSecretResponse do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:metadata, 1, type: InternalApi.Projecthub.ResponseMeta)
field(:secret, 2, type: :string)
end

defmodule InternalApi.Projecthub.ProjectCreated do
@moduledoc false

Expand All @@ -738,6 +779,16 @@ defmodule InternalApi.Projecthub.ProjectDeleted do
field(:org_id, 3, type: :string, json_name: "orgId")
end

defmodule InternalApi.Projecthub.ProjectRestored do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:project_id, 1, type: :string, json_name: "projectId")
field(:timestamp, 2, type: Google.Protobuf.Timestamp)
field(:org_id, 3, type: :string, json_name: "orgId")
end

defmodule InternalApi.Projecthub.ProjectUpdated do
@moduledoc false

Expand Down Expand Up @@ -786,6 +837,8 @@ defmodule InternalApi.Projecthub.ProjectService.Service do

rpc(:Destroy, InternalApi.Projecthub.DestroyRequest, InternalApi.Projecthub.DestroyResponse)

rpc(:Restore, InternalApi.Projecthub.RestoreRequest, InternalApi.Projecthub.RestoreResponse)

rpc(:Users, InternalApi.Projecthub.UsersRequest, InternalApi.Projecthub.UsersResponse)

rpc(
Expand All @@ -812,6 +865,12 @@ defmodule InternalApi.Projecthub.ProjectService.Service do
InternalApi.Projecthub.RegenerateWebhookResponse
)

rpc(
:RegenerateWebhookSecret,
InternalApi.Projecthub.RegenerateWebhookSecretRequest,
InternalApi.Projecthub.RegenerateWebhookSecretResponse
)

rpc(
:ChangeProjectOwner,
InternalApi.Projecthub.ChangeProjectOwnerRequest,
Expand Down
1 change: 1 addition & 0 deletions ee/rbac/lib/internal_api/rbac.pb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule InternalApi.RBAC.SubjectType do

field(:USER, 0)
field(:GROUP, 1)
field(:SERVICE_ACCOUNT, 2)
end

defmodule InternalApi.RBAC.Scope do
Expand Down
46 changes: 46 additions & 0 deletions ee/rbac/lib/internal_api/repository.pb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ defmodule InternalApi.Repository.DeployKey do
field(:title, 1, type: :string)
field(:fingerprint, 2, type: :string)
field(:created_at, 3, type: Google.Protobuf.Timestamp, json_name: "createdAt")
field(:public_key, 4, type: :string, json_name: "publicKey")
end

defmodule InternalApi.Repository.DescribeRemoteRepositoryRequest do
Expand Down Expand Up @@ -370,6 +371,7 @@ defmodule InternalApi.Repository.Repository do
field(:whitelist, 11, type: InternalApi.Projecthub.Project.Spec.Repository.Whitelist)
field(:hook_id, 12, type: :string, json_name: "hookId")
field(:default_branch, 13, type: :string, json_name: "defaultBranch")
field(:connected, 14, type: :bool)
end

defmodule InternalApi.Repository.RemoteRepository do
Expand Down Expand Up @@ -630,6 +632,38 @@ defmodule InternalApi.Repository.VerifyWebhookSignatureResponse do
field(:valid, 1, type: :bool)
end

defmodule InternalApi.Repository.ClearExternalDataRequest do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:repository_id, 1, type: :string, json_name: "repositoryId")
end

defmodule InternalApi.Repository.ClearExternalDataResponse do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:repository, 1, type: InternalApi.Repository.Repository)
end

defmodule InternalApi.Repository.RegenerateWebhookSecretRequest do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:repository_id, 1, type: :string, json_name: "repositoryId")
end

defmodule InternalApi.Repository.RegenerateWebhookSecretResponse do
@moduledoc false

use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"

field(:secret, 1, type: :string)
end

defmodule InternalApi.Repository.RepositoryService.Service do
@moduledoc false

Expand Down Expand Up @@ -732,6 +766,18 @@ defmodule InternalApi.Repository.RepositoryService.Service do
InternalApi.Repository.VerifyWebhookSignatureRequest,
InternalApi.Repository.VerifyWebhookSignatureResponse
)

rpc(
:ClearExternalData,
InternalApi.Repository.ClearExternalDataRequest,
InternalApi.Repository.ClearExternalDataResponse
)

rpc(
:RegenerateWebhookSecret,
InternalApi.Repository.RegenerateWebhookSecretRequest,
InternalApi.Repository.RegenerateWebhookSecretResponse
)
end

defmodule InternalApi.Repository.RepositoryService.Stub do
Expand Down
1 change: 1 addition & 0 deletions ee/rbac/lib/internal_api/user.pb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ defmodule InternalApi.User.User.CreationSource do

field(:NOT_SET, 0)
field(:OKTA, 1)
field(:SERVICE_ACCOUNT, 2)
end

defmodule InternalApi.User.ListFavoritesRequest do
Expand Down
2 changes: 1 addition & 1 deletion ee/rbac/lib/rbac/grpc_servers/rbac_server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ defmodule Rbac.GrpcServers.RbacServer do
total_pages: total_pages,
members:
Enum.map(subject_role_bindings, fn binding ->
subject_type = if binding.type == "user", do: :USER, else: :GROUP
subject_type = binding.type |> String.upcase() |> String.to_existing_atom()

%RBAC.ListMembersResponse.Member{
subject: %RBAC.Subject{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CreateServiceAccounts < ActiveRecord::Migration[6.1]
def change
create_table :service_accounts, id: false do |t|
t.uuid :id, primary_key: true, null: false
t.string :description
t.uuid :creator_id, null: false
end

add_foreign_key :service_accounts, :users, column: :id, on_delete: :cascade
add_foreign_key :service_accounts, :users, column: :creator_id, on_delete: :nullify
end
end
4 changes: 4 additions & 0 deletions guard/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ START_GPRC_GUARD_API?="true"
START_GRPC_AUTH_API?="true"
START_GRPC_USER_API?="true"
START_GRPC_ORGANIZATION_API?="true"
START_GRPC_SERVICE_ACCOUNT_API?="true"
START_INSTANCE_CONFIG?="true"
INSTANCE_CONFIG_API?="true"
START_GRPC_INSTANCE_CONFIG_API="true"
Expand Down Expand Up @@ -66,6 +67,7 @@ CONTAINER_ENV_VARS= \
-e START_GRPC_AUTH_API=$(START_GRPC_AUTH_API) \
-e START_GRPC_USER_API=$(START_GRPC_USER_API) \
-e START_GRPC_ORGANIZATION_API=$(START_GRPC_ORGANIZATION_API) \
-e START_GRPC_SERVICE_ACCOUNT_API=$(START_GRPC_SERVICE_ACCOUNT_API) \
-e START_INSTANCE_CONFIG=$(START_INSTANCE_CONFIG) \
-e INSTANCE_CONFIG_API=$(INSTANCE_CONFIG_API) \
-e START_GRPC_INSTANCE_CONFIG_API=$(START_GRPC_INSTANCE_CONFIG_API) \
Expand Down Expand Up @@ -141,4 +143,6 @@ endif
/home/protoc/source/encryptor.proto
docker run --rm -v $(PWD):/home/protoc/code -v $(TMP_INTERNAL_REPO_DIR):/home/protoc/source renderedtext/protoc:$(RT_PROTOC_IMG_VSN) protoc -I /home/protoc/source -I /home/protoc/source/include --elixir_out=plugins=grpc:$(RELATIVE_INTERNAL_PB_OUTPUT_DIR) --plugin=/root/.mix/escripts/protoc-gen-elixir \
/home/protoc/source/instance_config.proto
docker run --rm -v $(PWD):/home/protoc/code -v $(TMP_INTERNAL_REPO_DIR):/home/protoc/source renderedtext/protoc:$(RT_PROTOC_IMG_VSN) protoc -I /home/protoc/source -I /home/protoc/source/include --elixir_out=plugins=grpc:$(RELATIVE_INTERNAL_PB_OUTPUT_DIR) --plugin=/root/.mix/escripts/protoc-gen-elixir \
/home/protoc/source/service_account.proto
rm -rf $(TMP_INTERNAL_REPO_DIR)
1 change: 1 addition & 0 deletions guard/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ services:
START_GPRC_GUARD_API: "true"
START_GRPC_AUTH_API: "true"
START_GRPC_USER_API: "true"
START_GRPC_SERVICE_ACCOUNT_API: "true"
START_GRPC_ORGANIZATION_API: "true"
START_GRPC_INSTANCE_CONFIG_API: "true"
INSTANCE_CONFIG_API: "true"
Expand Down
19 changes: 19 additions & 0 deletions guard/helm/templates/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,25 @@ spec:
---
apiVersion: v1
kind: Service
metadata:
name: "{{ .Chart.Name }}-service-account-api"
namespace: {{ .Release.Namespace }}
spec:
type: NodePort
selector:
{{- if .Values.global.development.minimalDeployment }}
app: "{{ .Chart.Name }}"
{{- else }}
app: "{{ .Chart.Name }}-user-api"
{{- end }}
ports:
- name: grpc
port: 50051
targetPort: 50051
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: "{{ .Chart.Name }}-organization-api"
namespace: {{ .Release.Namespace }}
Expand Down
7 changes: 7 additions & 0 deletions guard/helm/templates/user-api-dpl.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ spec:
value: "false"
- name: START_GRPC_USER_API
value: "true"
- name: START_GRPC_SERVICE_ACCOUNT_API
value: "true"
- name: START_GPRC_HEALTH_CHECK
value: "true"
- name: RABBIT_CONSUMER
Expand All @@ -115,6 +117,11 @@ spec:
secretKeyRef:
name: {{ .Values.global.rabbitmq.secretName }}
key: amqp-url
- name: BASE_DOMAIN
valueFrom:
configMapKeyRef:
name: {{ .Values.global.domain.configMapName }}
key: BASE_DOMAIN
- name: LOG_LEVEL
value: {{ .Values.userApi.logging.level | quote }}
{{- if .Values.global.statsd.enabled }}
Expand Down
Loading