discourse_dev 0.0.9 → 0.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c041cf9d05c586e547562729b749c5f86bc388a8311cd2a12fbb48e6ef3d56f
4
- data.tar.gz: 69ddc047893ac6f4deeaf5f723eceb4317f6ba17a9d7b18bccfe520455286e76
3
+ metadata.gz: ee90422d22b330921c72be89d86be91edf94966537d869c06efb2687762853f4
4
+ data.tar.gz: cb5b3d27e5a93011c2ef10ff77d73b5393ed9a1f04a7efb538ef2f6a67776e12
5
5
  SHA512:
6
- metadata.gz: afcf13989b8677fe40aa5c80201d6b8fcc77bd1f7465bc03d8fbce153d009b2efd9a71de72f5950820d5f7b275ec7bbff16bed5d7b43c7264af02a382aa3b54f
7
- data.tar.gz: dbad0056c7af12f508f623b7ddaac2ffbf04c452fb26580d257822fc34d990146cf4722375450934de3b78e2ec8e5f70f594e52764e3c68f6448f9c78eb71181
6
+ metadata.gz: 396e17d2e66c01d8a8e75381bd3a36264ddfc7d01ffc1dc3a578ee938a328805b35eda592d603388d38cba278a9c089a7a71a2ea03ba67a5bc3c30130b94a37a
7
+ data.tar.gz: b8fe74f3a03140cad606e9479b043e30b23c7eaf9b1f098e06613ad7c8662b3c742aab901e03965e64c0dfa0110b6c7cfa7a2a77d97e8e97b1cda63ac4c6e0db
@@ -0,0 +1,120 @@
1
+ <%# Layout/CSS borrowed from Omniauth's Form system %>
2
+ <!DOCTYPE html>
3
+ <html>
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6
+ <title>Fake DiscourseConnect Provider</title>
7
+ <style type='text/css'>
8
+ body {
9
+ background: #ccc;
10
+ font-family: "Lucida Grande", "Lucida Sans", Helvetica, Arial, sans-serif;
11
+ }
12
+
13
+ h1 {
14
+ text-align: center;
15
+ margin: 30px auto 0px;
16
+ font-size: 18px;
17
+ padding: 10px 10px 15px;
18
+ background: #555;
19
+ color: white;
20
+ width: 320px;
21
+ border: 10px solid #444;
22
+ border-bottom: 0;
23
+ -moz-border-radius-topleft: 10px;
24
+ -moz-border-radius-topright: 10px;
25
+ -webkit-border-top-left-radius: 10px;
26
+ -webkit-border-top-right-radius: 10px;
27
+ border-top-left-radius: 10px;
28
+ border-top-right-radius: 10px;
29
+ }
30
+
31
+ h1,
32
+ form {
33
+ -moz-box-shadow: 2px 2px 7px rgba(0, 0, 0, 0.3);
34
+ -webkit-box-shadow: 2px 2px 7px rgba(0, 0, 0, 0.3);
35
+ }
36
+
37
+ form {
38
+ background: white;
39
+ border: 10px solid #eee;
40
+ border-top: 0;
41
+ padding: 20px;
42
+ margin: 0px auto 40px;
43
+ width: 300px;
44
+ -moz-border-radius-bottomleft: 10px;
45
+ -moz-border-radius-bottomright: 10px;
46
+ -webkit-border-bottom-left-radius: 10px;
47
+ -webkit-border-bottom-right-radius: 10px;
48
+ border-bottom-left-radius: 10px;
49
+ border-bottom-right-radius: 10px;
50
+ }
51
+
52
+ label {
53
+ display: block;
54
+ font-weight: bold;
55
+ margin-bottom: 5px;
56
+ }
57
+
58
+ input, select {
59
+ font-size: 18px;
60
+ padding: 4px 8px;
61
+ display: block;
62
+ margin-bottom: 10px;
63
+ width: 280px;
64
+ }
65
+
66
+ select {
67
+ width: calc(280px + 20px);
68
+ }
69
+
70
+ button {
71
+ font-size: 22px;
72
+ padding: 4px 8px;
73
+ display: block;
74
+ margin: 20px auto 0;
75
+ }
76
+
77
+ fieldset {
78
+ border: 1px solid #ccc;
79
+ border-left: 0;
80
+ border-right: 0;
81
+ padding: 10px 0;
82
+ }
83
+
84
+ fieldset input {
85
+ width: 260px;
86
+ font-size: 16px;
87
+ }
88
+
89
+ details summary {
90
+ cursor: pointer;
91
+ margin-bottom: 10px;
92
+ }
93
+ </style>
94
+ </head>
95
+ <body>
96
+ <h1>Fake DiscourseConnect Provider</h1>
97
+ <form method='post' noValidate='noValidate'>
98
+ <input type='hidden' name='sso_payload' value='<%= @payload %>'/>
99
+ <% @simple_fields.each do |f| %>
100
+ <label for='<%= f %>'><%= f %>:</label><input type='text' id='<%= f %>' name='<%= f %>' value='<%= @defaults[f] %>'/>
101
+ <% end %>
102
+ <details>
103
+ <summary>Advanced</summary>
104
+ <% @advanced_fields.each do |f| %>
105
+ <% if @bools.include? f %>
106
+ <label for='<%= f %>'><%= f %>:</label>
107
+ <select name="<%= f %>" id="<%= f %>">
108
+ <% ["", "true", "false"].each do |opt| %>
109
+ <option <%= "selected" if @defaults[f] == opt %> value="<%= opt %>"><%= opt %></option>
110
+ <% end %>
111
+ </select>
112
+ <% else %>
113
+ <label for='<%= f %>'><%= f %>:</label><input type='text' id='<%= f %>' name='<%= f %>' value='<%= @defaults[f] %>'/>
114
+ <% end %>
115
+ <% end %>
116
+ </details>
117
+ <button type='submit'>Go</button>
118
+ </form>
119
+ </body>
120
+ </html>
data/auth/plugin.rb ADDED
@@ -0,0 +1,220 @@
1
+ # frozen_string_literal: true
2
+
3
+ # name: discourse-development-auth
4
+ # about: A fake authentication provider for development puposes only
5
+ # version: 1.0
6
+ # authors: David Taylor
7
+ # url: https://github.com/discourse/discourse-development-auth
8
+
9
+ raise "discourse-development-auth is highly insecure and should not be installed in production" if Rails.env.production?
10
+
11
+ PLUGIN_NAME = "discourse-development-auth"
12
+
13
+ module ::OmniAuth
14
+ module Strategies
15
+ class Development
16
+ include ::OmniAuth::Strategy
17
+
18
+ FIELDS = %w{
19
+ uid
20
+ name
21
+ email
22
+ email_verified
23
+ nickname
24
+ first_name
25
+ last_name
26
+ location
27
+ description
28
+ image
29
+ }
30
+
31
+ COOKIE = "development-auth-defaults"
32
+
33
+ def request_phase
34
+ return unless is_allowed?
35
+ if (env['REQUEST_METHOD'] == 'POST') && (request.params['uid'])
36
+ data = request.params.slice(*FIELDS)
37
+
38
+ r = Rack::Response.new
39
+ r.set_cookie(COOKIE, {value: data.to_json, path: "/", expires: 1.month.from_now})
40
+
41
+ uri = URI.parse(callback_path)
42
+ uri.query = URI.encode_www_form(data)
43
+ r.redirect(uri)
44
+
45
+ return r.finish
46
+ end
47
+
48
+ build_form.to_response
49
+ end
50
+
51
+ def build_form
52
+ token = begin
53
+ verifier = CSRFTokenVerifier.new
54
+ verifier.call(env)
55
+ verifier.form_authenticity_token
56
+ end
57
+
58
+ request = Rack::Request.new(env)
59
+ raw_defaults = request.cookies[COOKIE] || "{}"
60
+ defaults = JSON.parse(raw_defaults) rescue {}
61
+ defaults["uid"] = SecureRandom.hex(8) unless defaults["uid"].present?
62
+ defaults["email_verified"] = "true" unless defaults["email_verified"].present?
63
+
64
+ OmniAuth::Form.build(:title => "Fake Authentication Provider") do
65
+ html "\n<input type='hidden' name='authenticity_token' value='#{token}'/>"
66
+
67
+ FIELDS.each do |f|
68
+ label_field(f, f)
69
+ if f == "email_verified"
70
+ html "<input type='checkbox' id='#{f}' name='#{f}' value='true' #{"checked" if defaults[f] == "true"}/>"
71
+ else
72
+ html "<input type='text' id='#{f}' name='#{f}' value='#{defaults[f]}'/>"
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def callback_phase
79
+ return unless is_allowed?
80
+ super
81
+ end
82
+
83
+ def auth_hash
84
+ info = request.params.slice(*FIELDS)
85
+ uid = info.delete("uid")
86
+ email_verified = (info.delete("email_verified") == "true")
87
+ OmniAuth::Utils.deep_merge(super, {
88
+ 'uid' => uid,
89
+ 'info' => info,
90
+ 'extra' => { "raw_info" => { "email_verified" => email_verified } }
91
+ })
92
+ end
93
+
94
+ def is_allowed?
95
+ return true if DiscourseDev.config.allow_anonymous_to_impersonate
96
+ fail!("Enable `allow_anonymous_to_impersonate` setting in `config/dev.yml` file.")
97
+ false
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ class DevelopmentAuthenticator < Auth::ManagedAuthenticator
104
+ def name
105
+ 'developmentauth'
106
+ end
107
+
108
+ def can_revoke?
109
+ true
110
+ end
111
+
112
+ def can_connect_existing_user?
113
+ true
114
+ end
115
+
116
+ def enabled?
117
+ DiscourseDev.auth_plugin_enabled?
118
+ end
119
+
120
+ def register_middleware(omniauth)
121
+ omniauth.provider :development, name: :developmentauth
122
+ end
123
+
124
+ def primary_email_verified?(auth)
125
+ auth['extra']['raw_info']['email_verified']
126
+ end
127
+ end
128
+
129
+ auth_provider authenticator: DevelopmentAuthenticator.new
130
+
131
+
132
+ ### DiscourseConnect
133
+ after_initialize do
134
+ module ::DevelopmentAuth
135
+ class Engine < ::Rails::Engine
136
+ engine_name PLUGIN_NAME
137
+ isolate_namespace ::DevelopmentAuth
138
+ end
139
+ end
140
+
141
+ class ::DevelopmentAuth::FakeDiscourseConnectController < ::ApplicationController
142
+ requires_plugin "discourse-development-auth"
143
+
144
+ skip_before_action :check_xhr, :preload_json, :redirect_to_login_if_required, :verify_authenticity_token
145
+
146
+ SIMPLE_FIELDS = %w{
147
+ external_id
148
+ email
149
+ username
150
+ name
151
+ }
152
+ ADVANCED_FIELDS = SingleSignOn::ACCESSORS.map(&:to_s) - SIMPLE_FIELDS
153
+ FIELDS = SIMPLE_FIELDS + ADVANCED_FIELDS
154
+
155
+ BOOLS = SingleSignOn::BOOLS.map(&:to_s)
156
+
157
+ COOKIE = "development-auth-discourseconnect-defaults"
158
+
159
+ def auth
160
+ return unless is_allowed?
161
+
162
+ params.require(:sso)
163
+ @payload = request.query_string
164
+ sso = SingleSignOn.parse(@payload, SiteSetting.discourse_connect_secret)
165
+
166
+ if request.method == "POST" && params[:external_id]
167
+ data = {}
168
+ FIELDS.each do |f|
169
+ sso.send(:"#{f}=", params[f])
170
+ data[f] = params[f]
171
+ cookies[COOKIE] = { value: data.to_json, path: "/", expires: 1.month.from_now }
172
+ end
173
+
174
+ return redirect_to sso.to_url(sso.return_sso_url)
175
+ end
176
+
177
+ raw_defaults = cookies[COOKIE] || "{}"
178
+ @defaults = JSON.parse(raw_defaults) rescue {}
179
+ @defaults["return_sso_url"] = sso.return_sso_url
180
+ @defaults["nonce"] = sso.nonce
181
+ @defaults["external_id"] = SecureRandom.hex(8) unless @defaults["external_id"].present?
182
+ render_form
183
+ end
184
+
185
+ private
186
+
187
+ def render_form
188
+ @simple_fields = SIMPLE_FIELDS
189
+ @advanced_fields = ADVANCED_FIELDS
190
+ @bools = BOOLS
191
+ append_view_path(File.expand_path("../app/views", __FILE__))
192
+ render template: "fake_discourse_connect/form", layout: false
193
+ end
194
+ end
195
+
196
+ DevelopmentAuth::Engine.routes.draw do
197
+ get "/fake-discourse-connect" => "fake_discourse_connect#auth"
198
+ post "/fake-discourse-connect" => "fake_discourse_connect#auth"
199
+ end
200
+
201
+ Discourse::Application.routes.append do
202
+ mount ::DevelopmentAuth::Engine, at: "/development-auth"
203
+ end
204
+
205
+ DiscourseSingleSignOn.singleton_class.prepend(Module.new do
206
+ def sso_url
207
+ if DiscourseDev.auth_plugin_enabled?
208
+ return "#{Discourse.base_path}/development-auth/fake-discourse-connect"
209
+ end
210
+ super
211
+ end
212
+ end)
213
+
214
+ EnableSsoValidator.prepend(Module.new do
215
+ def valid_value?(val)
216
+ return true if DiscourseDev.auth_plugin_enabled?
217
+ super
218
+ end
219
+ end)
220
+ end
@@ -4,3 +4,8 @@ site_settings:
4
4
  seed: 1
