discourse_dev 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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