mirror of
https://github.com/System-End/identity-vault.git
synced 2026-04-19 18:35:13 +00:00
feat: credential rotation for OAuth apps (#178)
This commit is contained in:
parent
73f87f0f9f
commit
e04d3e1119
10 changed files with 58 additions and 10 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
class Backend::ProgramsController < Backend::ApplicationController
|
class Backend::ProgramsController < Backend::ApplicationController
|
||||||
before_action :set_program, only: [ :show, :edit, :update, :destroy ]
|
before_action :set_program, only: [ :show, :edit, :update, :destroy, :rotate_credentials ]
|
||||||
|
|
||||||
hint :list_navigation, on: :index
|
hint :list_navigation, on: :index
|
||||||
hint :back_navigation, on: :index
|
hint :back_navigation, on: :index
|
||||||
|
|
@ -61,6 +61,13 @@ class Backend::ProgramsController < Backend::ApplicationController
|
||||||
redirect_to backend_programs_path, notice: "Program was successfully deleted."
|
redirect_to backend_programs_path, notice: "Program was successfully deleted."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rotate_credentials
|
||||||
|
authorize @program
|
||||||
|
@program.rotate_credentials!
|
||||||
|
redirect_to backend_program_path(@program), notice: "Credentials have been rotated. Make sure to update any integrations using the old secret/API key."
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_program
|
def set_program
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
class DeveloperAppsController < ApplicationController
|
class DeveloperAppsController < ApplicationController
|
||||||
before_action :require_developer_mode
|
before_action :require_developer_mode
|
||||||
before_action :set_app, only: [ :show, :edit, :update, :destroy ]
|
before_action :set_app, only: [ :show, :edit, :update, :destroy, :rotate_credentials ]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@apps = current_identity.owned_developer_apps.order(created_at: :desc)
|
@apps = current_identity.owned_developer_apps.order(created_at: :desc)
|
||||||
|
|
@ -43,6 +43,11 @@ class DeveloperAppsController < ApplicationController
|
||||||
redirect_to developer_apps_path, notice: t(".success"), status: :see_other
|
redirect_to developer_apps_path, notice: t(".success"), status: :see_other
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rotate_credentials
|
||||||
|
@app.rotate_credentials!
|
||||||
|
redirect_to developer_app_path(@app), notice: t(".success")
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def require_developer_mode
|
def require_developer_mode
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ class Identity::ReapAgedOutUsersJob < ApplicationJob
|
||||||
queue_as :default
|
queue_as :default
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
aged_out = Identity.where(ysws_eligible: true, hq_override: [false, nil])
|
aged_out = Identity.where(ysws_eligible: true, hq_override: [ false, nil ])
|
||||||
.where("birthday <= ?", 19.years.ago.to_date)
|
.where("birthday <= ?", 19.years.ago.to_date)
|
||||||
|
|
||||||
reaped_count = 0
|
reaped_count = 0
|
||||||
|
|
@ -15,4 +15,4 @@ class Identity::ReapAgedOutUsersJob < ApplicationJob
|
||||||
|
|
||||||
Rails.logger.info "ReapAgedOutUsersJob: marked #{reaped_count} #{"user".pluralize reaped_count} as alumni and ineligible"
|
Rails.logger.info "ReapAgedOutUsersJob: marked #{reaped_count} #{"user".pluralize reaped_count} as alumni and ineligible"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,12 @@ class Program < ApplicationRecord
|
||||||
onboarding_scenario_class&.new(identity)
|
onboarding_scenario_class&.new(identity)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rotate_credentials!
|
||||||
|
self.secret = SecureRandom.hex(32)
|
||||||
|
self.program_key = "prgmk." + SecureRandom.hex(32)
|
||||||
|
save!
|
||||||
|
end
|
||||||
|
|
||||||
def self.find_by_redirect_uri_host(url)
|
def self.find_by_redirect_uri_host(url)
|
||||||
return nil if url.blank?
|
return nil if url.blank?
|
||||||
begin
|
begin
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ class ProgramPolicy < ApplicationPolicy
|
||||||
|
|
||||||
def update_onboarding_scenario? = user&.super_admin?
|
def update_onboarding_scenario? = user&.super_admin?
|
||||||
|
|
||||||
|
def rotate_credentials? = user_is_program_manager?
|
||||||
|
|
||||||
class Scope < Scope
|
class Scope < Scope
|
||||||
def resolve
|
def resolve
|
||||||
if user.program_manager? || user.super_admin?
|
if user.program_manager? || user.super_admin?
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ class AnalyticsService
|
||||||
totals.map do |scenario, total|
|
totals.map do |scenario, total|
|
||||||
prom = promoted[scenario] || 0
|
prom = promoted[scenario] || 0
|
||||||
rate = total > 0 ? ((prom.to_f / total) * 100).round(1) : 0
|
rate = total > 0 ? ((prom.to_f / total) * 100).round(1) : 0
|
||||||
[scenario || "default", { total: total, promoted: prom, rate: rate }]
|
[ scenario || "default", { total: total, promoted: prom, rate: rate } ]
|
||||||
end.sort_by { |_, v| -v[:total] }.to_h
|
end.sort_by { |_, v| -v[:total] }.to_h
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,11 @@
|
||||||
<label>api key</label>
|
<label>api key</label>
|
||||||
<input type="text" value="<%= @program.program_key %>" readonly onclick="this.select()" data-click-to-copy autocomplete="off">
|
<input type="text" value="<%= @program.program_key %>" readonly onclick="this.select()" data-click-to-copy autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
|
<% if policy(@program).rotate_credentials? %>
|
||||||
|
<div class="form-row" style="margin-top: 0.5rem;">
|
||||||
|
<%= link_to "rotate secret & api key", rotate_credentials_backend_program_path(@program), method: :post, data: { confirm: "are you sure? this will invalidate the current secret and api key. any integrations using them will break." }, class: "btn btn-danger" %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% if @program.redirect_uri.present? %>
|
<% if @program.redirect_uri.present? %>
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,13 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
<small class="usn" style="display: block; margin-top: 0.25rem;"><%= t ".auth_link_hint" %></small>
|
<small class="usn" style="display: block; margin-top: 0.25rem;"><%= t ".auth_link_hint" %></small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div>
|
||||||
</section>
|
<%= button_to t(".rotate_credentials"), rotate_credentials_developer_app_path(@app), method: :post, class: "danger small-btn",
|
||||||
|
form: { "hx-confirm": t(".rotate_confirm") } %>
|
||||||
|
<small class="usn" style="display: block; margin-top: 0.25rem;"><%= t ".rotate_hint" %></small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="section-card" style="margin-bottom: 1.5rem;">
|
<section class="section-card" style="margin-bottom: 1.5rem;">
|
||||||
<h3><%= t ".configuration" %></h3>
|
<h3><%= t ".configuration" %></h3>
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,9 @@ en:
|
||||||
edit: Edit
|
edit: Edit
|
||||||
delete: Delete
|
delete: Delete
|
||||||
delete_confirm: Are you sure you want to delete this app? This cannot be undone.
|
delete_confirm: Are you sure you want to delete this app? This cannot be undone.
|
||||||
|
rotate_credentials: Rotate Secret & API Key
|
||||||
|
rotate_confirm: Are you sure? This will invalidate your current client secret and API key, breaking any existing integrations using those credentials.
|
||||||
|
rotate_hint: If your credentials have been compromised, you can rotate them to generate new ones. Just make sure to update your integration with the new credentials afterward!
|
||||||
client_id: Client ID
|
client_id: Client ID
|
||||||
click_to_copy_client_id: click to copy client ID
|
click_to_copy_client_id: click to copy client ID
|
||||||
blank_slate:
|
blank_slate:
|
||||||
|
|
@ -183,6 +186,9 @@ en:
|
||||||
edit_app: Edit App
|
edit_app: Edit App
|
||||||
delete_app: Delete App
|
delete_app: Delete App
|
||||||
delete_confirm: Are you sure you want to delete this app? This action cannot be undone and will revoke all existing tokens.
|
delete_confirm: Are you sure you want to delete this app? This action cannot be undone and will revoke all existing tokens.
|
||||||
|
rotate_credentials: Rotate Secret & API Key
|
||||||
|
rotate_confirm: Are you sure? This will generate a new client secret and API key. The old ones will stop working immediately.
|
||||||
|
rotate_hint: If your credentials have been compromised, rotate them here.
|
||||||
new:
|
new:
|
||||||
title: Create New OAuth App
|
title: Create New OAuth App
|
||||||
back_to_apps: ← Back to Apps
|
back_to_apps: ← Back to Apps
|
||||||
|
|
@ -218,6 +224,8 @@ en:
|
||||||
success: OAuth app updated successfully!
|
success: OAuth app updated successfully!
|
||||||
destroy:
|
destroy:
|
||||||
success: OAuth app deleted successfully!
|
success: OAuth app deleted successfully!
|
||||||
|
rotate_credentials:
|
||||||
|
success: OAuth app credentials rotated successfully!
|
||||||
require_developer_mode:
|
require_developer_mode:
|
||||||
developer_mode_required: Developer mode is not enabled for your account.
|
developer_mode_required: Developer mode is not enabled for your account.
|
||||||
set_app:
|
set_app:
|
||||||
|
|
@ -671,4 +679,4 @@ en:
|
||||||
delete_confirm: Are you sure you want to delete this address?
|
delete_confirm: Are you sure you want to delete this address?
|
||||||
add_new: Add a new address
|
add_new: Add a new address
|
||||||
add_button: Add Address
|
add_button: Add Address
|
||||||
done: Done
|
done: Done
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,12 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :programs
|
resources :programs do
|
||||||
|
member do
|
||||||
|
post :rotate_credentials
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
post "/break_glass", to: "break_glass#create"
|
post "/break_glass", to: "break_glass#create"
|
||||||
|
|
||||||
|
|
@ -356,7 +361,12 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
resources :authorized_applications, only: [ :index, :destroy ]
|
resources :authorized_applications, only: [ :index, :destroy ]
|
||||||
|
|
||||||
resources :developer_apps, path: "developer/apps"
|
resources :developer_apps, path: "developer/apps" do
|
||||||
|
member do
|
||||||
|
post :rotate_credentials
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
namespace :api do
|
namespace :api do
|
||||||
namespace :v1 do
|
namespace :v1 do
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue