securial 1.0.1 โ†’ 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -0
  3. data/README.md +14 -9
  4. data/app/controllers/concerns/securial/identity.rb +91 -2
  5. data/app/controllers/securial/accounts_controller.rb +68 -5
  6. data/app/controllers/securial/application_controller.rb +34 -2
  7. data/app/controllers/securial/passwords_controller.rb +44 -4
  8. data/app/controllers/securial/role_assignments_controller.rb +55 -4
  9. data/app/controllers/securial/roles_controller.rb +54 -0
  10. data/app/controllers/securial/sessions_controller.rb +77 -3
  11. data/app/controllers/securial/status_controller.rb +24 -0
  12. data/app/controllers/securial/users_controller.rb +54 -0
  13. data/app/jobs/securial/application_job.rb +9 -0
  14. data/app/mailers/securial/application_mailer.rb +12 -0
  15. data/app/mailers/securial/securial_mailer.rb +30 -0
  16. data/app/models/concerns/securial/password_resettable.rb +70 -0
  17. data/app/models/securial/application_record.rb +19 -0
  18. data/app/models/securial/current.rb +13 -0
  19. data/app/models/securial/role.rb +17 -0
  20. data/app/models/securial/role_assignment.rb +16 -0
  21. data/app/models/securial/session.rb +79 -1
  22. data/app/models/securial/user.rb +34 -0
  23. data/lib/generators/factory_bot/model/model_generator.rb +1 -0
  24. data/lib/securial/auth.rb +44 -6
  25. data/lib/securial/cli.rb +124 -0
  26. data/lib/securial/config.rb +49 -2
  27. data/lib/securial/engine.rb +41 -0
  28. data/lib/securial/error/auth.rb +52 -0
  29. data/lib/securial/error/base_securial_error.rb +51 -0
  30. data/lib/securial/error/config.rb +33 -0
  31. data/lib/securial/error.rb +33 -3
  32. data/lib/securial/helpers.rb +48 -4
  33. data/lib/securial/logger/broadcaster.rb +89 -1
  34. data/lib/securial/logger/builder.rb +54 -1
  35. data/lib/securial/logger/formatter.rb +73 -0
  36. data/lib/securial/logger.rb +42 -1
  37. data/lib/securial/middleware.rb +40 -9
  38. data/lib/securial/security/request_rate_limiter.rb +47 -1
  39. data/lib/securial/security.rb +37 -6
  40. data/lib/securial/version.rb +8 -1
  41. data/lib/securial.rb +36 -0
  42. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 166aa60d38c7fecac2ec442467982af0fb09a7521aa12233b5a57a459aeba328
4
- data.tar.gz: b4a27ad795aef20268a7912c0efc4df11c1cf58b62bf5d8814ee583c549f6f35
3
+ metadata.gz: 9f93334f493089fe07b825a71ccbe4c8eb2b3ae9b924e2e7cab16f4818d0bb4a
4
+ data.tar.gz: 47e73ee83002a71737e3d7e16c31fe428652ddb9a6d4104eb2d622b9bc9dc666
5
5
  SHA512:
6
- metadata.gz: 3b5e7ebd0df4540a6814df63530ce6af969120ce50a34dbc5e6157103fd14108fcf19dffee65d318e5d31ee9851e3385a3d0069bd96cd3c8124dbd73f2e6db58
7
- data.tar.gz: 00ac53e81274914b952e609ab57f777c9a4128520cbfff5044caad442f49ef2b89efeb84e6bec09d0f14e127a141b3ce53e9a352ab29082b68b3aee2a50a14f4
6
+ metadata.gz: 3108d25afcf5df41d3f7578b12fdb496402deb3ba7ce9e8485ed90e96e3d71dd7d4297c67dd49911d73ffd2a31100845e792e5e1670d3a35cbe877bad8066ba6
7
+ data.tar.gz: 1c403ba8a424348b2ac80c0a46c7cc71e9e720bff838a735caac6d263884c3eab06480917d3bff8c8f7a7ed295bc4ecac1e5cf53caf609c361aa2a5159533e0e
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ --private
2
+ --title "Securial Gem Documentation"
3
+ --exclude
4
+ lib/generators/factory_bot/model/model_generator.rb
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Securial
1
+ # Securial Gem
2
2
 