5
5
  start_date: "01/01/2020"
6
6
  max_likes_count: 10
7
+ auth_plugin_enabled: true
8
+ allow_anonymous_to_impersonate: false
9
+ new_user:
10
+ username: new_user
11
+ email: new_user@example.com
@@ -0,0 +1,6 @@
1
+ en:
2
+ js:
3
+ login:
4
+ developmentauth:
5
+ title: DevelopmentAuth
6
+ name: DevelopmentAuth
data/lib/discourse_dev.rb CHANGED
@@ -4,16 +4,40 @@ require 'i18n'
4
4
 
5
5
  Dir[File.dirname(__FILE__) + '/**/*.rb'].each {|file| require file }
6
6
 
7
- I18n.load_path += Dir[File.join(__dir__, 'faker', 'locales', '**/*.yml')]
8
- I18n.reload! if I18n.backend.initialized?
9
-
10
7
  module DiscourseDev
11
8
  require 'discourse_dev/railtie'
12
9
  require 'discourse_dev/engine'
13
10
 
11
+ def self.auth_plugin_enabled?
12
+ config.auth_plugin_enabled
13
+ end
14
+
14
15
  def self.config
15
16
  @config ||= Config.new
16
17
  end
18
+
19
+ def self.auth_plugin
20
+ return unless auth_plugin_enabled?
21
+
22
+ @auth_plugin ||= begin
23
+ path = File.join(root, 'auth', 'plugin.rb')
24
+ source = File.read(path)
25
+ metadata = Plugin::Metadata.parse(source)
26
+ Plugin::Instance.new(metadata, path)
27
+ end
28
+ end
29
+
30
+ def self.settings_file
31
+ File.join(root, "config", "settings.yml")
32
+ end
33
+
34
+ def self.client_locale_files(locale_str)
35
+ Dir[File.join(root, "config", "locales", "client*.#{locale_str}.yml")]
36
+ end
37
+
38
+ def self.root
39
+ File.expand_path("..", __dir__)
40
+ end
17
41
  end
