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 @@
Type
<%= stack_manager.stack_manager_type.humanize %>
- + +
+
Role Based Access Control
+
+ <% if stack_manager.enable_role_based_access_control %> + Enabled + <% else %> + Disabled + <% end %> +
+
+
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