Skip to content

Add service account feature #466

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

Draft
wants to merge 57 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9ea6d76
chore(ee/rbac): refresh protos
hamir-suspect Jul 18, 2025
80044e6
chore(rbac/ce): refresh protos
hamir-suspect Jul 18, 2025
335d8f2
chore(guard): refresh protos and implement service account service
hamir-suspect Jul 18, 2025
28b4d04
fix: add proper migration, start service account api
hamir-suspect Jul 18, 2025
6858b7a
fix: remove timestamps from service account schema
hamir-suspect Jul 18, 2025
2c44fa0
fix: failing tests
hamir-suspect Jul 21, 2025
61f32ab
fix: linting errors
hamir-suspect Jul 21, 2025
c4f5049
fix: start service account grpc api in tests
hamir-suspect Jul 21, 2025
586b4dd
fix: formatting and remove redundant transaction
hamir-suspect Jul 21, 2025
8c4ba43
fix: set env var for base domain for synthetic service account email …
hamir-suspect Jul 22, 2025
8b88e8c
fix: set correct subject type for rbac user
hamir-suspect Jul 22, 2025
1484d71
chore(github_hooks): fix security vunerability
hamir-suspect Jul 22, 2025
dcc94b0
fix(guard): typespec for subject type
hamir-suspect Jul 22, 2025
9f8a200
fix(guard): passtrough argument number
hamir-suspect Jul 22, 2025
f4104a8
Revert "chore(github_hooks): fix security vunerability"
hamir-suspect Jul 22, 2025
9f58a35
fix(guard): unused vars warning
hamir-suspect Jul 22, 2025
752093d
feat(rbac_ce): add service account subject type
hamir-suspect Jul 23, 2025
b157688
fix: return created_at and updated_at from service account store
hamir-suspect Jul 23, 2025
41e95fd
feat: add role_id to assign to the service account
hamir-suspect Jul 24, 2025
f27aecc
fix: add migration for rbac ce subject type
hamir-suspect Jul 24, 2025
60355ea
fix: add role_id to pattern matching in create request
hamir-suspect Jul 24, 2025
03ecccf
fix: map creation source to service_account
hamir-suspect Jul 25, 2025
21e59db
fix: failing test
hamir-suspect Jul 29, 2025
473f1ab
chore(guard): refresh protos
hamir-suspect Jul 30, 2025
5fa6bdf
feat: deactivate and reactivate service account
hamir-suspect Jul 30, 2025
f4ea91f
feat: do not authenticate deactivated users, deactivate and reactivat…
hamir-suspect Jul 30, 2025
d6cccf1
fix: remove redundant comments
hamir-suspect Jul 30, 2025
17fb93b
chore(guard): refresh protos
hamir-suspect Aug 1, 2025
d5e5f2d
chore: refresh protos
hamir-suspect Aug 4, 2025
d6eee10
feat: add DescribeMany and remove role assignment from service_accoun…
hamir-suspect Aug 4, 2025
e60622c
fix: linting and credo warnings
hamir-suspect Aug 4, 2025
2ae1c5f
feat(front): add service accounts service layer
skipi Jul 16, 2025
04a6ad4
feat(front): add feature flag and permissions to service accounts
skipi Jul 16, 2025
2119bd7
fix(front): pass service account client errors to ui
skipi Jul 16, 2025
f1d0cee
fix(front): use proper permission names
skipi Jul 16, 2025
2c52b73
toil(front): add service account controller tests
skipi Jul 16, 2025
8016bfc
fix(front): correct typespecs for feature enabled plug
skipi Jul 16, 2025
4a06a93
feat(front): ui for managing service accounts
skipi Jul 17, 2025
52f3401
feat(rbac): add missing service account roles
skipi Jul 17, 2025
a78c7bb
toil(front): minor UI adjustments
skipi Jul 30, 2025
6da4a4a
feat(front): project service accounts
skipi Jul 31, 2025
1c58d2b
feat(front): use rbac to load service accounts
skipi Aug 4, 2025
64b3fc2
toil(front): use RBAC alias in support client
skipi Aug 5, 2025
23ea144
fix(front): fetch service accounts with describe_many endpoint
skipi Aug 5, 2025
cdf5184
toil(front): credo fixes
skipi Aug 5, 2025
a7daecf
toil(front): format Dockerfile, add missing ee admin roles
skipi Aug 6, 2025
4b427ed
toil(front): update SA specs to use new API definitions
skipi Aug 6, 2025
d691db4
fix(rbac): add missing service accounts permissions
skipi Aug 6, 2025
9b2479a
fix(front): extract service accounts from response
skipi Aug 7, 2025
5b6d906
toil(github_hooks): fix nokogiri version lock
skipi Aug 7, 2025
72e7c46
fix(front): service account service response handling
skipi Aug 7, 2025
b63b799
fix(front): use correct destroy endpoint to delete service accounts
skipi Aug 11, 2025
a6b401f
toil(front): zero state for project service accounts
skipi Aug 11, 2025
6bba555
fix(front): fix service accounts spinner size
skipi Aug 11, 2025
a5fb57a
fix(front): pass member type when assigning roles
skipi Aug 11, 2025
f08674c
fix(rbac_ce): use subject type for role assignment
hamir-suspect Aug 11, 2025
e9d37e5
toil(rbac): code formatting
skipi Aug 12, 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
4 changes: 4 additions & 0 deletions ee/rbac/assets/permissions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ permissions:
description: "View the existing dashboards within the organization."
- name: "organization.dashboards.manage"
description: "Create new dashboard views."
- name: "organization.service_accounts.view"
description: "View service accounts within the organization."
- name: "organization.service_accounts.manage"
description: "Manage service accounts within the organization."
project:
- name: "project.view"
description: "Access the project. This permission is needed to see any page within the project."
Expand Down
4 changes: 4 additions & 0 deletions ee/rbac/assets/roles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ roles:
- "organization.custom_roles.view"
- "organization.dashboards.view"
- "organization.dashboards.manage"
- "organization.service_accounts.view"
- "organization.service_accounts.manage"
- name: "Admin"
description: "Admins can modify settings within the organization or any of its projects. However, they do not have access to billing information, and they cannot change general organization details, such as the organization name and URL."
maps_to: "Admin"
Expand Down Expand Up @@ -77,6 +79,8 @@ roles:
- "organization.dashboards.view"
- "organization.dashboards.manage"
- "project.delete"
- "organization.service_accounts.view"
- "organization.service_accounts.manage"
- name: "Member"
description: "Members can access the organization's homepage and the projects they are assigned to. However, they are not able to modify any settings."
permissions:
Expand Down
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
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
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
2 changes: 1 addition & 1 deletion front/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ RUN mix sentry_recompile && mix compile --warnings-as-errors
# -- elixir stage

