aha_builder_core 1.0.4 → 1.0.5
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.
- checksums.yaml +4 -4
- data/lib/aha/auth/version.rb +1 -1
- data/lib/generators/aha_builder_core/auth/USAGE +27 -0
- data/lib/generators/aha_builder_core/auth/auth_generator.rb +138 -0
- data/lib/generators/aha_builder_core/auth/templates/authentication.rb.tt +57 -0
- data/lib/generators/aha_builder_core/auth/templates/current.rb.tt +3 -0
- data/lib/generators/aha_builder_core/auth/templates/migration.rb.tt +21 -0
- data/lib/generators/aha_builder_core/auth/templates/sessions_controller.rb.tt +50 -0
- data/lib/generators/aha_builder_core/auth/templates/user.rb.tt +8 -0
- data/lib/generators/aha_builder_core/blob_storage/USAGE +19 -0
- data/lib/generators/aha_builder_core/blob_storage/blob_storage_generator.rb +43 -0
- data/lib/generators/aha_builder_core/blob_storage/templates/storage.yml.tt +16 -0
- metadata +11 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8242fcb16eead2a99076077e2722d5815501f5fc3234eebfbf53a37648d79e5d
|
|
4
|
+
data.tar.gz: 854469a275e4e2cc06469e9d3fd967c3039b565c66ca344ac2df39f37daf94d0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 51e7277709855e865c53940dfec85081cab5c9c2b0b74c53cfd383a70c5cc01ad4cd725451f0268ae3515d2c0985ec17704ae3fb1d51a4f8204a398f438aa13b
|
|
7
|
+
data.tar.gz: 9385a9e023d76fc58f4dd802cb0dcacd2f773e055f75b549e2be59f11c2832dd9cc357de0c31c13e857ccf85e9645d26c760b1a765e8a61f9f95886f0976b50d
|
data/lib/aha/auth/version.rb
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
Generates authentication scaffolding for Aha Builder Core integration.
|
|
3
|
+
Creates User model, migration, SessionsController, Authentication concern,
|
|
4
|
+
Current model, and routes.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
bin/rails generate aha_builder_core:auth
|
|
8
|
+
|
|
9
|
+
This will create:
|
|
10
|
+
app/models/user.rb
|
|
11
|
+
app/models/current.rb
|
|
12
|
+
app/controllers/sessions_controller.rb
|
|
13
|
+
app/controllers/concerns/authentication.rb
|
|
14
|
+
db/migrate/XXXXXXXXXXXXXX_create_users.rb
|
|
15
|
+
|
|
16
|
+
And add routes:
|
|
17
|
+
get "login" => "sessions#new"
|
|
18
|
+
get "callback" => "sessions#callback"
|
|
19
|
+
delete "logout" => "sessions#logout"
|
|
20
|
+
|
|
21
|
+
You can add custom attributes to the User model:
|
|
22
|
+
bin/rails generate aha_builder_core:auth company:string role:string
|
|
23
|
+
|
|
24
|
+
This adds company and role columns to the users table migration.
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
--skip-migration Skip generating the database migration
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module AhaBuilderCore
|
|
7
|
+
class AuthGenerator < Rails::Generators::Base
|
|
8
|
+
include ActiveRecord::Generators::Migration
|
|
9
|
+
|
|
10
|
+
source_root File.expand_path("templates", __dir__)
|
|
11
|
+
|
|
12
|
+
argument :attributes, type: :array, default: [], banner: "field[:type] field[:type]"
|
|
13
|
+
|
|
14
|
+
class_option :skip_migration, type: :boolean, default: false, desc: "Skip migration generation"
|
|
15
|
+
|
|
16
|
+
def generate_migration_file
|
|
17
|
+
return if options[:skip_migration]
|
|
18
|
+
|
|
19
|
+
migration_template "migration.rb.tt", File.join(db_migrate_path, "create_users.rb")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def create_user_model
|
|
23
|
+
template "user.rb.tt", "app/models/user.rb"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_current_model
|
|
27
|
+
template "current.rb.tt", "app/models/current.rb"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def create_sessions_controller
|
|
31
|
+
template "sessions_controller.rb.tt", "app/controllers/sessions_controller.rb"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def create_authentication_concern
|
|
35
|
+
template "authentication.rb.tt", "app/controllers/concerns/authentication.rb"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add_routes
|
|
39
|
+
route <<~RUBY
|
|
40
|
+
get "login", to: "sessions#new", as: :new_session
|
|
41
|
+
get "callback", to: "sessions#callback", as: :session_callback
|
|
42
|
+
delete "logout", to: "sessions#logout", as: :logout
|
|
43
|
+
RUBY
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def add_inertia_auth_share
|
|
47
|
+
return unless File.exist?("app/controllers/inertia_controller.rb")
|
|
48
|
+
|
|
49
|
+
inject_into_file "app/controllers/inertia_controller.rb",
|
|
50
|
+
after: "inertia_share flash: -> { flash.to_hash }\n" do
|
|
51
|
+
<<-RUBY
|
|
52
|
+
inertia_share auth: -> {
|
|
53
|
+
{
|
|
54
|
+
user: current_user&.as_json(only: %i[id email first_name last_name])
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
RUBY
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def add_authentication_to_application_controller
|
|
62
|
+
inject_into_file "app/controllers/application_controller.rb",
|
|
63
|
+
after: "class ApplicationController < ActionController::Base\n" do
|
|
64
|
+
" include Authentication\n"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def add_typescript_types
|
|
69
|
+
types_file = "app/frontend/types/index.ts"
|
|
70
|
+
return unless File.exist?(types_file)
|
|
71
|
+
|
|
72
|
+
gsub_file types_file,
|
|
73
|
+
/export interface Flash \{\n alert\?: string\n notice\?: string\n\}\n\nexport interface SharedData \{\n flash: Flash\n \[key: string\]: unknown\n\}/,
|
|
74
|
+
<<~TYPESCRIPT.chomp
|
|
75
|
+
export interface Flash {
|
|
76
|
+
alert?: string
|
|
77
|
+
notice?: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface AuthUser {
|
|
81
|
+
id: number
|
|
82
|
+
email: string
|
|
83
|
+
first_name: string | null
|
|
84
|
+
last_name: string | null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface SharedData {
|
|
88
|
+
flash: Flash
|
|
89
|
+
auth: {
|
|
90
|
+
user: AuthUser | null
|
|
91
|
+
}
|
|
92
|
+
[key: string]: unknown
|
|
93
|
+
}
|
|
94
|
+
TYPESCRIPT
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def generate_js_routes
|
|
98
|
+
say "\nRegenerating js-routes file...", :green
|
|
99
|
+
rails_command "js:routes"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def display_instructions
|
|
103
|
+
say "\nAha Auth setup complete!", :green
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def auth_attributes
|
|
109
|
+
%w[
|
|
110
|
+
auth_identifier:string
|
|
111
|
+
email:string
|
|
112
|
+
first_name:string
|
|
113
|
+
last_name:string
|
|
114
|
+
email_verified:boolean
|
|
115
|
+
]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def all_attributes
|
|
119
|
+
@all_attributes ||= (auth_attributes + attributes.map(&:to_s)).map do |attr|
|
|
120
|
+
Rails::Generators::GeneratedAttribute.parse(attr)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def custom_attributes
|
|
125
|
+
@custom_attributes ||= attributes.map do |attr|
|
|
126
|
+
Rails::Generators::GeneratedAttribute.parse(attr.to_s)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def migration_class_name
|
|
131
|
+
"CreateUsers"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def table_name
|
|
135
|
+
"users"
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Authentication
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
before_action :authenticate
|
|
6
|
+
helper_method :current_user, :logged_in?
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def authenticate
|
|
12
|
+
return unless session[:session_token].present?
|
|
13
|
+
|
|
14
|
+
session_result = Aha::Auth.validate_session(
|
|
15
|
+
session[:session_token],
|
|
16
|
+
refresh_token: session[:refresh_token]
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
if session_result.valid?
|
|
20
|
+
if session_result.refreshed?
|
|
21
|
+
session[:session_token] = session_result.new_session_token
|
|
22
|
+
session[:refresh_token] = session_result.new_refresh_token
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@current_user = User.find_by(id: session[:user_id])
|
|
26
|
+
Current.user = @current_user
|
|
27
|
+
else
|
|
28
|
+
clear_session
|
|
29
|
+
redirect_to login_path, alert: "Your session has expired. Please log in again."
|
|
30
|
+
end
|
|
31
|
+
rescue Aha::Auth::ApiError => e
|
|
32
|
+
Rails.logger.error "Session validation failed: #{e.message}"
|
|
33
|
+
clear_session
|
|
34
|
+
redirect_to login_path, alert: "Authentication error. Please log in again."
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def current_user
|
|
38
|
+
@current_user ||= Current.user
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def logged_in?
|
|
42
|
+
current_user.present?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def require_authentication
|
|
46
|
+
return if logged_in?
|
|
47
|
+
|
|
48
|
+
redirect_to login_path(return_to: request.fullpath), alert: "You must be logged in to access this page."
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def clear_session
|
|
52
|
+
session.delete(:session_token)
|
|
53
|
+
session.delete(:refresh_token)
|
|
54
|
+
session.delete(:user_id)
|
|
55
|
+
@current_user = nil
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :users do |t|
|
|
4
|
+
t.string :auth_identifier, null: false
|
|
5
|
+
t.string :email, null: false
|
|
6
|
+
t.string :first_name
|
|
7
|
+
t.string :last_name
|
|
8
|
+
t.boolean :email_verified, default: false, null: false
|
|
9
|
+
<% custom_attributes.each do |attribute| -%>
|
|
10
|
+
<% unless attribute.virtual? -%>
|
|
11
|
+
t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
|
|
12
|
+
<% end -%>
|
|
13
|
+
<% end -%>
|
|
14
|
+
|
|
15
|
+
t.timestamps
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
add_index :users, :auth_identifier, unique: true
|
|
19
|
+
add_index :users, :email, unique: true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
class SessionsController < ApplicationController
|
|
2
|
+
skip_before_action :authenticate, only: [ :new, :callback ]
|
|
3
|
+
|
|
4
|
+
def new
|
|
5
|
+
redirect_to Aha::Auth.login_url(
|
|
6
|
+
state: { return_to: params[:return_to] || root_path }.to_json,
|
|
7
|
+
session:
|
|
8
|
+
), allow_other_host: true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def callback
|
|
12
|
+
if params[:code].present?
|
|
13
|
+
result = Aha::Auth.authenticate_with_code(code: params[:code], session:)
|
|
14
|
+
|
|
15
|
+
user = User.find_or_initialize_by(auth_identifier: result[:user]["id"])
|
|
16
|
+
|
|
17
|
+
user.update!(
|
|
18
|
+
email: result[:user]["email"],
|
|
19
|
+
first_name: result[:user]["first_name"],
|
|
20
|
+
last_name: result[:user]["last_name"],
|
|
21
|
+
email_verified: result[:user]["email_verified"]
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
session[:session_token] = result[:session_token]
|
|
25
|
+
session[:refresh_token] = result[:refresh_token]
|
|
26
|
+
session[:user_id] = user.id
|
|
27
|
+
|
|
28
|
+
state = JSON.parse(params[:state]) rescue {}
|
|
29
|
+
redirect_to state["return_to"] || root_path
|
|
30
|
+
else
|
|
31
|
+
redirect_to new_session_path, alert: "Authentication failed. Please try again."
|
|
32
|
+
end
|
|
33
|
+
rescue Aha::Auth::ApiError => e
|
|
34
|
+
Rails.logger.error "Authentication failed: #{e.message}"
|
|
35
|
+
redirect_to new_session_path, alert: "Authentication failed. Please try again."
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def logout
|
|
39
|
+
if session[:session_token]
|
|
40
|
+
begin
|
|
41
|
+
Aha::Auth.logout(session_token: session[:session_token])
|
|
42
|
+
rescue => e
|
|
43
|
+
Rails.logger.error "Logout error: #{e.message}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
clear_session
|
|
48
|
+
redirect_to root_path, notice: "Successfully signed out."
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
class User < ApplicationRecord
|
|
2
|
+
validates :auth_identifier, presence: true, uniqueness: true
|
|
3
|
+
validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
|
|
4
|
+
|
|
5
|
+
def name
|
|
6
|
+
"#{first_name} #{last_name}".strip.presence || email
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
Configures Active Storage to use Aha blob storage service.
|
|
3
|
+
|
|
4
|
+
This generator sets up S3-compatible blob storage for file uploads
|
|
5
|
+
using environment variables provided by the container orchestrator.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
bin/rails generate aha_builder_core:blob_storage
|
|
9
|
+
|
|
10
|
+
This will:
|
|
11
|
+
- Run active_storage:install to create required migrations
|
|
12
|
+
- Add aws-sdk-s3 gem to Gemfile
|
|
13
|
+
- Update config/storage.yml with blob storage configuration
|
|
14
|
+
- Set config.active_storage.service = :blob in development.rb
|
|
15
|
+
- Set config.active_storage.service = :blob in production.rb
|
|
16
|
+
|
|
17
|
+
Prerequisites:
|
|
18
|
+
The container must have blob storage attached via the SetupBlobStorageTool
|
|
19
|
+
which sets the required BLOB_UPLOADS_S3_* environment variables.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module AhaBuilderCore
|
|
6
|
+
class BlobStorageGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
def install_active_storage
|
|
10
|
+
run "bin/rails active_storage:install"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add_aws_sdk_gem
|
|
14
|
+
gem "aws-sdk-s3", require: false unless gem_exists?("aws-sdk-s3")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update_storage_yml
|
|
18
|
+
template "storage.yml.tt", "config/storage.yml", force: true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def update_development_environment
|
|
22
|
+
gsub_file "config/environments/development.rb",
|
|
23
|
+
/config\.active_storage\.service\s*=\s*:\w+/,
|
|
24
|
+
"config.active_storage.service = :blob"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def update_production_environment
|
|
28
|
+
gsub_file "config/environments/production.rb",
|
|
29
|
+
/config\.active_storage\.service\s*=\s*:\w+/,
|
|
30
|
+
"config.active_storage.service = :blob"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def display_instructions
|
|
34
|
+
say "\nBlob storage configured!", :green
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def gem_exists?(gem_name)
|
|
40
|
+
File.read("Gemfile").include?(gem_name)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
test:
|
|
2
|
+
service: Disk
|
|
3
|
+
root: <%%= Rails.root.join("tmp/storage") %>
|
|
4
|
+
|
|
5
|
+
local:
|
|
6
|
+
service: Disk
|
|
7
|
+
root: <%%= Rails.root.join("storage") %>
|
|
8
|
+
|
|
9
|
+
blob:
|
|
10
|
+
service: S3
|
|
11
|
+
endpoint: <%%= ENV.fetch('BLOB_UPLOADS_S3_ENDPOINT') %>
|
|
12
|
+
access_key_id: <%%= ENV.fetch('BLOB_UPLOADS_S3_ACCESS_KEY_ID') %>
|
|
13
|
+
secret_access_key: <%%= ENV.fetch('BLOB_UPLOADS_S3_SECRET_ACCESS_KEY') %>
|
|
14
|
+
region: <%%= ENV.fetch('BLOB_UPLOADS_S3_REGION', 'us-east-1') %>
|
|
15
|
+
bucket: <%%= ENV.fetch('BLOB_UPLOADS_S3_BUCKET') %>
|
|
16
|
+
force_path_style: true
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: aha_builder_core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aha! Labs Inc.
|
|
@@ -125,6 +125,16 @@ files:
|
|
|
125
125
|
- lib/aha/auth/users_resource.rb
|
|
126
126
|
- lib/aha/auth/version.rb
|
|
127
127
|
- lib/aha_builder_core.rb
|
|
128
|
+
- lib/generators/aha_builder_core/auth/USAGE
|
|
129
|
+
- lib/generators/aha_builder_core/auth/auth_generator.rb
|
|
130
|
+
- lib/generators/aha_builder_core/auth/templates/authentication.rb.tt
|
|
131
|
+
- lib/generators/aha_builder_core/auth/templates/current.rb.tt
|
|
132
|
+
- lib/generators/aha_builder_core/auth/templates/migration.rb.tt
|
|
133
|
+
- lib/generators/aha_builder_core/auth/templates/sessions_controller.rb.tt
|
|
134
|
+
- lib/generators/aha_builder_core/auth/templates/user.rb.tt
|
|
135
|
+
- lib/generators/aha_builder_core/blob_storage/USAGE
|
|
136
|
+
- lib/generators/aha_builder_core/blob_storage/blob_storage_generator.rb
|
|
137
|
+
- lib/generators/aha_builder_core/blob_storage/templates/storage.yml.tt
|
|
128
138
|
homepage: https://www.aha.io
|
|
129
139
|
licenses:
|
|
130
140
|
- MIT
|