diff --git a/app/controllers/accounts/stack_managers_controller.rb b/app/controllers/accounts/stack_managers_controller.rb
index be7e404a..6133de87 100644
--- a/app/controllers/accounts/stack_managers_controller.rb
+++ b/app/controllers/accounts/stack_managers_controller.rb
@@ -1,6 +1,7 @@
module Accounts
class StackManagersController < ApplicationController
before_action :authenticate_user!
+ before_action :authorize_account, except: [ :verify_url, :check_reachable ]
before_action :set_stack, only: [ :sync_clusters, :sync_registries ]
skip_before_action :authenticate_user!, only: [ :verify_url, :check_reachable ]
@@ -138,5 +139,9 @@ module Accounts
def set_stack
@stack ||= current_account.stack_manager&.stack&.connect(current_user)
end
+
+ def authorize_account
+ authorize current_account, :update?
+ end
end
end
diff --git a/app/controllers/add_ons/base_controller.rb b/app/controllers/add_ons/base_controller.rb
index 6d05cc5a..e96364d9 100644
--- a/app/controllers/add_ons/base_controller.rb
+++ b/app/controllers/add_ons/base_controller.rb
@@ -20,6 +20,7 @@ class AddOns::BaseController < ApplicationController
def set_add_on
@add_on = current_account.add_ons.find(params[:add_on_id])
+ authorize @add_on
set_service
end
end
diff --git a/app/controllers/add_ons_controller.rb b/app/controllers/add_ons_controller.rb
index 60bb9a4c..9a29ccd8 100644
--- a/app/controllers/add_ons_controller.rb
+++ b/app/controllers/add_ons_controller.rb
@@ -124,6 +124,7 @@ class AddOnsController < ApplicationController
add_ons = AddOns::VisibleToUser.execute(account_user: current_account_user).add_ons
@add_on = add_ons.find(params[:id])
@service = K8::Helm::Service.create_from_add_on(K8::Connection.new(@add_on, current_user))
+ authorize @add_on
rescue ActiveRecord::RecordNotFound
redirect_to add_ons_path
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 2fdbe252..e9ba457b 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -52,4 +52,8 @@ class ApplicationController < ActionController::Base
flash[:alert] = "The requested resource could not be found."
redirect_to root_path
end
+
+ def pundit_user
+ current_account_user
+ end
end
diff --git a/app/controllers/projects/base_controller.rb b/app/controllers/projects/base_controller.rb
index 141f2fa2..341ee478 100644
--- a/app/controllers/projects/base_controller.rb
+++ b/app/controllers/projects/base_controller.rb
@@ -10,5 +10,6 @@ class Projects::BaseController < ApplicationController
private
def set_project
@project = current_account.projects.find(params[:project_id])
+ authorize @project
end
end
diff --git a/app/models/account_user.rb b/app/models/account_user.rb
index c818d409..070c8641 100644
--- a/app/models/account_user.rb
+++ b/app/models/account_user.rb
@@ -3,6 +3,7 @@
# Table name: account_users
#
# id :bigint not null, primary key
+# role :integer default("member"), not null
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint not null
@@ -22,7 +23,13 @@ class AccountUser < ApplicationRecord
belongs_to :user
belongs_to :account
- def admin?
- account.owner_id == user_id
+ enum :role, { owner: 0, admin: 1, member: 2 }
+
+ def admin_or_owner?
+ owner? || admin?
+ end
+
+ def destroyable?
+ !owner?
end
end
diff --git a/app/models/provider.rb b/app/models/provider.rb
index 4b650668..37b0b41e 100644
--- a/app/models/provider.rb
+++ b/app/models/provider.rb
@@ -15,14 +15,18 @@
# created_at :datetime not null
# updated_at :datetime not null
# external_id :string
+# sso_provider_id :bigint
# user_id :bigint not null
#
# Indexes
#
-# index_providers_on_user_id (user_id)
+# index_providers_on_sso_provider_id (sso_provider_id)
+# index_providers_on_sso_provider_id_and_uid (sso_provider_id,uid) UNIQUE WHERE (sso_provider_id IS NOT NULL)
+# index_providers_on_user_id (user_id)
#
# Foreign Keys
#
+# fk_rails_... (sso_provider_id => sso_providers.id)
# fk_rails_... (user_id => users.id)
#
class Provider < ApplicationRecord
diff --git a/app/policies/account_policy.rb b/app/policies/account_policy.rb
new file mode 100644
index 00000000..cea1637e
--- /dev/null
+++ b/app/policies/account_policy.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class AccountPolicy < ApplicationPolicy
+ def show?
+ belongs_to_account?
+ end
+
+ def create?
+ admin_or_owner?
+ end
+
+ def update?
+ admin_or_owner?
+ end
+
+ def destroy?
+ admin_or_owner?
+ end
+
+ private
+
+ def belongs_to_account?
+ user&.account_id == record.id
+ end
+
+ def admin_or_owner?
+ belongs_to_account? && user&.admin_or_owner?
+ end
+end
diff --git a/app/policies/add_on_policy.rb b/app/policies/add_on_policy.rb
index 0addb56e..932dac1d 100644
--- a/app/policies/add_on_policy.rb
+++ b/app/policies/add_on_policy.rb
@@ -18,6 +18,9 @@ class AddOnPolicy < ApplicationPolicy
end
def destroy?
+ end
+
+ def default?
visible_to_user?
end
diff --git a/app/views/accounts/stack_managers/_show.html.erb b/app/views/accounts/stack_managers/_show.html.erb
index 5e6322b7..7593e518 100644
--- a/app/views/accounts/stack_managers/_show.html.erb
+++ b/app/views/accounts/stack_managers/_show.html.erb
@@ -27,7 +27,18 @@
Provider URL
diff --git a/db/migrate/20251216022149_add_role_to_account_users.rb b/db/migrate/20251216022149_add_role_to_account_users.rb
new file mode 100644
index 00000000..1b233913
--- /dev/null
+++ b/db/migrate/20251216022149_add_role_to_account_users.rb
@@ -0,0 +1,5 @@
+class AddRoleToAccountUsers < ActiveRecord::Migration[7.2]
+ def change
+ add_column :account_users, :role, :integer, default: 2, null: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index bde52ed4..8915e5ab 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.2].define(version: 2025_11_26_014503) do
+ActiveRecord::Schema[7.2].define(version: 2025_12_16_022149) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -19,6 +19,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_11_26_014503) do
t.bigint "account_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.integer "role", default: 2, null: false
t.index ["account_id"], name: "index_account_users_on_account_id"
t.index ["user_id"], name: "index_account_users_on_user_id"
end
@@ -168,6 +169,14 @@ ActiveRecord::Schema[7.2].define(version: 2025_11_26_014503) do
t.index ["service_id"], name: "index_cron_schedules_on_service_id"
end
+ create_table "deployment_configurations", force: :cascade do |t|
+ t.bigint "project_id", null: false
+ t.integer "deployment_method", default: 0, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["project_id"], name: "index_deployment_configurations_on_project_id"
+ end
+
create_table "deployments", force: :cascade do |t|
t.bigint "build_id", null: false
t.integer "status", default: 0, null: false
@@ -346,6 +355,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_11_26_014503) do
t.string "email_attribute", default: "mail"
t.string "name_attribute", default: "cn"
t.string "filter"
+ t.boolean "allow_anonymous_reads", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
@@ -401,6 +411,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_11_26_014503) do
t.string "userinfo_endpoint"
t.string "jwks_uri"
t.string "scopes", default: "openid email profile"
+ t.string "uid_claim", default: "sub", null: false
+ t.string "email_claim", default: "email"
+ t.string "name_claim", default: "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
@@ -476,6 +489,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_11_26_014503) do
t.datetime "last_used_at"
t.string "registry_url"
t.string "external_id"
+ t.bigint "sso_provider_id"
+ t.index ["sso_provider_id", "uid"], name: "index_providers_on_sso_provider_id_and_uid", unique: true, where: "(sso_provider_id IS NOT NULL)"
+ t.index ["sso_provider_id"], name: "index_providers_on_sso_provider_id"
t.index ["user_id"], name: "index_providers_on_user_id"
end
@@ -515,6 +531,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_11_26_014503) do
t.bigint "configuration_id", null: false
t.string "name", null: false
t.boolean "enabled", default: true, null: false
+ t.integer "team_provisioning_mode", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id"], name: "index_sso_providers_on_account_id", unique: true
@@ -608,6 +625,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_11_26_014503) do
add_foreign_key "builds", "projects"
add_foreign_key "clusters", "accounts"
add_foreign_key "cron_schedules", "services"
+ add_foreign_key "deployment_configurations", "projects"
add_foreign_key "deployments", "builds"
add_foreign_key "environment_variables", "projects"
add_foreign_key "project_add_ons", "add_ons"
@@ -618,6 +636,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_11_26_014503) do
add_foreign_key "project_forks", "projects", column: "parent_project_id"
add_foreign_key "projects", "clusters"
add_foreign_key "projects", "clusters", column: "project_fork_cluster_id"
+ add_foreign_key "providers", "sso_providers"
add_foreign_key "providers", "users"
add_foreign_key "services", "projects"
add_foreign_key "sso_providers", "accounts"
diff --git a/spec/factories/account_users.rb b/spec/factories/account_users.rb
index ad88541d..25b571aa 100644
--- a/spec/factories/account_users.rb
+++ b/spec/factories/account_users.rb
@@ -3,6 +3,7 @@
# Table name: account_users
#
# id :bigint not null, primary key
+# role :integer default("member"), not null
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint not null
@@ -22,5 +23,18 @@ FactoryBot.define do
factory :account_user do
account
user
+ role { :member }
+
+ trait :owner do
+ role { :owner }
+ end
+
+ trait :admin do
+ role { :admin }
+ end
+
+ trait :member do
+ role { :member }
+ end
end
end
diff --git a/spec/factories/providers.rb b/spec/factories/providers.rb
index 1d956e83..c3e2c524 100644
--- a/spec/factories/providers.rb
+++ b/spec/factories/providers.rb
@@ -15,14 +15,18 @@
# created_at :datetime not null
# updated_at :datetime not null
# external_id :string
+# sso_provider_id :bigint
# user_id :bigint not null
#
# Indexes
#
-# index_providers_on_user_id (user_id)
+# index_providers_on_sso_provider_id (sso_provider_id)
+# index_providers_on_sso_provider_id_and_uid (sso_provider_id,uid) UNIQUE WHERE (sso_provider_id IS NOT NULL)
+# index_providers_on_user_id (user_id)
#
# Foreign Keys
#
+# fk_rails_... (sso_provider_id => sso_providers.id)
# fk_rails_... (user_id => users.id)
#
FactoryBot.define do