diff --git a/app/actions/projects/create.rb b/app/actions/projects/create.rb
index 152ca9b5..5458d035 100644
--- a/app/actions/projects/create.rb
+++ b/app/actions/projects/create.rb
@@ -71,8 +71,8 @@ module Projects
steps << Projects::ValidateNamespaceAvailability
steps << Projects::Save
- # Only register webhook in non-local mode
- if !Rails.application.config.local_mode && provider.git?
+ # Only register webhook in cloud mode
+ if Rails.application.config.cloud_mode && provider.git?
steps << Projects::RegisterGitWebhook
end
diff --git a/app/controllers/accounts/stack_managers_controller.rb b/app/controllers/accounts/stack_managers_controller.rb
index 35bfa84a..7ecfaa0c 100644
--- a/app/controllers/accounts/stack_managers_controller.rb
+++ b/app/controllers/accounts/stack_managers_controller.rb
@@ -10,6 +10,8 @@ module Accounts
head :not_found
end
+ # If the user is not having an email domain end in the
+ # portainer stack url, don't log them out, just return a different unauthorized.
if stack_manager.stack.client.authenticated?
head :ok
else
@@ -107,7 +109,12 @@ module Accounts
private
def stack_manager_params
- params.require(:stack_manager).permit(:provider_url, :stack_manager_type)
+ params.require(:stack_manager).permit(
+ :provider_url,
+ :stack_manager_type,
+ :access_token,
+ :enable_role_based_access_control
+ )
end
def set_stack
diff --git a/app/jobs/projects/build_job.rb b/app/jobs/projects/build_job.rb
index 645c676d..26befd69 100644
--- a/app/jobs/projects/build_job.rb
+++ b/app/jobs/projects/build_job.rb
@@ -22,7 +22,7 @@ class Projects::BuildJob < ApplicationJob
Builders::BuildCloud.new(
build,
K8::BuildCloudManager.new(
- K8::Connection.new(project, user),
+ K8::Connection.new(project, user, allow_anonymous: true),
project.build_configuration.build_cloud
)
)
diff --git a/app/models/stack_manager.rb b/app/models/stack_manager.rb
index d6791370..8d4516a1 100644
--- a/app/models/stack_manager.rb
+++ b/app/models/stack_manager.rb
@@ -2,13 +2,14 @@
#
# Table name: stack_managers
#
-# id :bigint not null, primary key
-# access_token :string
-# provider_url :string not null
-# stack_manager_type :integer default("portainer"), not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# account_id :bigint not null
+# id :bigint not null, primary key
+# access_token :string
+# enable_role_based_access_control :boolean default(TRUE)
+# provider_url :string not null
+# stack_manager_type :integer default("portainer"), not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# account_id :bigint not null
#
# Indexes
#
@@ -40,6 +41,14 @@ class StackManager < ApplicationRecord
end
end
+ def domain_host
+ URI.parse(provider_url).host
+ end
+
+ def is_user?(user)
+ user.email.ends_with?("@#{domain_host}")
+ end
+
private
def strip_trailing_slash_from_provider_url
diff --git a/app/services/k8/connection.rb b/app/services/k8/connection.rb
index 37f3c24e..bfef4db9 100644
--- a/app/services/k8/connection.rb
+++ b/app/services/k8/connection.rb
@@ -1,8 +1,9 @@
class K8::Connection
- attr_reader :clusterable, :user
- def initialize(clusterable, user)
+ attr_reader :clusterable, :user, :allow_anonymous
+ def initialize(clusterable, user, allow_anonymous: false)
@clusterable = clusterable
@user = user
+ @allow_anonymous = allow_anonymous
end
def cluster
@@ -24,7 +25,7 @@ class K8::Connection
cluster.kubeconfig
else
raise StandardError.new("No stack manager found") if stack_manager.blank?
- stack = stack_manager.stack.connect(user)
+ stack = stack_manager.stack.connect(user, allow_anonymous: allow_anonymous)
stack.fetch_kubeconfig(cluster)
end
end
diff --git a/app/views/accounts/stack_managers/_form.html.erb b/app/views/accounts/stack_managers/_form.html.erb
index 7b91f4bf..2f3de1b1 100644
--- a/app/views/accounts/stack_managers/_form.html.erb
+++ b/app/views/accounts/stack_managers/_form.html.erb
@@ -20,9 +20,19 @@
Access Token
<% end %>
<%= form.text_field :access_token, class: "input input-bordered w-full" %>
+
<% end %>
+
+ <%= form.label :enable_role_based_access_control, class: "label cursor-pointer justify-start gap-2" do %>
+ <%= form.check_box :enable_role_based_access_control, class: "checkbox" %>
+ Enable Role-Based Access Control
+ <% end %>
+
+
diff --git a/app/views/projects/create/_new_form_git.html.erb b/app/views/projects/create/_new_form_git.html.erb
index 2911fbe2..b832622f 100644
--- a/app/views/projects/create/_new_form_git.html.erb
+++ b/app/views/projects/create/_new_form_git.html.erb
@@ -93,13 +93,11 @@
<% end %>
- <% unless Rails.application.config.local_mode %>
- <%= render(FormFieldComponent.new(
- label: "Autodeploy",
- description: "Automatically deploy the project when the branch is pushed to."
- )) do %>
- <%= form.check_box :autodeploy, class: "checkbox" %>
- <% end %>
+ <%= render(FormFieldComponent.new(
+ label: "Autodeploy",
+ description: "Automatically deploy the project when the branch is pushed to."
+ )) do %>
+ <%= form.check_box :autodeploy, class: "checkbox" %>
<% end %>
<%= render(FormFieldComponent.new(
diff --git a/db/migrate/20251009003742_enable_role_based_access_control_to_stack_managers.rb b/db/migrate/20251009003742_enable_role_based_access_control_to_stack_managers.rb
new file mode 100644
index 00000000..661ea43f
--- /dev/null
+++ b/db/migrate/20251009003742_enable_role_based_access_control_to_stack_managers.rb
@@ -0,0 +1,5 @@
+class EnableRoleBasedAccessControlToStackManagers < ActiveRecord::Migration[7.2]
+ def change
+ add_column :stack_managers, :enable_role_based_access_control, :boolean, default: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 00133d3e..dbc7bd77 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_10_02_214647) do
+ActiveRecord::Schema[7.2].define(version: 2025_10_09_003742) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -451,6 +451,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_10_02_214647) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "access_token"
+ t.boolean "enable_role_based_access_control", default: true
t.index ["account_id"], name: "index_stack_managers_on_account_id", unique: true
end
diff --git a/lib/portainer/login.rb b/lib/portainer/login.rb
index ffd944aa..f35a6c2c 100644
--- a/lib/portainer/login.rb
+++ b/lib/portainer/login.rb
@@ -6,7 +6,7 @@ class Portainer::Login
executed do |context|
provider_url = context.account.stack_manager.provider_url
- hostname = URI.parse(provider_url).host
+ hostname = context.account.stack_manager.domain_host
context.user = User.find_or_initialize_by(
email: context.username + "@#{hostname}",
)
diff --git a/lib/portainer/stack.rb b/lib/portainer/stack.rb
index a6f8b5c3..7c27c7ac 100644
--- a/lib/portainer/stack.rb
+++ b/lib/portainer/stack.rb
@@ -11,8 +11,10 @@ class Portainer::Stack
self
end
- def connect(user)
- access_token = if stack_manager.access_token.present?
+ def connect(user, allow_anonymous: false)
+ access_token = if stack_manager.access_token.present? && !enable_role_based_access_control
+ stack_manager.access_token
+ elsif stack_manager.access_token.present? && allow_anonymous
stack_manager.access_token
else
user.portainer_jwt
diff --git a/spec/factories/stack_managers.rb b/spec/factories/stack_managers.rb
index b7f03f1e..f9582541 100644
--- a/spec/factories/stack_managers.rb
+++ b/spec/factories/stack_managers.rb
@@ -2,13 +2,14 @@
#
# Table name: stack_managers
#
-# id :bigint not null, primary key
-# access_token :string
-# provider_url :string not null
-# stack_manager_type :integer default("portainer"), not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# account_id :bigint not null
+# id :bigint not null, primary key
+# access_token :string
+# enable_role_based_access_control :boolean default(TRUE)
+# provider_url :string not null
+# stack_manager_type :integer default("portainer"), not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# account_id :bigint not null
#
# Indexes
#
diff --git a/spec/models/stack_manager_spec.rb b/spec/models/stack_manager_spec.rb
index 6ff4b39a..3d9671ba 100644
--- a/spec/models/stack_manager_spec.rb
+++ b/spec/models/stack_manager_spec.rb
@@ -2,13 +2,14 @@
#
# Table name: stack_managers
#
-# id :bigint not null, primary key
-# access_token :string
-# provider_url :string not null
-# stack_manager_type :integer default("portainer"), not null
-# created_at :datetime not null
-# updated_at :datetime not null
-# account_id :bigint not null
+# id :bigint not null, primary key
+# access_token :string
+# enable_role_based_access_control :boolean default(TRUE)
+# provider_url :string not null
+# stack_manager_type :integer default("portainer"), not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# account_id :bigint not null
#
# Indexes
#