# -- node stage
FROM node:16-alpine as node
FROM node:16-alpine AS node
WORKDIR /assets
COPY front/assets/package.json front/assets/package-lock.json ./
RUN npm set progress=false && npm install
Expand Down
3 changes: 3 additions & 0 deletions front/assets/css/app-semaphore.css
Original file line number Diff line number Diff line change
Expand Up @@ -2795,6 +2795,8 @@ img { max-width: 100%; }
.b--indigo { border-color: #1570ff; }
.b--dark-indigo { border-color: #00359f; }
.b--orange { border-color: #fd7e14; }
.b--yellow { border-color: #FBC335; }
.b--blue { border-color: #2196F3; }
.b--purple { border-color: #8658d6; }
.b--dark-purple { border-color: #5122a5; }
.b--dark-brown { border-color: #974510; }
Expand Down Expand Up @@ -4585,6 +4587,7 @@ code, .code, pre {
.bg-washed-purple { background-color: #f3ecff; }
/* Yellows */
.yellow { color: #FBC335; }
.gold { color: #FBC335; }
.lightest-yellow { color: #fff3bf; }
.washed-yellow { color: #fffae4; }
.bg-yellow { background-color: #FBC335; }
Expand Down
25 changes: 19 additions & 6 deletions front/assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import { default as Agents} from "./agents";
import { default as AddPeople } from "./people/add_people";
import { default as EditPerson } from "./people/edit_person";
import { default as SyncPeople } from "./people/sync_people";
import { default as ServiceAccounts } from "./service_accounts";
import { default as Report } from "./report";

import { InitializingScreen } from "./project_onboarding/initializing";
Expand Down Expand Up @@ -294,12 +295,23 @@ export var App = {
GroupManagement.init();
new Star();

const addPeopleAppRoot = document.getElementById("add-people");
if (addPeopleAppRoot) {
AddPeople({
dom: addPeopleAppRoot,
config: addPeopleAppRoot.dataset,
});

// Initialize Preact apps
const serviceAccountsEl = document.getElementById("service-accounts");
if (serviceAccountsEl) {
const config = JSON.parse(serviceAccountsEl.dataset.config);
ServiceAccounts({ dom: serviceAccountsEl, config });
}

const addPeopleEl = document.getElementById("add-people");
if (addPeopleEl) {
AddPeople({ dom: addPeopleEl, config: addPeopleEl.dataset });
}

const syncPeopleEl = document.querySelector(".app-sync-people");
if (syncPeopleEl) {
const config = JSON.parse(syncPeopleEl.dataset.config);
SyncPeople({ dom: syncPeopleEl, config });
}

document.querySelectorAll(".app-edit-person").forEach((editPersonAppRoot) => {
Expand Down Expand Up @@ -516,6 +528,7 @@ export var App = {

window.Notice.init();


$(document).on("click", ".x-select-on-click", function (event) {
event.currentTarget.setSelectionRange(0, event.currentTarget.value.length);
});
Expand Down
31 changes: 12 additions & 19 deletions front/assets/js/people/add_people/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const App = () => {
<span className="material-symbols-outlined mr2">person_add</span>
{`Add people`}
</button>
<Modal isOpen={isOpen} close={() => close(false)} title="Add people">
<Modal isOpen={isOpen} close={() => close(false)} title="Add new people" width="w-70-m">
<AddNewUsers close={close}/>
</Modal>
</Fragment>
Expand Down Expand Up @@ -85,7 +85,7 @@ const AddNewUsers = (props: { close: (reload: boolean) => void, }) => {
const Link = (props: { icon: VNode, title: string, }) => {
return (
<ActiveShadowLink
className={`btn btn-secondary ${
className={`flex-grow-1 btn btn-secondary ${
currentProvider === provider ? `active` : ``
}`}
disabled={loading}
Expand Down Expand Up @@ -134,16 +134,9 @@ const AddNewUsers = (props: { close: (reload: boolean) => void, }) => {
};

return (
<div
className="bg-white br3 shadow-1 w-90 w-70-m mw6 relative popup"
style={{ top: `200px`, transform: `translate(-50%, 0)` }}
>
<div className="flex items-center justify-between ph4 pt4 mb3">
<h2 className="f3 mb0">Add new people</h2>
</div>

<div className="pa4">
{userProviders.length > 1 && (
<div className="mb3 button-group ph4 w-100 items-center justify-center">
<div className="mb3 button-group w-100 items-center">
{userProviders.map(userProviderBox)}
</div>
)}
Expand All @@ -158,7 +151,7 @@ const AddNewUsers = (props: { close: (reload: boolean) => void, }) => {
return (
<Fragment key={idx}>
{loading && (
<div className="ph4 pb4 tc">
<div className="pb4 tc">
<toolbox.Asset path="images/spinner.svg"/>
</div>
)}
Expand Down Expand Up @@ -280,7 +273,7 @@ const ProvideVia = (props: ProvideViaProps) => {
return (
<Fragment>
<div className="ph4 pb4">{message}</div>
<div className="flex justify-end items-center mt2 pb4 ph4">
<div className="flex justify-end items-center mt2">
<button
className="btn btn-primary ml3"
onClick={() => props?.onCancel(anyInvites)}
Expand All @@ -294,11 +287,11 @@ const ProvideVia = (props: ProvideViaProps) => {

return (
<Fragment>
<div className="ph4">
<div className="">
<label className="db mb2">Invite users to join your organization</label>
</div>
<div
className="ph4 pv1 w-100"
className="pv1 ph1 w-100"
style={{ maxHeight: `400px`, overflow: `auto` }}
>
{!props.noManualInvite && (
Expand Down Expand Up @@ -370,7 +363,7 @@ const ProvideVia = (props: ProvideViaProps) => {
</div>

{collaborators.length != 0 && (
<div className="flex justify-end items-center mt2 pb4 ph4">
<div className="flex justify-end items-center mt2">
<a
className="gray underline pointer"
onClick={() => setSelectedCollaborators(collaborators)}
Expand Down Expand Up @@ -513,7 +506,7 @@ const ProvideViaEmail = (props: ProvideViaEmailProps) => {

return (
<Fragment>
<div className="ph4" style={{ maxHeight: `400px`, overflow: `auto` }}>
<div className="ph1" style={{ maxHeight: `400px`, overflow: `auto` }}>
{!arePeopleInvited && (
<label className="db mb2">Email addresses and usernames</label>
)}
Expand Down Expand Up @@ -593,7 +586,7 @@ const ProvideViaEmail = (props: ProvideViaEmailProps) => {
))}
</div>
{arePeopleInvited && (
<div className="flex justify-end pb4 ph4">
<div className="flex justify-end">
<button
className="btn btn-primary"
onClick={() => props?.onCancel(true)}
Expand All @@ -603,7 +596,7 @@ const ProvideViaEmail = (props: ProvideViaEmailProps) => {
</div>
)}
{!arePeopleInvited && (
<div className="flex justify-end pb4 ph4">
<div className="flex justify-end">
<button
className="btn btn-secondary mr3"
onClick={() => props?.onCancel(false)}
Expand Down
24 changes: 18 additions & 6 deletions front/assets/js/people/add_to_project.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export var AddToProject = {
if(modal){
var addPeopleToProjectBtn = document.getElementById("add_people_to_project");
var addGroupsToProjectBtn = document.getElementById("add_group_to_project");
var addServiceAccountsToProjectBtn = document.getElementById("add_service_accounts_to_project");
var cancelModalBtn = document.getElementById("cancel_btn");

if(addPeopleToProjectBtn){
Expand All @@ -37,6 +38,13 @@ export var AddToProject = {
}
}

if(addServiceAccountsToProjectBtn){
addServiceAccountsToProjectBtn.onclick = () => {
modal.style.display = "block";
this.initProjectNonMembersFilter("service_account")
}
}

cancelModalBtn.onclick = () => {
this.selectedUserIds = []
document.getElementById('users').innerHTML = ''
Expand Down Expand Up @@ -137,9 +145,11 @@ export var AddToProject = {
let assets_path = document.querySelector("meta[name='assets-path']").getAttribute("content")

return `<span ${props}>
${result.has_avatar
? `<img src="${result.avatar}" class="ba b--black-50 br-100 mr2" width="32">`
: `<img src="${assets_path}/images/org-${result.name.charAt(0).toLowerCase()}.svg" class="bg-washed-gray w2 h2 br-100 mr2 ba b--black-50"></div>`
${result.subject_type === "service_account"
? `<div class="dib w2 h2 br-100 mr2 ba b--black-50 tc bg-light-gray"><span class="material-symbols-outlined f6 gray" style="line-height: 2;">smart_toy</span></div>`
: result.has_avatar
? `<img src="${result.avatar}" class="ba b--black-50 br-100 mr2" width="32">`
: `<img src="${assets_path}/images/org-${result.name.charAt(0).toLowerCase()}.svg" class="bg-washed-gray w2 h2 br-100 mr2 ba b--black-50"></div>`
}
<span>${escapeHtml(result.name)}</span>
</span>`
Expand Down Expand Up @@ -180,9 +190,11 @@ export var AddToProject = {
`
<div id="${user.id}" class="flex items-center justify-between bg-white shadow-1 mv1 mh1 ph3 pv2 br3">
<div class="flex items-center">
${user.has_avatar
? `<img src="${user.avatar}" class="w2 h2 br-100 mr2 ba b--black-50">`
: `<img src="${assets_path}/images/org-${user.name.charAt(0).toLowerCase()}.svg" class="bg-washed-gray w2 h2 br-100 mr2 ba b--black-50">`
${user.subject_type === "service_account"
? `<div class="w2 h2 br-100 mr2 ba b--black-50 flex items-center justify-center bg-light-gray"><span class="material-symbols-outlined f6 gray">smart_toy</span></div>`
: user.has_avatar
? `<img src="${user.avatar}" class="w2 h2 br-100 mr2 ba b--black-50">`
: `<img src="${assets_path}/images/org-${user.name.charAt(0).toLowerCase()}.svg" class="bg-washed-gray w2 h2 br-100 mr2 ba b--black-50">`
}
<div class="flex items-center">
<div class="b">${escapeHtml(user.name)}</div>
Expand Down
3 changes: 2 additions & 1 deletion front/assets/js/people/change_role_dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ export var ChangeRoleDropdown = {
const body = {
user_id: roleBtn.attributes.user_id.value,
project_id: InjectedDataByBackend.ProjectId,
role_id: roleBtn.attributes.role_id.value
role_id: roleBtn.attributes.role_id.value,
member_type: roleBtn.attributes.member_type.value
}

toggleSpinner()
Expand Down
Loading