Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f0e9d20cd8
commit
514ada7cc9
|
@ -1481,6 +1481,13 @@
|
||||||
changes: ["vendor/gems/ipynbdiff/**/*"]
|
changes: ["vendor/gems/ipynbdiff/**/*"]
|
||||||
- <<: *if-merge-request-labels-run-all-rspec
|
- <<: *if-merge-request-labels-run-all-rspec
|
||||||
|
|
||||||
|
.vendor:rules:omniauth_crowd:
|
||||||
|
rules:
|
||||||
|
- <<: *if-merge-request
|
||||||
|
changes: ["vendor/gems/omniauth_crowd/**/*"]
|
||||||
|
- <<: *if-merge-request-labels-run-all-rspec
|
||||||
|
|
||||||
|
|
||||||
.vendor:rules:omniauth-gitlab:
|
.vendor:rules:omniauth-gitlab:
|
||||||
rules:
|
rules:
|
||||||
- <<: *if-merge-request
|
- <<: *if-merge-request
|
||||||
|
|
|
@ -14,6 +14,14 @@ vendor ipynbdiff:
|
||||||
include: vendor/gems/ipynbdiff/.gitlab-ci.yml
|
include: vendor/gems/ipynbdiff/.gitlab-ci.yml
|
||||||
strategy: depend
|
strategy: depend
|
||||||
|
|
||||||
|
vendor omniauth_crowd:
|
||||||
|
extends:
|
||||||
|
- .vendor:rules:omniauth_crowd
|
||||||
|
needs: []
|
||||||
|
trigger:
|
||||||
|
include: vendor/gems/omniauth_crowd/.gitlab-ci.yml
|
||||||
|
strategy: depend
|
||||||
|
|
||||||
vendor omniauth-gitlab:
|
vendor omniauth-gitlab:
|
||||||
extends:
|
extends:
|
||||||
- .vendor:rules:omniauth-gitlab
|
- .vendor:rules:omniauth-gitlab
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -51,7 +51,7 @@ gem 'omniauth-oauth2-generic', '~> 0.2.2'
|
||||||
gem 'omniauth-saml', '~> 1.10'
|
gem 'omniauth-saml', '~> 1.10'
|
||||||
gem 'omniauth-shibboleth', '~> 1.3.0'
|
gem 'omniauth-shibboleth', '~> 1.3.0'
|
||||||
gem 'omniauth-twitter', '~> 1.4'
|
gem 'omniauth-twitter', '~> 1.4'
|
||||||
gem 'omniauth_crowd', '~> 2.4.0'
|
gem 'omniauth_crowd', '~> 2.4.0', path: 'vendor/gems/omniauth_crowd' # See vendor/gems/omniauth_crowd/README.md
|
||||||
gem 'omniauth-authentiq', '~> 0.3.3'
|
gem 'omniauth-authentiq', '~> 0.3.3'
|
||||||
gem 'gitlab-omniauth-openid-connect', '~> 0.9.0', require: 'omniauth_openid_connect'
|
gem 'gitlab-omniauth-openid-connect', '~> 0.9.0', require: 'omniauth_openid_connect'
|
||||||
gem 'omniauth-salesforce', '~> 1.0.5'
|
gem 'omniauth-salesforce', '~> 1.0.5'
|
||||||
|
|
14
Gemfile.lock
14
Gemfile.lock
|
@ -31,6 +31,14 @@ PATH
|
||||||
omniauth (~> 1.0)
|
omniauth (~> 1.0)
|
||||||
omniauth-oauth2 (~> 1.7.1)
|
omniauth-oauth2 (~> 1.7.1)
|
||||||
|
|
||||||
|
PATH
|
||||||
|
remote: vendor/gems/omniauth_crowd
|
||||||
|
specs:
|
||||||
|
omniauth_crowd (2.4.0)
|
||||||
|
activesupport
|
||||||
|
nokogiri (>= 1.4.4)
|
||||||
|
omniauth (~> 1.0, < 3)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
|
@ -915,10 +923,6 @@ GEM
|
||||||
omniauth-twitter (1.4.0)
|
omniauth-twitter (1.4.0)
|
||||||
omniauth-oauth (~> 1.1)
|
omniauth-oauth (~> 1.1)
|
||||||
rack
|
rack
|
||||||
omniauth_crowd (2.4.0)
|
|
||||||
activesupport
|
|
||||||
nokogiri (>= 1.4.4)
|
|
||||||
omniauth (~> 1.0)
|
|
||||||
open4 (1.3.4)
|
open4 (1.3.4)
|
||||||
openid_connect (1.3.0)
|
openid_connect (1.3.0)
|
||||||
activemodel
|
activemodel
|
||||||
|
@ -1652,7 +1656,7 @@ DEPENDENCIES
|
||||||
omniauth-saml (~> 1.10)
|
omniauth-saml (~> 1.10)
|
||||||
omniauth-shibboleth (~> 1.3.0)
|
omniauth-shibboleth (~> 1.3.0)
|
||||||
omniauth-twitter (~> 1.4)
|
omniauth-twitter (~> 1.4)
|
||||||
omniauth_crowd (~> 2.4.0)
|
omniauth_crowd (~> 2.4.0)!
|
||||||
org-ruby (~> 0.9.12)
|
org-ruby (~> 0.9.12)
|
||||||
pact (~> 1.12)
|
pact (~> 1.12)
|
||||||
parallel (~> 1.19)
|
parallel (~> 1.19)
|
||||||
|
|
|
@ -6,6 +6,7 @@ module ContainerRegistry
|
||||||
|
|
||||||
ALLOWED_ACTIONS = %w(push delete).freeze
|
ALLOWED_ACTIONS = %w(push delete).freeze
|
||||||
PUSH_ACTION = 'push'
|
PUSH_ACTION = 'push'
|
||||||
|
DELETE_ACTION = 'delete'
|
||||||
EVENT_TRACKING_CATEGORY = 'container_registry:notification'
|
EVENT_TRACKING_CATEGORY = 'container_registry:notification'
|
||||||
|
|
||||||
attr_reader :event
|
attr_reader :event
|
||||||
|
@ -41,6 +42,10 @@ module ContainerRegistry
|
||||||
event['target'].has_key?('tag')
|
event['target'].has_key?('tag')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def target_digest?
|
||||||
|
event['target'].has_key?('digest')
|
||||||
|
end
|
||||||
|
|
||||||
def target_repository?
|
def target_repository?
|
||||||
!target_tag? && event['target'].has_key?('repository')
|
!target_tag? && event['target'].has_key?('repository')
|
||||||
end
|
end
|
||||||
|
@ -53,6 +58,10 @@ module ContainerRegistry
|
||||||
PUSH_ACTION == action
|
PUSH_ACTION == action
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def action_delete?
|
||||||
|
DELETE_ACTION == action
|
||||||
|
end
|
||||||
|
|
||||||
def container_repository_exists?
|
def container_repository_exists?
|
||||||
return unless container_registry_path
|
return unless container_registry_path
|
||||||
|
|
||||||
|
@ -74,7 +83,7 @@ module ContainerRegistry
|
||||||
|
|
||||||
def update_project_statistics
|
def update_project_statistics
|
||||||
return unless supported?
|
return unless supported?
|
||||||
return unless target_tag?
|
return unless target_tag? || (action_delete? && target_digest?)
|
||||||
return unless project
|
return unless project
|
||||||
|
|
||||||
Rails.cache.delete(project.root_ancestor.container_repositories_size_cache_key)
|
Rails.cache.delete(project.root_ancestor.container_repositories_size_cache_key)
|
||||||
|
|
|
@ -207,7 +207,6 @@ Learn how to install, configure, update, and maintain your GitLab instance.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
- [Debugging tips](troubleshooting/debug.md): Tips to debug problems when things go wrong.
|
|
||||||
- [Log system](logs.md): Where to look for logs.
|
- [Log system](logs.md): Where to look for logs.
|
||||||
- [Sidekiq Troubleshooting](troubleshooting/sidekiq.md): Debug when Sidekiq appears hung and is not processing jobs.
|
- [Sidekiq Troubleshooting](troubleshooting/sidekiq.md): Debug when Sidekiq appears hung and is not processing jobs.
|
||||||
- [Troubleshooting Elasticsearch](troubleshooting/elasticsearch.md)
|
- [Troubleshooting Elasticsearch](troubleshooting/elasticsearch.md)
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
---
|
---
|
||||||
stage: Systems
|
redirect_to: '../reference_architectures/troubleshooting.md'
|
||||||
group: Distribution
|
remove_date: '2022-10-19'
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Debugging tips **(FREE SELF)**
|
This document was moved to [another location](../reference_architectures/troubleshooting.md).
|
||||||
|
|
||||||
Sometimes things don't work the way they should. Here are some tips on debugging issues out
|
<!-- This redirect file can be deleted after 2022-10-19. -->
|
||||||
in production.
|
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||||
|
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
|
||||||
## More information
|
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
||||||
|
|
||||||
- [Debugging Stuck Ruby Processes](https://newrelic.com/blog/best-practices/debugging-stuck-ruby-processes-what-to-do-before-you-kill-9)
|
|
||||||
|
|
|
@ -21,8 +21,10 @@ installation.
|
||||||
- [Linux cheat sheet](linux_cheat_sheet.md)
|
- [Linux cheat sheet](linux_cheat_sheet.md)
|
||||||
- [Parsing GitLab logs with `jq`](log_parsing.md)
|
- [Parsing GitLab logs with `jq`](log_parsing.md)
|
||||||
- [Diagnostics tools](diagnostics_tools.md)
|
- [Diagnostics tools](diagnostics_tools.md)
|
||||||
- [Debugging tips](debug.md)
|
|
||||||
- [Tracing requests with correlation ID](tracing_correlation_id.md)
|
- [Tracing requests with correlation ID](tracing_correlation_id.md)
|
||||||
|
|
||||||
|
Some feature documentation pages also have a troubleshooting section at the end
|
||||||
|
that you can check for feature-specific help.
|
||||||
|
|
||||||
If you need a testing environment to troubleshoot, see the
|
If you need a testing environment to troubleshoot, see the
|
||||||
[apps for a testing environment](test_environments.md).
|
[apps for a testing environment](test_environments.md).
|
||||||
|
|
|
@ -170,5 +170,5 @@ This parameter is used for filtering by attributes, such as `environment_scope`.
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl --request DELETE --globoff --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/variables/VARIABLE_1?filter[environment_scope]=production"
|
curl --globoff --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/variables/VARIABLE_1?filter[environment_scope]=production"
|
||||||
```
|
```
|
||||||
|
|
|
@ -57,8 +57,7 @@ NOTE:
|
||||||
Username search is case insensitive.
|
Username search is case insensitive.
|
||||||
|
|
||||||
In addition, you can filter users based on the states `blocked` and `active`.
|
In addition, you can filter users based on the states `blocked` and `active`.
|
||||||
It does not support `active=false` or `blocked=false`. The list of billable users
|
It does not support `active=false` or `blocked=false`.
|
||||||
is the total number of users minus the blocked users.
|
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
GET /users?active=true
|
GET /users?active=true
|
||||||
|
|
|
@ -40,6 +40,7 @@ RSpec.describe ContainerRegistry::Event do
|
||||||
|
|
||||||
subject(:handle!) { described_class.new(raw_event).handle! }
|
subject(:handle!) { described_class.new(raw_event).handle! }
|
||||||
|
|
||||||
|
shared_examples 'event with project statistics update' do
|
||||||
it 'enqueues a project statistics update' do
|
it 'enqueues a project statistics update' do
|
||||||
expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:container_registry_size])
|
expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:container_registry_size])
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@ RSpec.describe ContainerRegistry::Event do
|
||||||
|
|
||||||
handle!
|
handle!
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
shared_examples 'event without project statistics update' do
|
shared_examples 'event without project statistics update' do
|
||||||
it 'does not queue a project statistics update' do
|
it 'does not queue a project statistics update' do
|
||||||
|
@ -60,10 +62,32 @@ RSpec.describe ContainerRegistry::Event do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'event with project statistics update'
|
||||||
|
|
||||||
context 'with no target tag' do
|
context 'with no target tag' do
|
||||||
let(:target) { super().without('tag') }
|
let(:target) { super().without('tag') }
|
||||||
|
|
||||||
it_behaves_like 'event without project statistics update'
|
it_behaves_like 'event without project statistics update'
|
||||||
|
|
||||||
|
context 'with a target digest' do
|
||||||
|
let(:target) { super().merge('digest' => 'abc123') }
|
||||||
|
|
||||||
|
it_behaves_like 'event without project statistics update'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a delete action' do
|
||||||
|
let(:action) { 'delete' }
|
||||||
|
|
||||||
|
context 'without a target digest' do
|
||||||
|
it_behaves_like 'event without project statistics update'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a target digest' do
|
||||||
|
let(:target) { super().merge('digest' => 'abc123') }
|
||||||
|
|
||||||
|
it_behaves_like 'event with project statistics update'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with an unsupported action' do
|
context 'with an unsupported action' do
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
workflow:
|
||||||
|
rules:
|
||||||
|
- if: $CI_MERGE_REQUEST_ID
|
||||||
|
|
||||||
|
.rspec:
|
||||||
|
cache:
|
||||||
|
key: omniauth-gitlab-ruby
|
||||||
|
paths:
|
||||||
|
- vendor/gems/omniauth_crowd/vendor/ruby
|
||||||
|
before_script:
|
||||||
|
- cd vendor/gems/omniauth_crowd
|
||||||
|
- ruby -v # Print out ruby version for debugging
|
||||||
|
- gem install bundler --no-document # Bundler is not installed with the image
|
||||||
|
- bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby
|
||||||
|
- bundle config set with 'development'
|
||||||
|
- bundle install -j $(nproc)
|
||||||
|
script:
|
||||||
|
- bundle exec rspec
|
||||||
|
|
||||||
|
rspec-2.6:
|
||||||
|
image: "ruby:2.6"
|
||||||
|
extends: .rspec
|
||||||
|
|
||||||
|
rspec-2.7:
|
||||||
|
image: "ruby:2.7"
|
||||||
|
extends: .rspec
|
||||||
|
|
||||||
|
rspec-3.0:
|
||||||
|
image: "ruby:3.0"
|
||||||
|
extends: .rspec
|
|
@ -0,0 +1,4 @@
|
||||||
|
source 'http://rubygems.org'
|
||||||
|
|
||||||
|
# Specify your gem's dependencies in omniauth-github.gemspec
|
||||||
|
gemspec
|
|
@ -0,0 +1,74 @@
|
||||||
|
PATH
|
||||||
|
remote: .
|
||||||
|
specs:
|
||||||
|
omniauth_crowd (2.4.0)
|
||||||
|
activesupport
|
||||||
|
nokogiri (>= 1.4.4)
|
||||||
|
omniauth (~> 1.0, < 3)
|
||||||
|
|
||||||
|
GEM
|
||||||
|
remote: http://rubygems.org/
|
||||||
|
specs:
|
||||||
|
activesupport (5.0.0.1)
|
||||||
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
i18n (~> 0.7)
|
||||||
|
minitest (~> 5.1)
|
||||||
|
tzinfo (~> 1.1)
|
||||||
|
addressable (2.5.2)
|
||||||
|
public_suffix (>= 2.0.2, < 4.0)
|
||||||
|
concurrent-ruby (1.0.5)
|
||||||
|
crack (0.4.3)
|
||||||
|
safe_yaml (~> 1.0.0)
|
||||||
|
diff-lcs (1.2.5)
|
||||||
|
hashdiff (0.3.6)
|
||||||
|
hashie (3.4.3)
|
||||||
|
i18n (0.8.1)
|
||||||
|
mini_portile2 (2.1.0)
|
||||||
|
minitest (5.10.1)
|
||||||
|
nokogiri (1.6.8.1)
|
||||||
|
mini_portile2 (~> 2.1.0)
|
||||||
|
omniauth (1.3.1)
|
||||||
|
hashie (>= 1.2, < 4)
|
||||||
|
rack (>= 1.0, < 3)
|
||||||
|
public_suffix (3.0.0)
|
||||||
|
rack (1.6.4)
|
||||||
|
rack-test (0.6.3)
|
||||||
|
rack (>= 1.0)
|
||||||
|
rake (10.5.0)
|
||||||
|
rexml (3.2.5)
|
||||||
|
rspec (3.0.0)
|
||||||
|
rspec-core (~> 3.0.0)
|
||||||
|
rspec-expectations (~> 3.0.0)
|
||||||
|
rspec-mocks (~> 3.0.0)
|
||||||
|
rspec-core (3.0.4)
|
||||||
|
rspec-support (~> 3.0.0)
|
||||||
|
rspec-expectations (3.0.4)
|
||||||
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
|
rspec-support (~> 3.0.0)
|
||||||
|
rspec-mocks (3.0.4)
|
||||||
|
rspec-support (~> 3.0.0)
|
||||||
|
rspec-support (3.0.4)
|
||||||
|
safe_yaml (1.0.4)
|
||||||
|
thread_safe (0.3.6)
|
||||||
|
tzinfo (1.2.2)
|
||||||
|
thread_safe (~> 0.1)
|
||||||
|
webmock (3.0.1)
|
||||||
|
addressable (>= 2.3.6)
|
||||||
|
crack (>= 0.3.2)
|
||||||
|
hashdiff
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
bundler (> 1.0.0)
|
||||||
|
omniauth_crowd!
|
||||||
|
rack
|
||||||
|
rack-test
|
||||||
|
rake
|
||||||
|
rexml (~> 3.2.5)
|
||||||
|
rspec (~> 3.0.0)
|
||||||
|
webmock (~> 3.0.0)
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.3.15
|
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright (c) 2011 Rob Di Marco
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,52 @@
|
||||||
|
# omniauth_crowd
|
||||||
|
|
||||||
|
This is fork of [omniauth_crowd](https://github.com/robdimarco/omniauth_crowd) to support:
|
||||||
|
|
||||||
|
1. OmniAuth v1 and v2. OmniAuth v2 disables GET requests by default
|
||||||
|
and defaults to POST. GitLab already has patched v1 to use POST,
|
||||||
|
but other dependencies need to be updated:
|
||||||
|
https://gitlab.com/gitlab-org/gitlab/-/issues/30073.
|
||||||
|
2. We may deprecate this library entirely in the future:
|
||||||
|
https://gitlab.com/gitlab-org/gitlab/-/issues/366212
|
||||||
|
|
||||||
|
The omniauth_crowd library is an OmniAuth provider that supports authentication against Atlassian Crowd REST apis.
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/robdimarco/omniauth_crowd.svg?branch=master)](https://travis-ci.org/robdimarco/omniauth_crowd)
|
||||||
|
|
||||||
|
## Helpful links
|
||||||
|
|
||||||
|
* [Documentation](http://github.com/robdimarco/omniauth_crow)
|
||||||
|
* [OmniAuth](https://github.com/intridea/omniauth/)
|
||||||
|
* [Atlassian Crowd](http://www.atlassian.com/software/crowd/)
|
||||||
|
* [Atlassian Crowd REST API](http://confluence.atlassian.com/display/CROWDDEV/Crowd+REST+APIs)
|
||||||
|
|
||||||
|
## Install and use
|
||||||
|
|
||||||
|
### 1. Add the OmniAuth Crowd REST plugin to your Gemfile
|
||||||
|
|
||||||
|
gem 'omniauth', '>= 1.0.0' # We depend on this
|
||||||
|
gem "omniauth_crowd"
|
||||||
|
|
||||||
|
### 2. You will need to configure OmniAuth to use your crowd authentication. This is generally done in Rails in the config/initializers/omniauth.rb with...
|
||||||
|
|
||||||
|
Rails.application.config.middleware.use OmniAuth::Builder do
|
||||||
|
provider :crowd, :crowd_server_url=>"https://crowd.mycompanyname.com/crowd", :application_name=>"app", :application_password=>"password"
|
||||||
|
end
|
||||||
|
|
||||||
|
You will need to supply the correct server URL, application name and password
|
||||||
|
|
||||||
|
## Contributing to omniauth_crowd
|
||||||
|
|
||||||
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
||||||
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
||||||
|
* Fork the project
|
||||||
|
* Start a feature/bugfix branch
|
||||||
|
* Commit and push until you are happy with your contribution
|
||||||
|
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
||||||
|
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
||||||
|
|
||||||
|
## Copyright
|
||||||
|
|
||||||
|
Copyright (c) 2011-14 Rob Di Marco. See LICENSE.txt for
|
||||||
|
further details.
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env rake
|
||||||
|
require "bundler/gem_tasks"
|
||||||
|
require 'rspec/core/rake_task'
|
||||||
|
|
||||||
|
desc 'Default: run specs.'
|
||||||
|
task :default => :spec
|
||||||
|
|
||||||
|
desc "Run specs"
|
||||||
|
RSpec::Core::RakeTask.new
|
||||||
|
|
||||||
|
desc 'Run specs'
|
||||||
|
task :default => :spec
|
|
@ -0,0 +1,97 @@
|
||||||
|
require 'omniauth'
|
||||||
|
require 'active_support'
|
||||||
|
require 'active_support/core_ext/object'
|
||||||
|
module OmniAuth
|
||||||
|
module Strategies
|
||||||
|
class Crowd
|
||||||
|
include OmniAuth::Strategy
|
||||||
|
|
||||||
|
autoload :Configuration, 'omniauth/strategies/crowd/configuration'
|
||||||
|
autoload :CrowdValidator, 'omniauth/strategies/crowd/crowd_validator'
|
||||||
|
def initialize(app, options = {}, &block)
|
||||||
|
options.symbolize_keys!()
|
||||||
|
super(app, {:name=> :crowd}.merge(options), &block)
|
||||||
|
@configuration = OmniAuth::Strategies::Crowd::Configuration.new(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def request_phase
|
||||||
|
if env['REQUEST_METHOD'] == 'GET'
|
||||||
|
|
||||||
|
if @configuration.use_sessions? && request.cookies[@configuration.session_cookie]
|
||||||
|
redirect callback_url
|
||||||
|
else
|
||||||
|
get_credentials
|
||||||
|
end
|
||||||
|
|
||||||
|
elsif (env['REQUEST_METHOD'] == 'POST') && (not request.params['username'])
|
||||||
|
get_credentials
|
||||||
|
else
|
||||||
|
session['omniauth.crowd'] = {'username' => request['username'], 'password' => request['password']}
|
||||||
|
redirect callback_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_client_ip
|
||||||
|
env['HTTP_X_FORWARDED_FOR'] ? env['HTTP_X_FORWARDED_FOR'] : env['REMOTE_ADDRESS']
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_sso_tokens
|
||||||
|
env['HTTP_COOKIE'].split(';').select { |val|
|
||||||
|
val.strip.start_with?(@configuration.session_cookie)
|
||||||
|
}.map { |val|
|
||||||
|
val.strip.split('=').last
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_credentials
|
||||||
|
|
||||||
|
configuration = @configuration
|
||||||
|
|
||||||
|
OmniAuth::Form.build(:title => (options[:title] || "Crowd Authentication")) do
|
||||||
|
text_field 'Login', 'username'
|
||||||
|
password_field 'Password', 'password'
|
||||||
|
|
||||||
|
if configuration.use_sessions? && configuration.sso_url
|
||||||
|
fieldset 'SSO' do
|
||||||
|
html "<a href=\"#{configuration.sso_url}/users/auth/crowd/callback\">" + (configuration.sso_url_image ? "<img src=\"#{configuration.sso_url_image}\" />" : '') + "</a>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end.to_response
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def callback_phase
|
||||||
|
|
||||||
|
creds = session.delete 'omniauth.crowd'
|
||||||
|
username = creds.nil? ? nil : creds['username']
|
||||||
|
password = creds.nil? ? nil : creds['password']
|
||||||
|
|
||||||
|
unless creds
|
||||||
|
if @configuration.use_sessions? && request.cookies[@configuration.session_cookie]
|
||||||
|
validator = CrowdValidator.new(@configuration, username, password, get_client_ip, get_sso_tokens)
|
||||||
|
else
|
||||||
|
return fail!(:no_credentials)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
validator = CrowdValidator.new(@configuration, username, password, get_client_ip, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
@user_info = validator.user_info
|
||||||
|
|
||||||
|
return fail!(:invalid_credentials) if @user_info.nil? || @user_info.empty?
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def auth_hash
|
||||||
|
OmniAuth::Utils.deep_merge(super, {
|
||||||
|
'uid' => @user_info.delete("user"),
|
||||||
|
'info' => @user_info
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,110 @@
|
||||||
|
require 'rack'
|
||||||
|
|
||||||
|
module OmniAuth
|
||||||
|
module Strategies
|
||||||
|
class Crowd
|
||||||
|
class Configuration
|
||||||
|
DEFAULT_SESSION_URL = "%s/rest/usermanagement/latest/session"
|
||||||
|
DEFAULT_AUTHENTICATION_URL = "%s/rest/usermanagement/latest/authentication"
|
||||||
|
DEFAULT_USER_GROUP_URL = "%s/rest/usermanagement/latest/user/group/direct"
|
||||||
|
DEFAULT_CONTENT_TYPE = 'application/xml'
|
||||||
|
DEFAULT_SESSION_COOKIE = 'crowd.token_key'
|
||||||
|
|
||||||
|
attr_reader :crowd_application_name, :crowd_password, :disable_ssl_verification, :include_users_groups, :use_sessions, :session_url, :content_type, :session_cookie, :sso_url, :sso_url_image
|
||||||
|
|
||||||
|
alias :"disable_ssl_verification?" :disable_ssl_verification
|
||||||
|
alias :"include_users_groups?" :include_users_groups
|
||||||
|
alias :"use_sessions?" :use_sessions
|
||||||
|
|
||||||
|
# @param [Hash] params configuration options
|
||||||
|
# @option params [String, nil] :crowd_server_url the Crowd server root URL; probably something like
|
||||||
|
# `https://crowd.mycompany.com` or `https://crowd.mycompany.com/crowd`; optional.
|
||||||
|
# @option params [String, nil] :crowd_authentication_url (:crowd_server_url + '/rest/usermanagement/latest/authentication') the URL to which to
|
||||||
|
# use for authenication; optional if `:crowd_server_url` is specified,
|
||||||
|
# required otherwise.
|
||||||
|
# @option params [String, nil] :application_name the application name specified in Crowd for this application, required.
|
||||||
|
# @option params [String, nil] :application_password the application password specified in Crowd for this application, required.
|
||||||
|
# @option params [Boolean, nil] :disable_ssl_verification disable verification for SSL cert,
|
||||||
|
# helpful when you developing with a fake cert.
|
||||||
|
# @option params [Boolean, true] : include a list of user groups when getting information ont he user
|
||||||
|
# @option params [String, nil] :crowd_user_group_url (:crowd_server_url + '/rest/usermanagement/latest/user/group/direct') the URL to which to
|
||||||
|
# use for retrieving users groups optional if `:crowd_server_url` is specified, or if `:include_user_groups` is false
|
||||||
|
# required otherwise.
|
||||||
|
# @option params [Boolean, false] :use_sessions Use Crowd sessions. If the user logins with user and password create a new Crowd session. Update the session if only a session token is sent (Cookie name set by option session_cookie)
|
||||||
|
# @option params [String, 'crowd.token_key'] :session_cookie Session cookie name. Defaults to: 'crowd.token_key'
|
||||||
|
# @option params [String, nil] :sso_url URL of the external SSO page. If this parameter is defined the login form will have a link which will redirect to the SSO page. The SSO must return to the URL of the page using omniauth_crowd (Path portion '/users/auth/crowd/callback' is appended to the URL)
|
||||||
|
# @option params [String, nil] :sso_url_image Optional image URL to be used in SSO link in the login form
|
||||||
|
def initialize(params)
|
||||||
|
parse_params params
|
||||||
|
end
|
||||||
|
|
||||||
|
# Build a Crowd authentication URL from +username+.
|
||||||
|
#
|
||||||
|
# @param [String] username the username to validate
|
||||||
|
#
|
||||||
|
# @return [String] a URL like `https://crowd.myhost.com/crowd/rest/usermanagement/latest/authentication?username=USERNAME`
|
||||||
|
def authentication_url(username)
|
||||||
|
append_username @authentication_url, username
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_group_url(username)
|
||||||
|
@user_group_url.nil? ? nil : append_username( @user_group_url, username)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def parse_params(options)
|
||||||
|
options= {:include_user_groups => true}.merge(options || {})
|
||||||
|
%w(application_name application_password).each do |opt|
|
||||||
|
raise ArgumentError.new(":#{opt} MUST be provided") if options[opt.to_sym] == ""
|
||||||
|
end
|
||||||
|
@crowd_application_name = options[:application_name]
|
||||||
|
@crowd_password = options[:application_password]
|
||||||
|
@use_sessions = options[:use_sessions]
|
||||||
|
@content_type = options[:content_type] || DEFAULT_CONTENT_TYPE
|
||||||
|
@session_cookie = options[:session_cookie] || DEFAULT_SESSION_COOKIE
|
||||||
|
@sso_url = options[:sso_url]
|
||||||
|
@sso_url_image = options[:sso_url_image]
|
||||||
|
|
||||||
|
unless options.include?(:crowd_server_url) || options.include?(:crowd_authentication_url)
|
||||||
|
raise ArgumentError.new("Either :crowd_server_url or :crowd_authentication_url MUST be provided")
|
||||||
|
end
|
||||||
|
|
||||||
|
if @use_sessions
|
||||||
|
@session_url = options[:crowd_session_url] || DEFAULT_SESSION_URL % options[:crowd_server_url]
|
||||||
|
validate_is_url 'session URL', @session_url
|
||||||
|
end
|
||||||
|
@authentication_url = options[:crowd_authentication_url] || DEFAULT_AUTHENTICATION_URL % options[:crowd_server_url]
|
||||||
|
validate_is_url 'authentication URL', @authentication_url
|
||||||
|
@disable_ssl_verification = options[:disable_ssl_verification]
|
||||||
|
@include_users_groups = options[:include_user_groups]
|
||||||
|
if @include_users_groups
|
||||||
|
@user_group_url = options[:crowd_user_group_url] || DEFAULT_USER_GROUP_URL % options[:crowd_server_url]
|
||||||
|
validate_is_url 'user group URL', @user_group_url
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
IS_NOT_URL_ERROR_MESSAGE = "%s is not a valid URL"
|
||||||
|
|
||||||
|
def validate_is_url(name, possibly_a_url)
|
||||||
|
url = URI.parse(possibly_a_url) rescue nil
|
||||||
|
raise ArgumentError.new(IS_NOT_URL_ERROR_MESSAGE % name) unless url.kind_of?(URI::HTTP)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds +service+ as an URL-escaped parameter to +base+.
|
||||||
|
#
|
||||||
|
# @param [String] base the base URL
|
||||||
|
# @param [String] service the service (a.k.a. return-to) URL.
|
||||||
|
#
|
||||||
|
# @return [String] the new joined URL.
|
||||||
|
def append_username(base, username)
|
||||||
|
result = base.dup
|
||||||
|
result << (result.include?('?') ? '&' : '?')
|
||||||
|
result << 'username='
|
||||||
|
result << Rack::Utils.escape(username)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
186
vendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd/crowd_validator.rb
vendored
Executable file
186
vendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd/crowd_validator.rb
vendored
Executable file
|
@ -0,0 +1,186 @@
|
||||||
|
require 'nokogiri'
|
||||||
|
require 'net/http'
|
||||||
|
require 'net/https'
|
||||||
|
|
||||||
|
module OmniAuth
|
||||||
|
module Strategies
|
||||||
|
class Crowd
|
||||||
|
class CrowdValidator
|
||||||
|
AUTHENTICATION_REQUEST_BODY = "<password><value>%s</value></password>"
|
||||||
|
def initialize(configuration, username, password, client_ip, tokens)
|
||||||
|
@configuration, @username, @password, @client_ip, @tokens = configuration, username, password, client_ip, tokens
|
||||||
|
@authentiction_uri = URI.parse(@configuration.authentication_url(@username))
|
||||||
|
@session_uri = URI.parse(@configuration.session_url) if @configuration.use_sessions
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_info
|
||||||
|
user_info_hash = retrieve_user_info!
|
||||||
|
|
||||||
|
if user_info_hash && @configuration.include_users_groups?
|
||||||
|
user_info_hash = add_user_groups!(user_info_hash)
|
||||||
|
else
|
||||||
|
user_info_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
if user_info_hash && @configuration.use_sessions?
|
||||||
|
user_info_hash = set_session!(user_info_hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
user_info_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def set_session!(user_info_hash)
|
||||||
|
|
||||||
|
response = nil
|
||||||
|
|
||||||
|
if user_info_hash["sso_token"]
|
||||||
|
response = make_session_request(user_info_hash["sso_token"])
|
||||||
|
else
|
||||||
|
response = make_session_request(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
if response.kind_of?(Net::HTTPSuccess) && response.body
|
||||||
|
doc = Nokogiri::XML(response.body)
|
||||||
|
user_info_hash["sso_token"] = doc.xpath('//token/text()').to_s
|
||||||
|
else
|
||||||
|
OmniAuth.logger.send(:warn, "(crowd) [set_session!] response code: #{response.code.to_s}")
|
||||||
|
OmniAuth.logger.send(:warn, "(crowd) [set_session!] response body: #{response.body}")
|
||||||
|
end
|
||||||
|
|
||||||
|
user_info_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_user_groups!(user_info_hash)
|
||||||
|
response = make_user_group_request(user_info_hash['user'])
|
||||||
|
unless response.code.to_i != 200 || response.body.nil? || response.body == ''
|
||||||
|
doc = Nokogiri::XML(response.body)
|
||||||
|
user_info_hash["groups"] = doc.xpath("//groups/group/@name").map(&:to_s)
|
||||||
|
end
|
||||||
|
user_info_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
def retrieve_user_info!
|
||||||
|
response = make_authorization_request
|
||||||
|
|
||||||
|
unless response === nil
|
||||||
|
unless response.code.to_i != 200 || response.body.nil? || response.body == ''
|
||||||
|
|
||||||
|
doc = Nokogiri::XML(response.body)
|
||||||
|
result = {
|
||||||
|
"user" => doc.xpath("//user/@name").to_s,
|
||||||
|
"name" => doc.xpath("//user/display-name/text()").to_s,
|
||||||
|
"first_name" => doc.xpath("//user/first-name/text()").to_s,
|
||||||
|
"last_name" => doc.xpath("//user/last-name/text()").to_s,
|
||||||
|
"email" => doc.xpath("//user/email/text()").to_s
|
||||||
|
}
|
||||||
|
|
||||||
|
if doc.at_xpath("//token")
|
||||||
|
result["sso_token"] = doc.xpath("//token/text()").to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
|
||||||
|
else
|
||||||
|
OmniAuth.logger.send(:warn, "(crowd) [retrieve_user_info!] response code: #{response.code.to_s}")
|
||||||
|
OmniAuth.logger.send(:warn, "(crowd) [retrieve_user_info!] response body: #{response.body}")
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
OmniAuth.logger.send(:warn, "(crowd) [retrieve_user_info!] None of the session tokens were valid")
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_request(uri, body=nil)
|
||||||
|
http_method = body.nil? ? Net::HTTP::Get : Net::HTTP::Post
|
||||||
|
http = Net::HTTP.new(uri.host, uri.port)
|
||||||
|
http.use_ssl = uri.port == 443 || uri.instance_of?(URI::HTTPS)
|
||||||
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl? && @configuration.disable_ssl_verification?
|
||||||
|
http.start do |c|
|
||||||
|
req = http_method.new(uri.query.nil? ? uri.path : "#{uri.path}?#{uri.query}")
|
||||||
|
req.body = body if body
|
||||||
|
req.basic_auth @configuration.crowd_application_name, @configuration.crowd_password
|
||||||
|
if @configuration.content_type
|
||||||
|
req.add_field 'Content-Type', @configuration.content_type
|
||||||
|
end
|
||||||
|
http.request(req)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_user_group_request(username)
|
||||||
|
make_request(URI.parse(@configuration.user_group_url(username)))
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_authorization_request
|
||||||
|
|
||||||
|
if @configuration.use_sessions? && @tokens.kind_of?(Array)
|
||||||
|
make_session_retrieval_request
|
||||||
|
else
|
||||||
|
make_request(@authentiction_uri, make_authentication_request_body(@password))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_session_request(token)
|
||||||
|
|
||||||
|
root = url = validation_factor = nil
|
||||||
|
doc = Nokogiri::XML::Document.new
|
||||||
|
|
||||||
|
if token === nil
|
||||||
|
|
||||||
|
url = @session_uri
|
||||||
|
root = doc.create_element('authentication-context')
|
||||||
|
|
||||||
|
doc.root = root
|
||||||
|
root.add_child(doc.create_element('username', @username))
|
||||||
|
root.add_child(doc.create_element('password', @password))
|
||||||
|
|
||||||
|
else
|
||||||
|
url = URI.parse(@session_uri.to_s() + "/#{token}")
|
||||||
|
end
|
||||||
|
|
||||||
|
if @configuration.use_sessions? || @client_ip
|
||||||
|
|
||||||
|
if root === nil
|
||||||
|
root = doc.create_element('validation-factors')
|
||||||
|
doc.root = root
|
||||||
|
else
|
||||||
|
root.add_child(doc.create_element('validation-factors'))
|
||||||
|
end
|
||||||
|
|
||||||
|
validation_factor = doc.create_element('validation-factor')
|
||||||
|
validation_factor.add_child(doc.create_element('name', 'remote_address'))
|
||||||
|
validation_factor.add_child(doc.create_element('value', @client_ip))
|
||||||
|
|
||||||
|
doc.xpath('//validation-factors').first.add_child(validation_factor)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
make_request(url, doc.to_s)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# create the body using Nokogiri so proper encoding of passwords can be ensured
|
||||||
|
def make_authentication_request_body(password)
|
||||||
|
request_body = Nokogiri::XML(AUTHENTICATION_REQUEST_BODY)
|
||||||
|
password_value = request_body.at_css "value"
|
||||||
|
password_value.content = password
|
||||||
|
return request_body.root.to_s # return the body without the xml header
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_session_retrieval_request
|
||||||
|
|
||||||
|
response = nil
|
||||||
|
|
||||||
|
@tokens.any? { |token|
|
||||||
|
response = make_request(URI.parse(@session_uri.to_s() + "/#{token}"))
|
||||||
|
response.code.to_i == 200 && !response.body.nil? && response.body != ''
|
||||||
|
}
|
||||||
|
|
||||||
|
response
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
require 'omniauth/strategies/crowd'
|
|
@ -0,0 +1,5 @@
|
||||||
|
module OmniAuth
|
||||||
|
module Crowd
|
||||||
|
VERSION = "2.4.0"
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
require File.expand_path('../lib/omniauth_crowd/version', __FILE__)
|
||||||
|
lib = File.expand_path('../lib', __FILE__)
|
||||||
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||||
|
|
||||||
|
Gem::Specification.new do |gem|
|
||||||
|
gem.authors = ["Robert Di Marco"]
|
||||||
|
gem.email = ["rob@innovationontherun.com"]
|
||||||
|
gem.description = "This is an OmniAuth provider for Atlassian Crowd's REST API. It allows you to easily integrate your Rack application in with Atlassian Crowd."
|
||||||
|
gem.summary = "An OmniAuth provider for Atlassian Crowd REST API"
|
||||||
|
gem.homepage = "http://github.com/robdimarco/omniauth_crowd"
|
||||||
|
gem.files = Dir.glob("lib/**/*.*")
|
||||||
|
gem.test_files = Dir.glob("spec/**/**/*.*")
|
||||||
|
gem.name = "omniauth_crowd"
|
||||||
|
gem.require_paths = ["lib"]
|
||||||
|
gem.version = OmniAuth::Crowd::VERSION
|
||||||
|
|
||||||
|
gem.add_runtime_dependency 'omniauth', '~> 1.0', '< 3'
|
||||||
|
gem.add_runtime_dependency 'nokogiri', '>= 1.4.4'
|
||||||
|
gem.add_runtime_dependency 'activesupport', '>= 0'
|
||||||
|
gem.add_development_dependency(%q<rack>, [">= 0"])
|
||||||
|
gem.add_development_dependency(%q<rake>, [">= 0"])
|
||||||
|
gem.add_development_dependency(%q<rack-test>, [">= 0"])
|
||||||
|
gem.add_development_dependency(%q<rexml>, ["~> 3.2.5"])
|
||||||
|
gem.add_development_dependency(%q<rspec>, ["~> 3.0.0"])
|
||||||
|
gem.add_development_dependency(%q<webmock>, ["~> 3.0.0"])
|
||||||
|
gem.add_development_dependency(%q<bundler>, ["> 1.0.0"])
|
||||||
|
end
|
|
@ -0,0 +1,8 @@
|
||||||
|
<groups expand="group">
|
||||||
|
<group name="Developers">
|
||||||
|
<link rel="self" href="http://crowd.bogus.com/crowd/rest/usermanagement/latest/group?groupname=Developers"/>
|
||||||
|
</group>
|
||||||
|
<group name="jira-users">
|
||||||
|
<link rel="self" href="http://crowd.bogus.com/crowd/rest/usermanagement/latest/group?groupname=jira-users"/>
|
||||||
|
</group>
|
||||||
|
</groups>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<session expand="user">
|
||||||
|
<token>rtk8eMvqq00EiGn5iJCMZQ00</token>
|
||||||
|
<user name="foo">
|
||||||
|
<link rel="self" href="http://crowd.example.org/crowd/rest/usermanagement/latest/user?username=foo"/>
|
||||||
|
</user>
|
||||||
|
<link rel="self" href="http://crowd.example.org/crowd/rest/usermanagement/latest/session/rtk8eMvqq00EiGn5iJCMZQ00"/>
|
||||||
|
</session>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<user name="foo" expand="attributes">
|
||||||
|
<link rel="self" href="http://crowd.example.org/crowd/rest/usermanagement/latest/user?username=foo"/>
|
||||||
|
<first-name>Foo</first-name>
|
||||||
|
<last-name>Foobaz</last-name>
|
||||||
|
<display-name>Foo Foobaz</display-name>
|
||||||
|
<email>foo@example.org</email>
|
||||||
|
<password><link rel="edit" href="http://crowd.example.org/crowd/rest/usermanagement/latest/user/password?username=foo"/></password>
|
||||||
|
<active>true</active>
|
||||||
|
<attributes><link rel="self" href="http://crowd.example.org/crowd/rest/usermanagement/latest/user/attribute?username=foo"/></attributes>
|
||||||
|
</user>
|
|
@ -0,0 +1,387 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe OmniAuth::Strategies::Crowd, :type=>:strategy do
|
||||||
|
include OmniAuth::Test::StrategyTestCase
|
||||||
|
def strategy
|
||||||
|
@crowd_server_url ||= 'https://crowd.example.org'
|
||||||
|
@application_name ||= 'bogus_app'
|
||||||
|
@application_password ||= 'bogus_app_password'
|
||||||
|
[OmniAuth::Strategies::Crowd, {:crowd_server_url => @crowd_server_url,
|
||||||
|
:application_name => @application_name,
|
||||||
|
:application_password => @application_password,
|
||||||
|
:use_sessions => @using_sessions,
|
||||||
|
:sso_url => @sso_url,
|
||||||
|
:sso_url_image => @sso_url_image
|
||||||
|
}]
|
||||||
|
end
|
||||||
|
|
||||||
|
@using_sessions = false
|
||||||
|
@sso_url = nil
|
||||||
|
@sso_url_image = nil
|
||||||
|
let(:config) { OmniAuth::Strategies::Crowd::Configuration.new(strategy[1]) }
|
||||||
|
let(:validator) { OmniAuth::Strategies::Crowd::CrowdValidator.new(config, 'foo', 'bar', nil, nil) }
|
||||||
|
|
||||||
|
describe 'Authentication Request Body' do
|
||||||
|
|
||||||
|
it 'should send password in session request' do
|
||||||
|
body = <<-BODY.strip
|
||||||
|
<password>
|
||||||
|
<value>bar</value>
|
||||||
|
</password>
|
||||||
|
BODY
|
||||||
|
expect(validator.send(:make_authentication_request_body, 'bar')).to eq(body)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should escape special characters username and password in session request' do
|
||||||
|
body = <<-BODY.strip
|
||||||
|
<password>
|
||||||
|
<value>bar<</value>
|
||||||
|
</password>
|
||||||
|
BODY
|
||||||
|
expect(validator.send(:make_authentication_request_body, 'bar<')).to eq(body)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /auth/crowd' do
|
||||||
|
it 'should show the login form' do
|
||||||
|
get '/auth/crowd'
|
||||||
|
expect(last_response).to be_ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST /auth/crowd' do
|
||||||
|
it 'should redirect to callback' do
|
||||||
|
post '/auth/crowd', :username=>'foo', :password=>'bar'
|
||||||
|
expect(last_response).to be_redirect
|
||||||
|
expect(last_response.headers['Location']).to eq('http://example.org/auth/crowd/callback')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /auth/crowd/callback without any credentials' do
|
||||||
|
it 'should fail' do
|
||||||
|
get '/auth/crowd/callback'
|
||||||
|
expect(last_response).to be_redirect
|
||||||
|
expect(last_response.headers['Location']).to match(/no_credentials/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /auth/crowd/callback with credentials can be successful' do
|
||||||
|
context "when using authentication endpoint" do
|
||||||
|
before do
|
||||||
|
stub_request(:post, "https://crowd.example.org/rest/usermanagement/latest/authentication?username=foo").
|
||||||
|
to_return(:body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'success.xml')))
|
||||||
|
|
||||||
|
stub_request(:get, "https://crowd.example.org/rest/usermanagement/latest/user/group/direct?username=foo").
|
||||||
|
to_return(:body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'groups.xml')))
|
||||||
|
|
||||||
|
#Adding this to prevent Content-Type text/xml from being added back in the future
|
||||||
|
stub_request(:get, "https://crowd.example.org/rest/usermanagement/latest/user/group/direct?username=foo").with(:headers => {"Content-Type" => "text/xml"}).
|
||||||
|
to_return(:status => [415, "Unsupported Media Type"])
|
||||||
|
get '/auth/crowd/callback', nil, 'rack.session'=>{'omniauth.crowd'=> {"username"=>"foo", "password"=>"ba"}}
|
||||||
|
end
|
||||||
|
it 'should call through to the master app' do
|
||||||
|
expect(last_response.body).to eq('true')
|
||||||
|
end
|
||||||
|
it 'should have an auth hash' do
|
||||||
|
auth = last_request.env['omniauth.auth']
|
||||||
|
expect(auth).to be_kind_of(Hash)
|
||||||
|
end
|
||||||
|
it 'should have good data' do
|
||||||
|
auth = last_request.env['omniauth.auth']
|
||||||
|
expect(auth['provider']).to eq(:crowd)
|
||||||
|
expect(auth['uid']).to eq('foo')
|
||||||
|
expect(auth['info']).to be_kind_of(Hash)
|
||||||
|
expect(auth['info']['groups'].sort).to eq(["Developers", "jira-users"].sort)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "when using session endpoint" do
|
||||||
|
before do
|
||||||
|
@using_sessions = true
|
||||||
|
stub_request(:post, "https://crowd.example.org/rest/usermanagement/latest/authentication?username=foo").
|
||||||
|
to_return(:body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'success.xml')))
|
||||||
|
stub_request(:post, "https://crowd.example.org/rest/usermanagement/latest/session").
|
||||||
|
to_return(:status => 201, :body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'session.xml')))
|
||||||
|
stub_request(:get, "https://crowd.example.org/rest/usermanagement/latest/user/group/direct?username=foo").
|
||||||
|
to_return(:body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'groups.xml')))
|
||||||
|
end
|
||||||
|
|
||||||
|
after { @using_sessions = false }
|
||||||
|
|
||||||
|
it 'should call through to the master app' do
|
||||||
|
get '/auth/crowd/callback', nil, 'rack.session'=>{'omniauth.crowd'=> {"username"=>"foo", "password"=>"ba"}}
|
||||||
|
expect(last_response.body).to eq('true')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should have an auth hash' do
|
||||||
|
get '/auth/crowd/callback', nil, 'rack.session'=>{'omniauth.crowd'=> {"username"=>"foo", "password"=>"ba"}}
|
||||||
|
expect(last_request.env['omniauth.auth']).to be_kind_of(Hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should have good data' do
|
||||||
|
get '/auth/crowd/callback', nil, 'rack.session'=>{'omniauth.crowd'=> {"username"=>"foo", "password"=>"ba"}}
|
||||||
|
auth = last_request.env['omniauth.auth']
|
||||||
|
expect(auth['provider']).to eq(:crowd)
|
||||||
|
expect(auth['uid']).to eq('foo')
|
||||||
|
expect(auth['info']).to be_kind_of(Hash)
|
||||||
|
expect(auth['info']['sso_token']).to eq('rtk8eMvqq00EiGn5iJCMZQ00')
|
||||||
|
expect(auth['info']['groups'].sort).to eq(["Developers", "jira-users"].sort)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /auth/crowd/callback with credentials will fail' do
|
||||||
|
before do
|
||||||
|
stub_request(:post, "https://crowd.example.org/rest/usermanagement/latest/authentication?username=foo").
|
||||||
|
to_return(:status=>400)
|
||||||
|
get '/auth/crowd/callback', nil, 'rack.session'=>{'omniauth.crowd'=> {"username"=>"foo", "password"=>"ba"}}
|
||||||
|
end
|
||||||
|
it 'should fail' do
|
||||||
|
expect(last_response).to be_redirect
|
||||||
|
expect(last_response.headers['Location']).to match(/invalid_credentials/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /auth/crowd without credentials will redirect to login form' do
|
||||||
|
|
||||||
|
sso_url = 'https://foo.bar'
|
||||||
|
|
||||||
|
before do
|
||||||
|
@using_sessions = true
|
||||||
|
@sso_url = sso_url
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should have the SSO button in the response body' do
|
||||||
|
|
||||||
|
found_legend = found_anchor = nil
|
||||||
|
|
||||||
|
get '/auth/crowd'
|
||||||
|
|
||||||
|
Nokogiri::HTML(last_response.body).xpath('//html/body/form/fieldset/*').each do |element|
|
||||||
|
|
||||||
|
if element.name === 'legend' && element.content() === 'SSO'
|
||||||
|
found_legend = true
|
||||||
|
elsif element.name === 'a' && element.attr('href') === "#{sso_url}/users/auth/crowd/callback"
|
||||||
|
found_anchor = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(found_legend).to(be(true))
|
||||||
|
expect(found_anchor).to(be(true))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
@using_sessions = false
|
||||||
|
@sso_url = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /auth/crowd without credentials will redirect to login form which has custom image in the SSO link' do
|
||||||
|
|
||||||
|
sso_url = 'https://foo.bar'
|
||||||
|
sso_url_image = 'https://foo.bar/image.png'
|
||||||
|
|
||||||
|
before do
|
||||||
|
@using_sessions = true
|
||||||
|
@sso_url = sso_url
|
||||||
|
@sso_url_image = 'https://foo.bar/image.png'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should have the SSO button with a custom image in the response body' do
|
||||||
|
|
||||||
|
found_legend = found_anchor = found_image = false
|
||||||
|
|
||||||
|
get '/auth/crowd'
|
||||||
|
|
||||||
|
Nokogiri::HTML(last_response.body).xpath('//html/body/form/fieldset/*').each do |element|
|
||||||
|
|
||||||
|
if element.name === 'legend' && element.content() === 'SSO'
|
||||||
|
found_legend = true
|
||||||
|
elsif element.name === 'a' && element.attr('href') === "#{sso_url}/users/auth/crowd/callback"
|
||||||
|
|
||||||
|
found_anchor = true
|
||||||
|
|
||||||
|
if element.children.length === 1 && element.children.first.name === 'img' && element.children.first.attr('src') === sso_url_image
|
||||||
|
found_image = true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(found_legend).to(be(true))
|
||||||
|
expect(found_anchor).to(be(true))
|
||||||
|
expect(found_image).to(be(true))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
@using_sessions = false
|
||||||
|
@sso_url = nil
|
||||||
|
@sso_url_image = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /auth/crowd without credentials but with SSO cookie will redirect to callback' do
|
||||||
|
|
||||||
|
sso_url = 'https://foo.bar'
|
||||||
|
|
||||||
|
before do
|
||||||
|
|
||||||
|
@using_sessions = true
|
||||||
|
@sso_url = sso_url
|
||||||
|
|
||||||
|
set_cookie('crowd.token_key=foobar')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should redirect to callback' do
|
||||||
|
get '/auth/crowd'
|
||||||
|
expect(last_response).to be_redirect
|
||||||
|
expect(last_response.headers['Location']).to eq('http://example.org/auth/crowd/callback')
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
|
||||||
|
@using_sessions = false
|
||||||
|
@sso_url = nil
|
||||||
|
|
||||||
|
clear_cookies()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST /auth/crowd/callback without credentials but with SSO cookie will redirect to login form because session is invalid' do
|
||||||
|
|
||||||
|
sso_url = 'https://foo.bar'
|
||||||
|
token = 'foobar'
|
||||||
|
|
||||||
|
before do
|
||||||
|
|
||||||
|
@using_sessions = true
|
||||||
|
@sso_url = sso_url
|
||||||
|
|
||||||
|
stub_request(:get, "https://crowd.example.org/rest/usermanagement/latest/session/#{token}").
|
||||||
|
to_return(:status => [404])
|
||||||
|
|
||||||
|
set_cookie("crowd.token_key=#{token}")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should redirect to login form' do
|
||||||
|
post '/auth/crowd/callback'
|
||||||
|
expect(last_response).to be_redirect
|
||||||
|
expect(last_response.headers['Location']).to match(/invalid_credentials/)
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
|
||||||
|
@using_sessions = false
|
||||||
|
@sso_url = nil
|
||||||
|
|
||||||
|
clear_cookies()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /auth/crowd/callback without credentials but with SSO cookie will succeed' do
|
||||||
|
|
||||||
|
sso_url = 'https://foo.bar'
|
||||||
|
token = 'rtk8eMvqq00EiGn5iJCMZQ00'
|
||||||
|
|
||||||
|
before do
|
||||||
|
|
||||||
|
@using_sessions = true
|
||||||
|
@sso_url = sso_url
|
||||||
|
|
||||||
|
stub_request(:get, "https://crowd.example.org/rest/usermanagement/latest/session/#{token}").
|
||||||
|
to_return(:status => 200, :body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'session.xml')))
|
||||||
|
stub_request(:post, "https://crowd.example.org/rest/usermanagement/latest/session/#{token}").
|
||||||
|
to_return(:status => 200)
|
||||||
|
stub_request(:get, "https://crowd.example.org/rest/usermanagement/latest/user/group/direct?username=foo").
|
||||||
|
to_return(:body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'groups.xml')))
|
||||||
|
|
||||||
|
set_cookie("crowd.token_key=#{token}")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return user data' do
|
||||||
|
|
||||||
|
auth = nil
|
||||||
|
|
||||||
|
get '/auth/crowd/callback'
|
||||||
|
|
||||||
|
auth = last_request.env['omniauth.auth']
|
||||||
|
|
||||||
|
expect(auth['provider']).to eq(:crowd)
|
||||||
|
expect(auth['uid']).to eq('foo')
|
||||||
|
expect(auth['info']).to be_kind_of(Hash)
|
||||||
|
expect(auth['info']['groups'].sort).to eq(["Developers", "jira-users"].sort)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
|
||||||
|
@using_sessions = false
|
||||||
|
@sso_url = nil
|
||||||
|
|
||||||
|
clear_cookies()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /auth/crowd/callback without credentials but with multiple SSO cookies will succeed because one of them is valid' do
|
||||||
|
|
||||||
|
sso_url = 'https://foo.bar'
|
||||||
|
|
||||||
|
before do
|
||||||
|
|
||||||
|
@using_sessions = true
|
||||||
|
@sso_url = sso_url
|
||||||
|
|
||||||
|
stub_request(:get, "https://crowd.example.org/rest/usermanagement/latest/session/foo").
|
||||||
|
to_return(:status => 404)
|
||||||
|
stub_request(:get, "https://crowd.example.org/rest/usermanagement/latest/session/fubar").
|
||||||
|
to_return(:status => 404)
|
||||||
|
stub_request(:get, "https://crowd.example.org/rest/usermanagement/latest/session/rtk8eMvqq00EiGn5iJCMZQ00").
|
||||||
|
to_return(:status => 200, :body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'session.xml')))
|
||||||
|
stub_request(:post, "https://crowd.example.org/rest/usermanagement/latest/session/rtk8eMvqq00EiGn5iJCMZQ00").
|
||||||
|
to_return(:status => 200)
|
||||||
|
stub_request(:get, "https://crowd.example.org/rest/usermanagement/latest/user/group/direct?username=foo").
|
||||||
|
to_return(:body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'groups.xml')))
|
||||||
|
|
||||||
|
header('Cookie', "crowd.token_key=foo;crowd.token_key=rtk8eMvqq00EiGn5iJCMZQ00;crowd.token_key=fubar")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return user data' do
|
||||||
|
|
||||||
|
auth = nil
|
||||||
|
|
||||||
|
get '/auth/crowd/callback'
|
||||||
|
|
||||||
|
auth = last_request.env['omniauth.auth']
|
||||||
|
|
||||||
|
expect(auth['provider']).to eq(:crowd)
|
||||||
|
expect(auth['uid']).to eq('foo')
|
||||||
|
expect(auth['info']).to be_kind_of(Hash)
|
||||||
|
expect(auth['info']['groups'].sort).to eq(["Developers", "jira-users"].sort)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
|
||||||
|
@using_sessions = false
|
||||||
|
@sso_url = nil
|
||||||
|
|
||||||
|
header('Cookie', nil)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,14 @@
|
||||||
|
require 'bundler/setup'
|
||||||
|
Bundler.setup
|
||||||
|
require 'rack/test'
|
||||||
|
require 'webmock'
|
||||||
|
require 'webmock/rspec'
|
||||||
|
require 'nokogiri'
|
||||||
|
|
||||||
|
require 'omniauth_crowd'
|
||||||
|
RSpec.configure do |config|
|
||||||
|
WebMock.disable_net_connect!
|
||||||
|
config.include Rack::Test::Methods
|
||||||
|
config.raise_errors_for_deprecations!
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue