Integrate Discourse Connect (SSO) #129
| @ -19,6 +19,8 @@ LDAP_SUFFIX='dc=kosmos,dc=org' | ||||
| WEBHOOKS_ALLOWED_IPS='10.1.1.163' | ||||
| 
 | ||||
| DISCOURSE_PUBLIC_URL='https://community.kosmos.org' | ||||
| DISCOURSE_CONNECT_SECRET='discourse_connect_ftw' | ||||
| 
 | ||||
| GITEA_PUBLIC_URL='https://gitea.kosmos.org' | ||||
| MASTODON_PUBLIC_URL='https://kosmos.social' | ||||
| MEDIAWIKI_PUBLIC_URL='https://wiki.kosmos.org' | ||||
|  | ||||
| @ -1,3 +1,6 @@ | ||||
| DISCOURSE_PUBLIC_URL='http://discourse.example.com' | ||||
| DISCOURSE_CONNECT_SECRET='discourse_connect_ftw' | ||||
| 
 | ||||
| EJABBERD_API_URL='http://xmpp.example.com/api' | ||||
| 
 | ||||
| BTCPAY_API_URL='http://btcpay.example.com/api/v1' | ||||
|  | ||||
							
								
								
									
										6
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								Gemfile
									
									
									
									
									
								
							| @ -40,6 +40,9 @@ gem 'net-ldap' | ||||
| gem "rqrcode", "~> 2.0" | ||||
| gem 'rails-settings-cached', '~> 2.8.3' | ||||
| gem 'pagy', '~> 6.0', '>= 6.0.2' | ||||
| gem 'flipper' | ||||
| gem 'flipper-active_record' | ||||
| gem 'flipper-ui' | ||||
| 
 | ||||
| # HTTP requests | ||||
| gem 'faraday' | ||||
| @ -48,6 +51,9 @@ gem 'faraday' | ||||
| gem 'sidekiq', '< 7' | ||||
| gem 'sidekiq-scheduler' | ||||
| 
 | ||||
| # Service integrations | ||||
| gem 'discourse_api' | ||||
| 
 | ||||
| # Monitoring | ||||
| gem "sentry-ruby" | ||||
| gem "sentry-rails" | ||||
|  | ||||
							
								
								
									
										30
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								Gemfile.lock
									
									
									
									
									
								
							| @ -108,6 +108,11 @@ GEM | ||||
|       devise (>= 3.4.1) | ||||
|       net-ldap (>= 0.16.0) | ||||
|     diff-lcs (1.5.0) | ||||
|     discourse_api (2.0.0) | ||||
|       faraday (~> 2.7) | ||||
|       faraday-follow_redirects | ||||
|       faraday-multipart | ||||
|       rack (>= 1.6) | ||||
|     dotenv (2.8.1) | ||||
|     dotenv-rails (2.8.1) | ||||
|       dotenv (= 2.8.1) | ||||
| @ -126,8 +131,23 @@ GEM | ||||
|     faraday (2.7.1) | ||||
|       faraday-net_http (>= 2.0, < 3.1) | ||||
|       ruby2_keywords (>= 0.0.4) | ||||
|     faraday-follow_redirects (0.3.0) | ||||
|       faraday (>= 1, < 3) | ||||
|     faraday-multipart (1.0.4) | ||||
|       multipart-post (~> 2) | ||||
|     faraday-net_http (3.0.2) | ||||
|     ffi (1.15.5) | ||||
|     flipper (0.28.0) | ||||
|       concurrent-ruby (< 2) | ||||
|     flipper-active_record (0.28.0) | ||||
|       activerecord (>= 4.2, < 8) | ||||
|       flipper (~> 0.28.0) | ||||
|     flipper-ui (0.28.0) | ||||
|       erubi (>= 1.0.0, < 2.0.0) | ||||
|       flipper (~> 0.28.0) | ||||
|       rack (>= 1.4, < 3) | ||||
|       rack-protection (>= 1.5.3, <= 4.0.0) | ||||
|       sanitize (< 7) | ||||
|     fugit (1.7.2) | ||||
|       et-orbi (~> 1, >= 1.2.7) | ||||
|       raabro (~> 1.4) | ||||
| @ -172,6 +192,7 @@ GEM | ||||
|     mini_mime (1.1.2) | ||||
|     mini_portile2 (2.8.0) | ||||
|     minitest (5.16.3) | ||||
|     multipart-post (2.3.0) | ||||
|     net-imap (0.3.1) | ||||
|       net-protocol | ||||
|     net-ldap (0.17.1) | ||||
| @ -199,6 +220,8 @@ GEM | ||||
|     raabro (1.4.0) | ||||
|     racc (1.6.0) | ||||
|     rack (2.2.4) | ||||
|     rack-protection (3.0.6) | ||||
|       rack | ||||
|     rack-test (2.0.2) | ||||
|       rack (>= 1.3) | ||||
|     rails (7.0.4) | ||||
| @ -283,6 +306,9 @@ GEM | ||||
|     ruby2_keywords (0.0.5) | ||||
|     rufus-scheduler (3.8.2) | ||||
|       fugit (~> 1.1, >= 1.1.6) | ||||
|     sanitize (6.0.1) | ||||
|       crass (~> 1.0.2) | ||||
|       nokogiri (>= 1.12.0) | ||||
|     sentry-rails (5.8.0) | ||||
|       railties (>= 5.0) | ||||
|       sentry-ruby (~> 5.8.0) | ||||
| @ -370,10 +396,14 @@ DEPENDENCIES | ||||
|   database_cleaner | ||||
|   devise (~> 4.9.0) | ||||
|   devise_ldap_authenticatable | ||||
|   discourse_api | ||||
|   dotenv-rails | ||||
|   factory_bot_rails | ||||
|   faker | ||||
|   faraday | ||||
|   flipper | ||||
|   flipper-active_record | ||||
|   flipper-ui | ||||
|   importmap-rails | ||||
|   jbuilder (~> 2.7) | ||||
|   letter_opener | ||||
|  | ||||
| @ -114,6 +114,10 @@ command: | ||||
| * [Sidekiq](https://github.com/mperham/sidekiq/wiki/) | ||||
| * [ActiveJob](https://github.com/mperham/sidekiq/wiki/Active-Job) | ||||
| 
 | ||||
| ### Feature Flags | ||||
| 
 | ||||
| * [Flipper](https://www.flippercloud.io/docs/get-started/self-hosted) | ||||
| 
 | ||||
| ## License | ||||
| 
 | ||||
| [GNU Affero General Public License v3.0](https://choosealicense.com/licenses/agpl-3.0/) | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| class AccountController < ApplicationController | ||||
|   before_action :require_user_signed_in | ||||
|   before_action :authenticate_user! | ||||
| 
 | ||||
|   def index | ||||
|     @current_section = :account | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| class Contributions::DonationsController < ApplicationController | ||||
|   before_action :require_user_signed_in | ||||
|   before_action :authenticate_user! | ||||
| 
 | ||||
|   # GET /donations | ||||
|   # GET /donations.json | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| class Contributions::ProjectsController < ApplicationController | ||||
|   before_action :require_user_signed_in | ||||
|   before_action :authenticate_user! | ||||
| 
 | ||||
|   # GET /contributions | ||||
|   def index | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| class DashboardController < ApplicationController | ||||
|   before_action :require_user_signed_in | ||||
|   before_action :authenticate_user! | ||||
| 
 | ||||
|   def index | ||||
|     @current_section = :services | ||||
|  | ||||
							
								
								
									
										17
									
								
								app/controllers/discourse/sso_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/controllers/discourse/sso_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| class Discourse::SsoController < ApplicationController | ||||
|   before_action :authenticate_user! | ||||
| 
 | ||||
|   def connect | ||||
|     secret = Setting.discourse_connect_secret | ||||
|     sso = DiscourseApi::SingleSignOn.parse(request.query_string, secret) | ||||
|     sso.external_id = current_user.id | ||||
|     sso.email = current_user.email | ||||
|     sso.username = current_user.cn | ||||
|     sso.name = current_user.display_name | ||||
|     sso.admin = current_user.is_admin? | ||||
|     sso.sso_secret = secret | ||||
| 
 | ||||
|     redirect_to sso.to_url("#{Setting.discourse_public_url}/session/sso_login"), | ||||
|                 allow_other_host: true | ||||
|   end | ||||
| end | ||||
| @ -1,5 +1,5 @@ | ||||
| class InvitationsController < ApplicationController | ||||
|   before_action :require_user_signed_in, except: ["show"] | ||||
|   before_action :authenticate_user!, except: ["show"] | ||||
|   before_action :require_user_signed_out, only: ["show"] | ||||
| 
 | ||||
|   # GET /invitations | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| require "rqrcode" | ||||
| 
 | ||||
| class Services::LightningController < ApplicationController | ||||
|   before_action :require_user_signed_in | ||||
|   before_action :authenticate_user! | ||||
|   before_action :authenticate_with_lndhub | ||||
|   before_action :set_current_section | ||||
|   before_action :fetch_balance | ||||
|  | ||||
							
								
								
									
										30
									
								
								app/controllers/services/remotestorage_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								app/controllers/services/remotestorage_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| class Services::RemotestorageController < ApplicationController | ||||
|   before_action :require_user_signed_in | ||||
|   before_action :require_service_enabled | ||||
|   before_action :require_feature_enabled | ||||
|   before_action :set_current_section | ||||
| 
 | ||||
|   def dashboard | ||||
|     # unless current_user.services_enabled.include?(:remotestorage) | ||||
|     #   redirect_to service_remotestorage_info_path | ||||
|     # end | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|     def require_feature_enabled | ||||
|       unless Flipper.enabled?(:remotestorage, current_user) | ||||
|         http_status :forbidden | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def require_service_enabled | ||||
|       unless Setting.remotestorage_enabled? | ||||
|         http_status :not_found | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def set_current_section | ||||
|       @current_section = :services | ||||
|     end | ||||
| end | ||||
| @ -2,6 +2,9 @@ | ||||
| class Setting < RailsSettings::Base | ||||
|   cache_prefix { "v1" } | ||||
| 
 | ||||
|   field :accounts_domain, type: :string, | ||||
|     default: ENV["AKKOUNTS_DOMAIN"].presence | ||||
| 
 | ||||
|   # | ||||
|   # Internal services | ||||
|   # | ||||
| @ -41,6 +44,9 @@ class Setting < RailsSettings::Base | ||||
|   field :discourse_enabled, type: :boolean, | ||||
|     default: (ENV["DISCOURSE_PUBLIC_URL"].present?.to_s || false) | ||||
| 
 | ||||
|   field :discourse_connect_secret, type: :string, readonly: true, | ||||
|     default: ENV["DISCOURSE_CONNECT_SECRET"].presence | ||||
| 
 | ||||
|   # | ||||
|   # ejabberd | ||||
|   # | ||||
|  | ||||
| @ -7,11 +7,46 @@ | ||||
|     title: "Enable Discourse integration", | ||||
|     description: "Discourse configuration present and features enabled" | ||||
|   ) %> | ||||
|   <% if Setting.discourse_enabled? %> | ||||
|     <%= render FormElements::FieldsetComponent.new(title: "Public URL") do %> | ||||
|       <%= f.text_field :discourse_public_url, | ||||
|         value: Setting.discourse_public_url, | ||||
|         class: "w-full", disabled: true %> | ||||
|     <% end %> | ||||
| <% if Setting.discourse_enabled? %> | ||||
|   <%= render FormElements::FieldsetComponent.new(title: "Public URL") do %> | ||||
|     <%= f.text_field :discourse_public_url, | ||||
|       value: Setting.discourse_public_url, | ||||
|       class: "w-full", disabled: true %> | ||||
|   <% end %> | ||||
|   <%= render FormElements::FieldsetComponent.new(title: "Connect secret") do %> | ||||
|     <%= f.password_field :discourse_connect_secret, | ||||
|       value: Setting.discourse_connect_secret, | ||||
|       class: "w-full", disabled: true %> | ||||
|   <% end %> | ||||
| <% end %> | ||||
| </ul> | ||||
| <% if Setting.discourse_enabled? %> | ||||
|   <% content_for :documentation do %> | ||||
|     <h3 class="mt-8">How to configure Discourse</h3> | ||||
|     <ol class="list-decimal list-inside"> | ||||
|     <li class="mb-6"> | ||||
|       Set the <strong>Discourse Connect URL</strong> to the following URL: | ||||
|     </li> | ||||
|     <li data-controller="clipboard" class="mb-6 flex gap-1"> | ||||
|       <input type="text" class="grow" disabled="disabled" | ||||
|              value="https://<%= Setting.accounts_domain %>/discourse/connect" | ||||
|              data-clipboard-target="source" /> | ||||
|       <button class="btn-md btn-icon btn-blue shrink-0" | ||||
|               data-clipboard-target="trigger" data-action="clipboard#copy" | ||||
|               title="Copy to clipboard"> | ||||
|         <span class="content-initial"> | ||||
|           <%= render partial: "icons/copy", locals: { custom_class: "text-white h-4 w-4 inline" } %> | ||||
|         </span> | ||||
|         <span class="content-active hidden"> | ||||
|           <%= render partial: "icons/check", locals: { custom_class: "text-white h-4 w-4 inline" } %> | ||||
|         </span> | ||||
|       </button> | ||||
|     </li> | ||||
|     <li class="mb-6"> | ||||
|       Set the <strong>Discourse Connect Secret</strong> to the value above. | ||||
|     </li> | ||||
|     <li> | ||||
|       Enable Discourse Connect. | ||||
|     </li> | ||||
|   <% end %> | ||||
| <% end %> | ||||
|  | ||||
| @ -19,7 +19,7 @@ | ||||
|       class: "w-full", disabled: true %> | ||||
|   <% end %> | ||||
| </ul> | ||||
| <h3 class="mt-8">User default settings</h3> | ||||
| <h3 class="mt-10">User default settings</h3> | ||||
| <ul role="list"> | ||||
|   <%= render FormElements::FieldsetComponent.new( | ||||
|         title: "Default rooms", | ||||
|  | ||||
| @ -20,4 +20,10 @@ | ||||
|       </p> | ||||
|     </section> | ||||
|   <% end %> | ||||
| 
 | ||||
|   <% if content_for?(:documentation) %> | ||||
|   <section> | ||||
|     <%= yield :documentation %> | ||||
|   </section> | ||||
|   <% end %> | ||||
| <% end %> | ||||
|  | ||||
| @ -6,6 +6,10 @@ | ||||
|       <h3>Account</h3> | ||||
|       <table class="divided"> | ||||
|         <tbody> | ||||
|           <tr> | ||||
|             <th>ID</th> | ||||
|             <td><%= @user.id %></td> | ||||
|           </tr> | ||||
|           <tr> | ||||
|             <th>Created at</th> | ||||
|             <td><%= @user.created_at.strftime("%Y-%m-%d (%H:%M UTC)") %></td> | ||||
|  | ||||
| @ -73,6 +73,17 @@ | ||||
|           </p> | ||||
|         <% end %> | ||||
|       </div> | ||||
|       <% if Setting.remotestorage_enabled? && Flipper.enabled?(:remotestorage, current_user) %> | ||||
|         <div class="border border-gray-300 rounded-md hover:border-gray-400"> | ||||
|           <%= link_to services_storage_path, | ||||
|                 class: "block h-full px-6 py-6 rounded-md" do %> | ||||
|             <h3 class="mb-3.5">Storage</h3> | ||||
|             <p class="text-gray-600"> | ||||
|               Sync your data between apps and devices | ||||
|             </p> | ||||
|           <% end %> | ||||
|         </div> | ||||
|       <% end %> | ||||
|       <!-- <div class="border border-gray-300 rounded-md hover:border-gray-400 --> | ||||
|       <!--             bg-[length:80%] bg-[right_top_-30px] bg-no-repeat --> | ||||
|       <!--             bg-[url(/img/logos/icon_mastodon.svg)]"> --> | ||||
|  | ||||
| @ -1,7 +1,13 @@ | ||||
| <% | ||||
|   # TODO remove when https://github.com/hotwired/turbo/issues/203 is fixed | ||||
|   enable_turbo = !session[:user_return_to].match?('/discourse/connect') | ||||
| %> | ||||
| 
 | ||||
| <%= render HeaderCompactComponent.new(title: "Log in") %> | ||||
| 
 | ||||
| <%= render MainCompactComponent.new do %> | ||||
|   <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> | ||||
|   <%= form_for(resource, as: resource_name, url: session_path(resource_name), | ||||
|                          data: { turbo: enable_turbo.to_s }) do |f| %> | ||||
|     <%= render "devise/shared/error_messages", resource: resource %> | ||||
|     <div class="mb-6"> | ||||
|       <%= f.label :cn, 'User', class: 'block mb-2 font-bold' %> | ||||
|  | ||||
							
								
								
									
										7
									
								
								app/views/services/remotestorage/dashboard.html.erb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/views/services/remotestorage/dashboard.html.erb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| <%= render HeaderComponent.new(title: "Storage") %> | ||||
| 
 | ||||
| <%= render MainSimpleComponent.new do %> | ||||
|   <section> | ||||
|     <h3>Feature enabled</h3> | ||||
|   </section> | ||||
| <% end %> | ||||
| @ -1,7 +1,7 @@ | ||||
| require 'sidekiq/web' | ||||
| 
 | ||||
| Rails.application.routes.draw do | ||||
|   devise_for :users, controllers: { confirmations: "users/confirmations" } | ||||
|   devise_for :users, controllers: { confirmations: 'users/confirmations' } | ||||
| 
 | ||||
|   get 'welcome', to: 'welcome#index' | ||||
|   get 'check_your_email', to: 'welcome#check_your_email' | ||||
| @ -19,6 +19,8 @@ Rails.application.routes.draw do | ||||
|   resources :invitations, only: ['index', 'show', 'create', 'destroy'] | ||||
| 
 | ||||
|   namespace :services do | ||||
|     get 'storage', to: 'remotestorage#dashboard' | ||||
| 
 | ||||
|     resources :lightning, only: [:index] do | ||||
|       collection do | ||||
|         get 'transactions' | ||||
| @ -59,15 +61,20 @@ Rails.application.routes.draw do | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   get ".well-known/webfinger" => "webfinger#show" | ||||
|   get ".well-known/webfinger", to: 'webfinger#show' | ||||
| 
 | ||||
|   namespace :discourse do | ||||
|     get "connect", to: 'sso#connect' | ||||
|   end | ||||
| 
 | ||||
|   authenticate :user, ->(user) { user.is_admin? } do | ||||
|     mount Sidekiq::Web => '/sidekiq' | ||||
|     mount Sidekiq::Web, at: '/sidekiq' | ||||
|     mount Flipper::UI.app(Flipper), at: '/flipper' | ||||
|   end | ||||
| 
 | ||||
|   # Letter Opener (open "sent" emails in dev and staging) | ||||
|   if Rails.env.match(/staging|development/) | ||||
|     mount LetterOpenerWeb::Engine, at: "letter_opener" | ||||
|     mount LetterOpenerWeb::Engine, at: '/letter_opener' | ||||
|   end | ||||
| 
 | ||||
|   root to: 'dashboard#index' | ||||
|  | ||||
							
								
								
									
										22
									
								
								db/migrate/20230523120753_create_flipper_tables.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								db/migrate/20230523120753_create_flipper_tables.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| class CreateFlipperTables < ActiveRecord::Migration[7.0] | ||||
|   def self.up | ||||
|     create_table :flipper_features do |t| | ||||
|       t.string :key, null: false | ||||
|       t.timestamps null: false | ||||
|     end | ||||
|     add_index :flipper_features, :key, unique: true | ||||
| 
 | ||||
|     create_table :flipper_gates do |t| | ||||
|       t.string :feature_key, null: false | ||||
|       t.string :key, null: false | ||||
|       t.string :value | ||||
|       t.timestamps null: false | ||||
|     end | ||||
|     add_index :flipper_gates, [:feature_key, :key, :value], unique: true | ||||
|   end | ||||
| 
 | ||||
|   def self.down | ||||
|     drop_table :flipper_gates | ||||
|     drop_table :flipper_features | ||||
|   end | ||||
| end | ||||
							
								
								
									
										18
									
								
								db/schema.rb
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								db/schema.rb
									
									
									
									
									
								
							| @ -10,7 +10,7 @@ | ||||
| # | ||||
| # It's strongly recommended that you check this file into your version control system. | ||||
| 
 | ||||
| ActiveRecord::Schema[7.0].define(version: 2023_04_03_135149) do | ||||
| ActiveRecord::Schema[7.0].define(version: 2023_05_23_120753) do | ||||
|   create_table "donations", force: :cascade do |t| | ||||
|     t.integer "user_id" | ||||
|     t.integer "amount_sats" | ||||
| @ -23,6 +23,22 @@ ActiveRecord::Schema[7.0].define(version: 2023_04_03_135149) do | ||||
|     t.index ["user_id"], name: "index_donations_on_user_id" | ||||
|   end | ||||
| 
 | ||||
|   create_table "flipper_features", force: :cascade do |t| | ||||
|     t.string "key", null: false | ||||
|     t.datetime "created_at", null: false | ||||
|     t.datetime "updated_at", null: false | ||||
|     t.index ["key"], name: "index_flipper_features_on_key", unique: true | ||||
|   end | ||||
| 
 | ||||
|   create_table "flipper_gates", force: :cascade do |t| | ||||
|     t.string "feature_key", null: false | ||||
|     t.string "key", null: false | ||||
|     t.string "value" | ||||
|     t.datetime "created_at", null: false | ||||
|     t.datetime "updated_at", null: false | ||||
|     t.index ["feature_key", "key", "value"], name: "index_flipper_gates_on_feature_key_and_key_and_value", unique: true | ||||
|   end | ||||
| 
 | ||||
|   create_table "invitations", force: :cascade do |t| | ||||
|     t.string "token" | ||||
|     t.integer "user_id" | ||||
|  | ||||
							
								
								
									
										41
									
								
								spec/requests/discourse/sso_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								spec/requests/discourse/sso_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| require 'rails_helper' | ||||
| require 'webmock/rspec' | ||||
| 
 | ||||
| RSpec.describe "Discourse SSO", type: :request do | ||||
| 
 | ||||
|   describe "GET /discourse/connect" do | ||||
|     let(:user) { create :user, cn: 'jimmy', ou: 'kosmos.org' } | ||||
| 
 | ||||
|     before do | ||||
|       Warden.test_mode! | ||||
|       login_as user, scope: :user | ||||
|       allow(user).to receive(:display_name).and_return('Jimbo') | ||||
|       allow(user).to receive(:is_admin?).and_return(false) | ||||
|     end | ||||
| 
 | ||||
|     after do | ||||
|       Warden.test_reset! | ||||
|     end | ||||
| 
 | ||||
|     context "with invalid SSO credentials" do | ||||
|       it "results in a failed signature check" do | ||||
|         expect { | ||||
|           get discourse_connect_path( | ||||
|             sso: "bm9uY2U9ODk2N2NiMmFlZTdlMjdjNzZiZTNkZWQ5ODIwYzMzN2QmcmV0dXJuX3Nzb191cmw9aHR0cCUzQSUyRiUyRmxvY2FsaG9zdCUzQTMwMDAlMkZzZXNzaW9uJTJGc3NvX2xvZ2lu", | ||||
|             sig: "01fc008ff7b51855217e879b6f14aaddefbbd4df2d128951f7bb70cfde834c2a" | ||||
|           ) | ||||
|         }.to raise_error(DiscourseApi::SingleSignOn::ParseError) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context "valid SSO credentials" do | ||||
|       it "redirects to the Discourse SSO endpoint" do | ||||
|         get discourse_connect_path( | ||||
|           sso: "bm9uY2U9YjQwYWZmYzg0YWQ2NWE1ZTk5MjdlZWU1NWEzMjdhMTQmcmV0dXJuX3Nzb191cmw9aHR0cCUzQSUyRiUyRmxvY2FsaG9zdCUzQTMwMDAlMkZzZXNzaW9uJTJGc3NvX2xvZ2lu", | ||||
|           sig: "b7905c5db612391293249ad5272dac493681efcd255133f6c2aff91ba654a319" | ||||
|         ) | ||||
|         expect(response).to redirect_to('http://discourse.example.com/session/sso_login?sso=YWRtaW49ZmFsc2UmZW1haWw9amltbXklNDBleGFtcGxlLmNvbSZleHRlcm5hbF9pZD0xJm5hbWU9SmltYm8mbm9uY2U9YjQwYWZmYzg0YWQ2NWE1ZTk5MjdlZWU1NWEzMjdhMTQmcmV0dXJuX3Nzb191cmw9aHR0cCUzQSUyRiUyRmxvY2FsaG9zdCUzQTMwMDAlMkZzZXNzaW9uJTJGc3NvX2xvZ2luJnVzZXJuYW1lPWppbW15&sig=d5f8b1d6db66569bef789fda4a3216119c2d42b84725d043c9a57dde1e528842') | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user