Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ GEM
bindex (0.8.1)
bootsnap (1.18.6)
msgpack (~> 1.2)
brakeman (7.0.2)
brakeman (7.1.0)
racc
builder (3.3.0)
capybara (3.40.0)
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/home_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class HomeController < ApplicationController
def index
end
end
end
8 changes: 4 additions & 4 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
class SessionsController < ApplicationController
def create
auth = request.env["omniauth.auth"]

if logged_in?
# User is already logged in, so link the new provider to their account
identity = current_user.identities.find_or_create_by(provider: auth.provider, uid: auth.uid)

if identity.persisted?
redirect_to root_path, notice: "#{auth.provider.humanize} account connected successfully!"
else
Expand All @@ -14,7 +14,7 @@ def create
else
# User is not logged in, so sign them in
user = User.from_omniauth(auth)

if user
session[:user_id] = user.id
redirect_to root_path, notice: "Signed in successfully"
Expand All @@ -32,4 +32,4 @@ def destroy
def failure
redirect_to root_path, alert: "Authentication failed: #{params[:message]}"
end
end
end
2 changes: 1 addition & 1 deletion app/models/identity.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class Identity < ApplicationRecord
belongs_to :user

validates :provider, presence: true
validates :uid, presence: true
validates :uid, uniqueness: { scope: :provider }
Expand Down
12 changes: 12 additions & 0 deletions app/models/role.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Role < ApplicationRecord
has_many :user_roles, dependent: :destroy
has_many :users, through: :user_roles

validates :name, presence: true, uniqueness: true

SUPERADMIN = "superadmin"

def self.superadmin
find_or_create_by(name: SUPERADMIN)
end
end
24 changes: 19 additions & 5 deletions app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
class User < ApplicationRecord
has_many :identities, dependent: :destroy

has_many :user_roles, dependent: :destroy
has_many :roles, through: :user_roles

validates :email, presence: true, uniqueness: true

def self.from_omniauth(auth)
Expand All @@ -12,23 +14,35 @@ def self.from_omniauth(auth)
end
id.user = user
end

# Update user name if it's better than what we have
if identity.user.name.blank? || identity.user.name == "User"
identity.user.update(name: auth.info.name || auth.info.nickname || identity.user.name)
end

identity.user
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error "OAuth authentication failed: #{e.message}"
nil
end

def connected_providers
identities.pluck(:provider)
end

def provider_connected?(provider)
identities.exists?(provider: provider)
end

def superadmin?
roles.exists?(name: Role::SUPERADMIN)
end

def make_superadmin!
roles << Role.superadmin unless superadmin?
end

def remove_superadmin!
roles.delete(Role.superadmin) if superadmin?
end
end
6 changes: 6 additions & 0 deletions app/models/user_role.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class UserRole < ApplicationRecord
belongs_to :user
belongs_to :role

validates :user_id, uniqueness: { scope: :role_id }
end
3 changes: 3 additions & 0 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
<div class="flex items-center space-x-4">
<% if logged_in? %>
<span class="text-gray-300">Welcome, <%= current_user.name %></span>
<% if current_user.superadmin? %>
<%= link_to "Admin", "#", class: "bg-purple-600 hover:bg-purple-700 px-4 py-2 rounded text-sm" %>
<% end %>
<%= button_to "Sign Out", logout_path, method: :delete,
class: "bg-red-600 hover:bg-red-700 px-4 py-2 rounded text-sm",
data: { turbo: false } %>
Expand Down
6 changes: 3 additions & 3 deletions db/cable_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
t.binary "payload", limit: 536870912, null: false
t.datetime "created_at", null: false
t.integer "channel_hash", limit: 8, null: false
t.index ["channel"], name: "index_solid_cable_messages_on_channel"
t.index ["channel_hash"], name: "index_solid_cable_messages_on_channel_hash"
t.index ["created_at"], name: "index_solid_cable_messages_on_created_at"
t.index [ "channel" ], name: "index_solid_cable_messages_on_channel"
t.index [ "channel_hash" ], name: "index_solid_cable_messages_on_channel_hash"
t.index [ "created_at" ], name: "index_solid_cable_messages_on_created_at"
end
end
6 changes: 3 additions & 3 deletions db/cache_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
t.datetime "created_at", null: false
t.integer "key_hash", limit: 8, null: false
t.integer "byte_size", limit: 4, null: false
t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size"
t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size"
t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true
t.index [ "byte_size" ], name: "index_solid_cache_entries_on_byte_size"
t.index [ "key_hash", "byte_size" ], name: "index_solid_cache_entries_on_key_hash_and_byte_size"
t.index [ "key_hash" ], name: "index_solid_cache_entries_on_key_hash", unique: true
end
end
2 changes: 1 addition & 1 deletion db/migrate/20250725193121_create_users.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ def change
t.timestamps
end
add_index :users, :email, unique: true
add_index :users, [:provider, :uid], unique: true
add_index :users, [ :provider, :uid ], unique: true
end
end
4 changes: 2 additions & 2 deletions db/migrate/20250725195922_create_identities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def change

t.timestamps
end
add_index :identities, [:provider, :uid], unique: true

add_index :identities, [ :provider, :uid ], unique: true
end
end
10 changes: 10 additions & 0 deletions db/migrate/20250725205833_create_roles.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CreateRoles < ActiveRecord::Migration[8.0]
def change
create_table :roles do |t|
t.string :name, null: false

t.timestamps
end
add_index :roles, :name, unique: true
end
end
12 changes: 12 additions & 0 deletions db/migrate/20250725205853_create_user_roles.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CreateUserRoles < ActiveRecord::Migration[8.0]
def change
create_table :user_roles do |t|
t.references :user, null: false, foreign_key: true
t.references :role, null: false, foreign_key: true

t.timestamps
end

add_index :user_roles, [ :user_id, :role_id ], unique: true
end
end
27 changes: 23 additions & 4 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 59 additions & 0 deletions lib/tasks/superadmin.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
namespace :superadmin do
desc "Make a user a superadmin by email"
task :make, [ :email ] => :environment do |t, args|
unless args[:email]
puts "Please provide an email: rake superadmin:make[user@example.com]"
exit 1
end

user = User.find_by(email: args[:email])

if user.nil?
puts "User with email '#{args[:email]}' not found"
exit 1
end

if user.superadmin?
puts "User '#{args[:email]}' is already a superadmin"
else
user.make_superadmin!
puts "User '#{args[:email]}' has been made a superadmin"
end
end

desc "Remove superadmin role from a user by email"
task :unmake, [ :email ] => :environment do |t, args|
unless args[:email]
puts "Please provide an email: rake superadmin:unmake[user@example.com]"
exit 1
end

user = User.find_by(email: args[:email])

if user.nil?
puts "User with email '#{args[:email]}' not found"
exit 1
end

if user.superadmin?
user.remove_superadmin!
puts "Superadmin role removed from user '#{args[:email]}'"
else
puts "User '#{args[:email]}' is not a superadmin"
end
end

desc "List all superadmins"
task list: :environment do
superadmins = User.joins(:roles).where(roles: { name: Role::SUPERADMIN })

if superadmins.any?
puts "Superadmins:"
superadmins.each do |user|
puts " - #{user.email} (#{user.name})"
end
else
puts "No superadmins found"
end
end
end