18
42
 
19
43
  require "active_record/database_configurations"
@@ -1,14 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rails'
4
+ require 'highline/import'
4
5
 
5
6
  module DiscourseDev
6
7
  class Config
7
- attr_reader :config, :default_config
8
+ attr_reader :config, :default_config, :file_path
8
9
 
9
10
  def initialize
10
- default_file_path = File.join(File.expand_path(__dir__), "config.yml")
11
- file_path = File.join(Rails.root, "config", "dev.yml")
11
+ default_file_path = File.join(DiscourseDev.root, "config", "dev.yml")
12
+ @file_path = File.join(Rails.root, "config", "dev.yml")
12
13
  @default_config = YAML.load_file(default_file_path)
13
14
 
14
15
  if File.exists?(file_path)
@@ -23,6 +24,7 @@ module DiscourseDev
23
24
  def update!
24
25
  update_site_settings
25
26
  create_admin_user
27
+ create_new_user
26
28
  set_seed
27
29
  end
28
30
 
@@ -54,21 +56,24 @@ module DiscourseDev
54
56
  settings = config["admin"]
55
57
 
56
58
  if settings.present?
57
- email = settings["email"]
59
+ create_admin_user_from_settings(settings)
60
+ else
61
+ create_admin_user_from_input
62
+ end
63
+ end
64
+
65
+ def create_new_user
66
+ settings = config["new_user"]
67
+
68
+ if settings.present?
69
+ email = settings["email"] || "new_user@example.com"
58
70
 