3
3
  [![Gem Version](https://img.shields.io/gem/v/securial?logo=rubygems&logoColor=ffffff&logoSize=auto&label=version&color=violet&cacheSeconds=120)](https://rubygems.org/gems/securial)
4
4
  [![Gem Downloads](https://img.shields.io/gem/dt/securial.svg)](https://rubygems.org/gems/securial)
@@ -8,19 +8,22 @@
8
8
  [![Coveralls](https://img.shields.io/coverallsCoverage/github/AlyBadawy/Securial?branch=main&logo=coveralls&logoColor=%233F5767&labelColor=ddeedd)
9
9
  ](https://coveralls.io/github/AlyBadawy/Securial?branch=main)
10
10
 
11
+ [![Documentation](https://img.shields.io/badge/yard-Documentation-blue?style=flat&logo=readthedocs&logoColor=%238CA1AF&label=yard&link=https%3A%2F%2Fwww.rubydoc.info%2Fgems%2Fsecurial%2Findex)](https://alybadawy.github.io/Securial/_index.html)
12
+
11
13
  ---
12
14
 
15
+ ## Overview
16
+
13
17
  ### ๐Ÿ›ก๏ธ What is Securial?
14
18
 
15
19
  **Securial** is a mountable Rails engine that provides robust, extensible authentication and access control for Rails applications. It supports:
16
20
 
17
- - ๐ŸŸฆ JWT-based authentication
18
- - ๐ŸŸฆ API tokens
19
- - ๐ŸŸฆ Session-based auth
20
- - ๐ŸŸฆ Simple integration with web and mobile apps
21
- - ๐ŸŸฆ Clean, JSON-based API responses
22
- - ๐ŸŸฆ Simple user management with roles
23
- - ๐ŸŸฆ Database-agnostic support
21
+ - ๐Ÿ”‘ JWT-based authentication
22
+ - โ†ช๏ธ API session tokens, with refresh tokens
23
+ - ๐Ÿคณ Simple integration with web and mobile apps
24
+ - ๐Ÿงน Clean, JSON-based API responses
25
+ - ๐Ÿง User management with roles
26
+ - ๐Ÿซ™ Database-agnostic support
24
27
 
25
28
  ### ๐Ÿ‘€ Why Securial?
26
29
 
@@ -34,12 +37,13 @@ Securial can be installed on an existing Rails application or use the `securial
34
37
 
35
38
  ### Installation on an existing Rails app:
36
39
 
37
- To add Securial to an existing Rails app:
40
+ Add Securial to an existing Rails app is as simple as 1..2..3:
38
41
 
39
42
  - Add `gem "securial"` to your GemFile
40
43
  - Run `bundle install`
41
44
  - Run `rails generate securial:install`
42
45
  - Mount the Securial engine in your Rails application `config/routes.rb` file:
46
+
43
47
  ```ruby
44
48
  Rails.application.routes.draw do
45
49
  mount Securial::Engine => "/securial"
@@ -47,6 +51,7 @@ To add Securial to an existing Rails app:
47
51
  # The rest of your routes
48
52
  end
49
53
  ```
54
+
50
55
  - Run the migrations by running the command: `rails db:migrate`
51
56
 
52
57
  ๐Ÿ’ก Full installation steps are available in the [Wiki โ€บ Installation](https://github.com/AlyBadawy/Securial/wiki/Installation).
@@ -1,4 +1,10 @@
1
1
  module Securial
2
+ # Identity Concern
3
+ #
4
+ # Provides authentication and user identification functionality for controllers.
5
+ # This concern adds before_action hooks for user identification and authentication,
6
+ # and provides helper methods for accessing the current user and enforcing authentication.
7
+ #
2
8
  module Identity
3
9
  extend ActiveSupport::Concern
4
10
 
@@ -8,6 +14,41 @@ module Securial
8
14
  helper_method :current_user if respond_to?(:helper_method)
9
15
  end
10
16
 
17
+ # @!method skip_authentication!(options = {})
18
+ # Skips authentication for specified controller actions, making them publicly accessible.
19
+ #
20
+ # This class method allows you to bypass the `authenticate_user!` before_action
21
+ # that is applied by default to all controller actions when including the Identity concern.
22
+ # It's useful for endpoints that should be publicly accessible, such as landing pages,
23
+ # password reset, login, or registration.
24
+ #
25
+ # @param [Hash] options Options to pass to skip_before_action
26
+ # @option options [Symbol, Array<Symbol>] :only Specific action(s) to skip authentication for
27
+ # @option options [Symbol, Array<Symbol>] :except Action(s) to exclude from skipping
28
+ #
29
+ # @example Skip authentication for login and registration actions
30
+ # class SessionsController < ApplicationController
31
+ # include Securial::Identity
32
+ #
33
+ # skip_authentication! only: [:new, :create]
34
+ #
35
+ # def new
36
+ # # Login form action
37
+ # end
38
+ #
39
+ # def create
40
+ # # Authentication logic
41
+ # end
42
+ # end
43
+ #
44
+ # @example Skip authentication for all actions except protected ones
45
+ # class PublicController < ApplicationController
46
+ # include Securial::Identity
47
+ #
48
+ # skip_authentication! except: [:dashboard, :account]
49
+ # end
50
+ #
51
+ # @return [void]
11
52
  class_methods do
12
53
  def skip_authentication!(**options)
13
54
  skip_before_action :authenticate_user!, **options
@@ -15,15 +56,34 @@ module Securial
15
56
  end
16
57
 
17
58
 
59
+ # Returns the currently authenticated user or nil if no user is authenticated.
60
+ #
61
+ # This method provides access to the current user object from the session
62
+ # tracked in Current.session.
63
+ #
64
+ # @return [Securial::User, nil] The current authenticated user or nil
65
+ #
18
66
  def current_user
19
67
  Current.session&.user
20
68
  end
21
69
 
70
+ # Ensures the current user has admin privileges.
71
+ #
72
+ # This method checks if a user is authenticated and has admin privileges.
73
+ # If the user is not an admin, it renders a forbidden response.
74
+ # If no user is authenticated, it calls authenticate_user! to handle the unauthorized case.
75
+ #
76
+ # @return [void]
77
+ #
22
78
  def authenticate_admin!
23
79
  if current_user
24
80
  return if current_user.is_admin?
25
81
 
26
- render status: :forbidden, json: { error: "You are not authorized to perform this action" }
82
+ render status: :forbidden,
83
+ json: {
84
+ errors: ["You are not authorized to perform this action"],
85
+ instructions: "Please contact an administrator if you believe this is an error.",
86
+ }
27
87
  else
28
88
  authenticate_user!
29
89
  end
@@ -31,6 +91,18 @@ module Securial
31
91
 
32
92
  private
33
93
 
94
+ # Identifies the current user from the Authorization header.
95
+ #
96
+ # This method attempts to identify the user by:
97
+ # 1. Checking for a valid Bearer token in the Authorization header
98
+ # 2. Decoding the token to extract the session ID
99
+ # 3. Finding and validating the corresponding session
100
+ # 4. Ensuring IP address and user agent match for security
101
+ #
102
+ # If identification succeeds, the session is stored in Current.session
103
+ #
104
+ # @return [void]
105
+ #
34
106
  def identify_user
35
107
  return if internal_rails_request?
36
108
 
@@ -53,13 +125,30 @@ module Securial
53
125
  end
54
126
  end
55
127
 
128
+ # Ensures a user is authenticated before proceeding.
129
+ #
130
+ # This method checks if a user has been successfully identified.
131
+ # If not, it renders an unauthorized response and halts the request.
132
+ # Internal Rails requests are exempt from authentication requirements.
133
+ #
134
+ # @return [void]
56
135
  def authenticate_user!
57
136
  return if internal_rails_request?
58
137
  return if Current.session&.user
59
138
 
60
- render status: :unauthorized, json: { error: "You are not signed in" } and return
139
+ render status: :unauthorized,
140
+ json: {
141
+ errors: ["You are not signed in"],
142
+ instructions: "Please sign in to access this resource.",
143
+ } and return
61
144
  end
62
145
 
146
+ # Determines if the current request is from internal Rails controllers.
147
+ #
148
+ # This method checks if the current controller is one of Rails' internal
149
+ # controllers (Info, Mailers, Welcome) which should bypass authentication.
150
+ #
151
+ # @return [Boolean] true if the request is from internal Rails controllers, false otherwise
63
152
  def internal_rails_request?
64
153
  defined?(Rails::InfoController) && is_a?(Rails::InfoController) ||
65
154
  defined?(Rails::MailersController) && is_a?(Rails::MailersController) ||
@@ -1,59 +1,122 @@
1
1
  module Securial
2
+ #
3
+ # AccountsController
4
+ #
5
+ # This controller handles user account-related operations including:
6
+ # - User registration
7
+ # - Profile viewing and management
8
+ # - Account updates
9
+ # - Account deletion
10
+ #
11
+ # Routes typically mounted at Securial/accounts/* in the host application.
12
+ #
2
13
  class AccountsController < ApplicationController
14
+ # Retrieves the current user's profile.
15
+ #
16
+ # Provides the authenticated user's complete profile information.
17
+ # Requires authentication via the Identity concern.
18
+ #
19
+ # @return [void] Renders user profile with 200 OK status
3
20
  def me
4
21
  @securial_user = Current.user
5
22
 
6
23
  render :show, status: :ok, location: @securial_user
7
24
  end
8
25
 
26
+ # Shows a specific user's profile by username.
27
+ #
28
+ # Retrieves and displays public profile information for the requested user.
29
+ #
30
+ # @param [String] params[:username] The username of the requested user profile
31
+ # @return [void] Renders user profile with 200 OK status or 404 if not found
9
32
  def show
10
33
  @securial_user = Securial::User.find_by(username: params.expect(:username))
11
34
  render_user_profile
12
35
  end
13
36
 
37
+ # Registers a new user account.
38
+ #
39
+ # Creates a new user in the system with the provided registration information.
40
+ #
41
+ # @param [Hash] params[:securial_user] User attributes including email_address, password, etc.
42
+ # @return [void] Renders new user with 201 Created status or errors with 422
14
43
  def register
15
44
  @securial_user = Securial::User.new(user_params)
16
45
  if @securial_user.save
17
46
  render :show, status: :created, location: @securial_user
18
47
  else
19
- render json: { errors: @securial_user.errors.full_messages }, status: :unprocessable_entity
48
+ render json: {
49
+ errors: @securial_user.errors.full_messages }, status: :unprocessable_entity
20
50
  end
21
51
  end
22
52
 
53
+ # Updates the current user's profile information.
54
+ #
55
+ # Allows users to modify their profile after authenticating with their current password.
56
+ #
57
+ # @param [String] current_password User's current password for verification
58
+ # @param [Hash] params[:securial_user] Updated user attributes
59
+ # @return [void] Renders updated user with 200 OK status or errors with 422
23
60
  def update_profile
24
61
  @securial_user = Current.user
25
62
  if @securial_user.authenticate(params[:securial_user][:current_password])
26
63
  if @securial_user.update(user_params)
27
64
  render :show, status: :ok, location: @securial_user
28
65
  else
29
- render json: { errors: @securial_user.errors.full_messages }, status: :unprocessable_entity
66
+ render json: {
67
+ errors: @securial_user.errors.full_messages,
68
+ instructions: "Please ensure all required fields are filled out correctly.",
69
+ }, status: :unprocessable_entity
30
70
  end
31
71
  else
32
- render json: { error: "Current password is incorrect" }, status: :unprocessable_entity
72
+ render json: {
73
+ errors: ["Current password is incorrect"],
74
+ instructions: "Please verify your current password and try again.",
75
+ }, status: :unprocessable_entity
33
76
  end
34
77
  end
35
78
 
79
+ # Permanently deletes the current user's account.
80
+ #
81
+ # Removes the user account and all associated data after password verification.
82
+ #
83
+ # @param [String] params[:current_password] User's current password for verification
84
+ # @return [void] Renders success message with 200 OK status or error with 422
36
85
  def delete_account
37
86
  @securial_user = Current.user
38
87
  if @securial_user.authenticate(params.expect(securial_user: [:current_password]).dig(:current_password))
39
88
  @securial_user.destroy
40
89
  render json: { message: "Account deleted successfully" }, status: :ok
41
90
  else
42
- render json: { error: "Current password is incorrect" }, status: :unprocessable_entity
91
+ render json: {
92
+ errors: ["Current password is incorrect"],
93
+ instructions: "Please verify your current password and try again.",
94
+ }, status: :unprocessable_entity
43
95
  end
44
96
  end
45
97
 
46
98
  private
47
99
 
100
+ # Permits and extracts user parameters from the request.
101
+ #
102
+ # @return [ActionController::Parameters] Permitted user parameters
103
+ #
48
104
  def user_params
49
105
  params.expect(securial_user: [:email_address, :password, :password_confirmation, :first_name, :last_name, :phone, :username, :bio])
50
106
  end
51
107
 
108
+ # Renders the user profile or a not found response.
109
+ #
110
+ # @return [void] Renders user profile or not found error
111
+ #
52
112
  def render_user_profile
53
113
  if @securial_user
54
114
  render :show, status: :ok, location: @securial_user
55
115
  else
56
- render json: { error: "User not found" }, status: :not_found
116
+ render json: {
117
+ errors: ["User not found"],
118
+ instructions: "Please check the username and try again.",
119
+ }, status: :not_found
57
120
  end
58
121
  end
59
122
  end
@@ -1,4 +1,16 @@
1
1
  module Securial
2
+ #
3
+ # ApplicationController
4
+ #
5
+ # This is the base controller for the Securial engine, inheriting from ActionController::API.
6
+ # and provides common functionality for all Securial controllers, including:
7
+ # - Custom view path configuration
8
+ # - Common exception handling for API responses
9
+ # - Standardized error rendering
10
+ #
11
+ # All other controllers in the Securial engine inherit from this controller
12
+ # to ensure consistent behavior across the API.
13
+ #
2
14
  class ApplicationController < ActionController::API
3
15
  prepend_view_path Securial::Engine.root.join("app", "views")
4
16
 
@@ -7,12 +19,32 @@ module Securial
7
19
 
8
20
  private
9
21
 
22
+ # Renders a standardized 404 Not Found JSON response.
23
+ #
24
+ # Called automatically when an ActiveRecord::RecordNotFound exception is raised,
25
+ # ensuring consistent error responses across the API.
26
+ #
27
+ # @param [ActiveRecord::RecordNotFound] exception The exception raised when a record is not found
28
+ # @return [void]
10
29
  def render_404
11
- render status: :not_found, json: { error: "Record not found" }
30
+ render status: :not_found, json: {
31
+ errors: ["Record not found"],
32
+ instructions: "Please check the requested resource and try again.",
33
+ }
12
34
  end
13
35
 
36
+ # Renders a standardized 400 Bad Request JSON response.
37
+ #
38
+ # Called automatically when an ActionController::ParameterMissing exception is raised,
39
+ # providing the client with information about the missing parameter.
40
+ #
41
+ # @param [ActionController::ParameterMissing] exception The exception raised for missing parameters
42
+ # @return [void]
14
43
  def render_400(exception)
15
- render status: :bad_request, json: { error: exception.message }
44
+ render status: :bad_request, json: {
45
+ errors: [exception.message],
46
+ instructions: "Please ensure all required parameters are provided and formatted correctly.",
47
+ }
16
48
  end
17
49
  end
18
50
  end
@@ -1,8 +1,30 @@
1
1
  module Securial
2
+ #
3
+ # PasswordsController
4
+ #
5
+ # Controller for managing user password operations in the Securial authentication system.
6
+ #
7
+ # This controller handles password-related operations including:
8
+ # - Forgot password functionality
9
+ # - Password reset with secure tokens
10
+ #
11
+ # All actions in this controller skip standard authentication requirements to allow
12
+ # unauthenticated users to recover their accounts.
13
+ #
14
+ # Routes typically mounted at Securial/password/* in the host application.
15
+ #
2
16
  class PasswordsController < ApplicationController
3
17
  skip_authentication!
4
18
  before_action :set_user_by_password_token, only: %i[ reset_password ]
5
19
 
20
+ # Initiates the password reset process for a user.
21
+ #
22
+ # Looks up a user by email address and, if found, generates a secure reset token
23
+ # and sends password reset instructions via email. To prevent user enumeration attacks,
24
+ # returns the same success response regardless of whether the email exists.
25
+ #
26
+ # @param [String] params[:email_address] The email address of the user requesting password reset
27
+ # @return [void] Renders success message with 200 OK status
6
28
  def forgot_password
7
29
  if user = User.find_by(email_address: params.require(:email_address))
8
30
  user.generate_reset_password_token!
@@ -12,6 +34,15 @@ module Securial
12
34
  render status: :ok, json: { message: "Password reset instructions sent (if user with that email address exists)." }
13
35
  end
14
36
 
37
+ # Resets a user's password using a valid reset token.
38
+ #
39
+ # Validates the provided token, clears it to prevent reuse, and updates
40
+ # the user's password if the new password is valid.
41
+ #
42
+ # @param [String] params[:token] The password reset token from the email
43
+ # @param [String] params[:password] The new password
44
+ # @param [String] params[:password_confirmation] Confirmation of the new password
45
+ # @return [void] Renders success message with 200 OK status or errors with 422
15
46
  def reset_password
16
47
  @user.clear_reset_password_token!
17
48
  if @user.update(params.permit(:password, :password_confirmation))
@@ -23,13 +54,22 @@ module Securial
23
54
 
24
55
  private
25
56
 
57
+ # Locates and validates a user by their password reset token.
58
+ #
59
+ # Sets @user instance variable if the token is valid and not expired.
60
+ # Renders an error response if the token is invalid or expired.
61
+ #
62
+ # @param [String] params[:token] The password reset token to validate
63
+ # @return [void]
26
64
  def set_user_by_password_token
27
- @user = User.find_by_reset_password_token!(params[:token]) # rubocop:disable Rails/DynamicFindBy
28
- unless @user.reset_password_token_valid?
65
+ begin
66
+ @user = User.find_by_reset_password_token!(params[:token]) # rubocop:disable Rails/DynamicFindBy
67
+ unless @user.reset_password_token_valid?
68
+ render status: :unprocessable_entity, json: { errors: { token: "is invalid or has expired" } } and return
69
+ end
70
+ rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveRecord::RecordNotFound
29
71
  render status: :unprocessable_entity, json: { errors: { token: "is invalid or has expired" } } and return
30
72
  end
31
- rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveRecord::RecordNotFound
32
- render status: :unprocessable_entity, json: { errors: { token: "is invalid or has expired" } } and return
33
73
  end
34
74
  end
35
75
  end
@@ -1,10 +1,35 @@
1
1
  module Securial
2
+ #
3
+ # RoleAssignmentsController
4
+ #
5
+ # Controller for managing role assignments in the Securial authorization system.
6
+ #
7
+ # This controller handles role management operations including:
8
+ # - Assigning roles to users
9
+ # - Removing roles from users
10
+ #
11
+ # All operations require admin authentication and are typically used for
12
+ # user permission management within the application.
13
+ #
14
+ # Routes typically mounted at Securial/admins/role_assignments/* in the host application.
15
+ #
2
16
  class RoleAssignmentsController < ApplicationController
17
+ # Assigns a role to a user.
18
+ #
19
+ # Creates a new role assignment between the specified user and role.
20
+ # Validates that the assignment doesn't already exist.
21
+ #
22
+ # @param [Integer] params[:user_id] The ID of the user to assign the role to
23
+ # @param [Integer] params[:role_id] The ID of the role to be assigned
24
+ # @return [void] Renders the created assignment with 201 Created status or errors with 422
3
25
  def create
4
26
  return unless define_user_and_role
5
27
 
6
28
  if @securial_user.roles.exists?(@securial_role.id)
7
- render json: { error: "Role already assigned to user" }, status: :unprocessable_entity
29
+ render json: {
30
+ errors: ["Role already assigned to user"],
31
+ instructions: "Please check the user's current roles before assigning a new one.",
32
+ }, status: :unprocessable_entity
8
33
  return
9
34
  end
10
35
  @securial_role_assignment = RoleAssignment.new(securial_role_assignment_params)
@@ -12,6 +37,14 @@ module Securial
12
37
  render :show, status: :created
13
38
  end
14
39
 
40
+ # Removes a role from a user.
41
+ #
42
+ # Deletes an existing role assignment between the specified user and role.
43
+ # Validates that the assignment exists before attempting deletion.
44
+ #
45
+ # @param [Integer] params[:user_id] The ID of the user to remove the role from
46
+ # @param [Integer] params[:role_id] The ID of the role to be removed
47
+ # @return [void] Renders the deleted assignment with 200 OK status or errors with 422
15
48
  def destroy
16
49
  return unless define_user_and_role
17
50
  @role_assignment = RoleAssignment.find_by(securial_role_assignment_params)
@@ -19,27 +52,45 @@ module Securial
19
52
  @role_assignment.destroy!
20
53
  render :show, status: :ok
21
54
  else
22
- render json: { error: "Role is not assigned to user" }, status: :unprocessable_entity
55
+ render json: {
56
+ errors: ["Role is not assigned to user"],
57
+ instructions: "Please check the user's current roles before attempting to remove a role.",
58
+ }, status: :unprocessable_entity
23
59
  end
24
60
  end
25
61
 
26
62
  private
27
63
 
64
+ # Looks up and validates the existence of both the user and role.
65
+ #
66
+ # Sets @securial_user and @securial_role instance variables if both exist.
67
+ # Renders error responses if either cannot be found.
68
+ #
69
+ # @return [Boolean] true if both user and role were found, false otherwise
28
70
  def define_user_and_role
29
71
  @securial_user = User.find_by(id: params.expect(securial_role_assignment: [:user_id]).dig(:user_id))
30
72
  @securial_role = Role.find_by(id: params.expect(securial_role_assignment: [:role_id]).dig(:role_id))
31
73
  if @securial_user.nil?
32
- render json: { error: "User not found" }, status: :unprocessable_entity
74
+ render json: {
75
+ errors: ["User not found"],
76
+ instructions: "Please check the user ID and try again.",
77
+ }, status: :unprocessable_entity
33
78
  return false
34
79
  end
35
80
  if @securial_role.nil?
36
- render json: { error: "Role not found" }, status: :unprocessable_entity
81
+ render json: {
82
+ errors: ["Role not found"],
83
+ instructions: "Please check the role ID and try again.",
84
+ }, status: :unprocessable_entity
37
85
  return false
38
86
  end
39
87
 
40
88
  true
41
89
  end
42
90
 
91
+ # Permits and extracts role assignment parameters from the request.
92
+ #
93
+ # @return [ActionController::Parameters] Permitted role assignment parameters
43
94
  def securial_role_assignment_params
44
95
  params.expect(securial_role_assignment: [:user_id, :role_id])
45
96
  end
@@ -1,14 +1,47 @@
1
1
  module Securial
2
+ #
3
+ # RolesController
4
+ #
5
+ # Controller for managing roles in the Securial authorization system.
6
+ #
7
+ # This controller handles role management operations including:
8
+ # - Creating new roles
9
+ # - Listing available roles
10
+ # - Updating role properties
11
+ # - Deleting roles
12
+ #
13
+ # All operations require admin authentication and are typically used for
14
+ # setting up and managing the application's permission structure.
15
+ #
16
+ # Routes typically mounted at Securial/admins/roles/* in the host application.
17
+ #
2
18
  class RolesController < ApplicationController
3
19
  before_action :set_securial_role, only: [:show, :update, :destroy]
4
20
 
21
+ # Lists all roles in the system.
22
+ #
23
+ # Retrieves all roles for administrative display and management.
24
+ #
25
+ # @return [void] Renders index view with all roles
5
26
  def index
6
27
  @securial_roles = Role.all
7
28
  end
8
29
 
30
+ # Shows details for a specific role.
31
+ #
32
+ # Retrieves and displays information for a single role.
33
+ #
34
+ # @param [Integer] params[:id] The ID of the role to display
35
+ # @return [void] Renders show view with the specified role
9
36
  def show
10
37
  end
11
38
 
39
+ # Creates a new role in the system.
40
+ #
41
+ # Adds a new role with the provided attributes.
42
+ #
43
+ # @param [Hash] params[:securial_role] Role attributes including role_name and hide_from_profile
44
+ # @return [void] Renders the created role with 201 Created status or errors with 422
12
45
  def create
13
46
  @securial_role = Role.new(securial_role_params)
14
47
 
@@ -19,6 +52,13 @@ module Securial
19
52
  end
20
53
  end
21
54
 
55
+ # Updates an existing role.
56
+ #
57
+ # Modifies the attributes of an existing role.
58
+ #
59
+ # @param [Integer] params[:id] The ID of the role to update
60
+ # @param [Hash] params[:securial_role] Updated role attributes
61
+ # @return [void] Renders the updated role or errors with 422 status
22
62
  def update
23
63
  if @securial_role.update(securial_role_params)
24
64
  render :show
@@ -27,6 +67,12 @@ module Securial
27
67
  end
28
68
  end
29
69
 
70
+ # Deletes an existing role.
71
+ #
72
+ # Permanently removes a role from the system.
73
+ #
74
+ # @param [Integer] params[:id] The ID of the role to delete
75
+ # @return [void] Returns 204 No Content status
30
76
  def destroy
31
77
  @securial_role.destroy
32
78
  head :no_content
@@ -34,10 +80,18 @@ module Securial
34
80
 
35
81
  private
36
82
 
83
+ # Finds and sets a specific role for show, update and destroy actions.
84
+ #
85
+ # @param [Integer] params[:id] The ID of the role to find
86
+ # @return [void]
37
87
  def set_securial_role
38
88
  @securial_role = Role.find(params[:id])
39
89
  end
40
90
 
91
+ # Permits and extracts role parameters from the request.
92
+ #
93
+ # @return [ActionController::Parameters] Permitted role parameters
94
+ #
41
95
  def securial_role_params
42
96
  params.expect(securial_role: [:role_name, :hide_from_profile])
43
97
  end