59
- admin = ::User.create!(
71
+ new_user = ::User.create!(
60
72
  email: email,
61
- username: settings["username"] || UserNameSuggester.suggest(email),
62
- password: settings["password"]
73
+ username: settings["username"] || UserNameSuggester.suggest(email)
63
74
  )
64
- admin.grant_admin!
65
- if admin.trust_level < 1
66
- admin.change_trust_level!(1)
67
- end
68
- admin.email_tokens.update_all confirmed: true
69
- admin.activate
70
- else
71
- Rake::Task['admin:create'].invoke
75
+ new_user.email_tokens.update_all confirmed: true
76
+ new_user.activate
72
77
  end
73
78
  end
74
79
 
@@ -83,7 +88,69 @@ module DiscourseDev
83
88
 
84
89
  def method_missing(name)
85
90
  name = name.to_s
86
- config[name] || default_config[name]
91
+ return config[name] if config.keys.include?(name)
92
+ default_config[name]
93
+ end
94
+
95
+ def create_admin_user_from_settings(settings)
96
+ email = settings["email"]
97
+
98
+ admin = ::User.create!(
99
+ email: email,
100
+ username: settings["username"] || UserNameSuggester.suggest(email),
101
+ password: settings["password"]
102
+ )
103
+ admin.grant_admin!
104
+ if admin.trust_level < 1
105
+ admin.change_trust_level!(1)
106
+ end
107
+ admin.email_tokens.update_all confirmed: true
108
+ admin.activate
109
+ end
110
+
111
+ def create_admin_user_from_input
112
+ begin
113
+ email = ask("Email: ")
114
+ password = ask("Password (optional, press ENTER to skip): ")
115
+ username = UserNameSuggester.suggest(email)
116
+
117
+ admin = ::User.new(
118
+ email: email,
119
+ username: username
120
+ )
121
+
122
+ if password.present?
123
+ admin.password = password
124
+ else
125
+ puts "Once site is running use https://localhost:9292/user/#{username}/become to access the account in development"
126
+ end
127
+
128
+ admin.name = ask("Full name: ") if SiteSetting.full_name_required
129
+ saved = admin.save
130
+
131
+ if saved
132
+ File.open(file_path, 'a') do | file|
133
+ file.puts("admin:")
134
+ file.puts(" username: #{admin.username}")
135
+ file.puts(" email: #{admin.email}")
136
+ file.puts(" password: #{password}") if password.present?
137
+ end
138
+ else
139
+ say(admin.errors.full_messages.join("\n"))
140
+ end
141
+ end while !saved
142
+
143
+ admin.active = true
144
+ admin.save
145
+
146
+ admin.grant_admin!
147
+ if admin.trust_level < 1
148
+ admin.change_trust_level!(1)
149
+ end
150
+ admin.email_tokens.update_all confirmed: true
151
+ admin.activate
152
+
153
+ say("\nAdmin account created successfully with username #{admin.username}")
87
154
  end
88
155
  end
89
156
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DiscourseDev
4
- VERSION = "0.0.9"
4
+ VERSION = "0.1.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: discourse_dev
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vinoth Kannan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-09 00:00:00.000000000 Z
11
+ date: 2021-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faker
@@ -65,6 +65,8 @@ files:
65
65
  - LICENSE.txt
66
66
  - README.md
67
67
  - Rakefile
68
+ - auth/app/views/fake_discourse_connect/form.html.erb
69
+ - auth/plugin.rb
68
70
  - avatars/03F55412-DE8A-4F83-AAA6-D67EE5CE48DA-200w.jpeg
69
71
  - avatars/1C4EEDC2-FE9C-40B3-A2C9-A038873EE692-200w.jpeg
70
72
  - avatars/26CFEFB3-21C8-49FC-8C19-8E6A62B6D2E0-200w.jpeg
@@ -78,12 +80,14 @@ files:
78
80
  - avatars/BA0CB1F2-8C79-4376-B13B-DD5FB8772537-200w.jpeg
79
81
  - avatars/E0B4CAB3-F491-4322-BEF2-208B46748D4A-200w.jpeg
80
82
  - avatars/FBEBF655-4886-455A-A4A4-D62B77DD419B-200w.jpeg
83
+ - config/dev.yml
84
+ - config/locales/client.en.yml
85
+ - config/locales/faker.en.yml
81
86
  - config/routes.rb
82
87
  - discourse_dev.gemspec
83
88
  - lib/discourse_dev.rb
84
89
  - lib/discourse_dev/category.rb
85
90
  - lib/discourse_dev/config.rb
86
- - lib/discourse_dev/config.yml
87
91
  - lib/discourse_dev/engine.rb
88
92
  - lib/discourse_dev/group.rb
89
93
  - lib/discourse_dev/post.rb
@@ -96,7 +100,6 @@ files:
96
100
  - lib/discourse_dev/user.rb
97
101
  - lib/discourse_dev/version.rb
98
102
  - lib/faker/discourse.rb
99
- - lib/faker/locales/en.yml
100
103
  homepage: https://github.com/discourse/discourse_dev
101
104
  licenses:
102
105
  - MIT