faction 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a931eff98f4c85176316bb01e874a499ff64119e
4
+ data.tar.gz: 33ad1322fefac0622f9043cca4eece16092eb8ab
5
+ SHA512:
6
+ metadata.gz: 104284e288f39af5d3f9e2d4ce787c2834914de352b92cfa91b0ceaffafc2c35f88c90274c07a3a6056d11523ef942545ac9d8acf4b86fa89b7685801122ee25
7
+ data.tar.gz: 6d08fdedba41b27d5273a693ffebbaad304fbeee69a283180a4edcdbbf6a3ccee6571a64764faf0e389c4a927ba60d6aa07b69b299c10707c23873810dfa17a4
@@ -0,0 +1,4 @@
1
+ *~
2
+ *.gem
3
+ doc/
4
+ .idea/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in faction.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Olli Helenius
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,41 @@
1
+ # Faction
2
+
3
+ Faction is a client for Atlassian Crowd's SOAP API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'faction', github: 'onesto/faction'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install faction
18
+
19
+ ## Usage
20
+
21
+ ### User authentication
22
+
23
+ ```ruby
24
+ token = begin
25
+ crowd = Faction::Client.new('http://localhost:8050/crowd/services/SecurityServer', 'myapp', 'secret')
26
+ validation_factors = {:user_agent => "Some web browser",
27
+ :remote_address => "127.0.0.1"}
28
+ crowd.authenticate_principal('test-user', 'secret-password', validation_factors)
29
+ rescue Faction::AuthenticationException => e
30
+ puts "Authentication failed: #{e}"
31
+ end
32
+ # .. do something with token ..
33
+ ```
34
+
35
+ ## Contributing
36
+
37
+ 1. Fork it
38
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
39
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
40
+ 4. Push to the branch (`git push origin my-new-feature`)
41
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,13 @@
1
+ require 'faction'
2
+
3
+ CROWD_URL = 'http://localhost:8085/crowd/services/SecurityServer'
4
+ APP_NAME = 'application'
5
+ APP_PASSWORD = 'password'
6
+
7
+ TEST_USERNAME = 'test'
8
+ TEST_PASSWORD = 'test'
9
+
10
+ crowd = Faction::Client.new(CROWD_URL, APP_NAME, APP_PASSWORD, :verify_cert => false)
11
+ token = crowd.authenticate_principal(TEST_USERNAME, TEST_PASSWORD)
12
+ puts("#{TEST_USERNAME} logged in, token = #{token}")
13
+ crowd.invalidate_principal_token(token)
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'faction/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "faction"
8
+ spec.version = Faction::VERSION
9
+ spec.authors = ["Olli Helenius"]
10
+ spec.email = ["olli.helenius@onesto.fi"]
11
+ spec.description = %q{A simple Savon-based client for Atlassian Crowd SOAP API}
12
+ spec.summary = %q{A simple Savon-based client for Atlassian Crowd SOAP API}
13
+ spec.homepage = "https://github.com/onesto/faction"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_dependency 'savon', '< 0.8'
25
+ end
@@ -0,0 +1,289 @@
1
+ require 'openssl'
2
+ require 'savon'
3
+ require "faction/version"
4
+
5
+
6
+ module Faction #:nodoc:
7
+
8
+ # Exception base for Faction
9
+ class Exception < ::Exception; end
10
+ # Any sort of Exception from Crowd
11
+ class AuthenticationException < Exception; end
12
+
13
+ # See http://docs.atlassian.com/crowd/current/com/atlassian/crowd/integration/service/soap/server/SecurityServer.html
14
+ # for Crowd SOAP API documentation.
15
+ #
16
+ # The <tt>validation_factors</tt> parameter is a <tt>Hash</tt> that can contain the following keys:
17
+ # * <tt>:user_agent</tt> - <tt>ValidationFactor.USER_AGENT</tt>
18
+ # * <tt>:remote_address</tt> - <tt>ValidationFactor.REMOTE_ADDRESS</tt>
19
+ # * <tt>:remote_host</tt> - <tt>ValidationFactor.REMOTE_HOST</tt>
20
+ # * <tt>:forwarded_for</tt> - <tt>ValidationFactor.X_FORWARDED_FOR</tt>
21
+ # * <tt>:random_number</tt> - <tt>ValidationFactor.RANDOM_NUMBER</tt>
22
+ # * <tt>:name</tt> - <tt>ValidationFactor.NAME</tt>
23
+ class Client
24
+ @@debug = false
25
+
26
+ # Sets the global debugging for Client
27
+ def self.debug=(value)
28
+ @@debug = value
29
+ end
30
+
31
+ # True if Faction is in debug mode
32
+ def self.debug?
33
+ @@debug
34
+ end
35
+
36
+ # Url of the Crowd SecurityServer
37
+ attr_reader :crowd_url
38
+ # Application name
39
+ attr_reader :app_name
40
+ # Application password
41
+ attr_reader :app_password
42
+
43
+ # Instantiates a new client using a "standard" <tt>crowd.properties</tt> file.
44
+ def self.from_properties(properties_file)
45
+ props = Hash[*open(properties_file).readlines.map {|line|
46
+ line.split(/=/, 2).map(&:strip)}.flatten]
47
+ self.new(props['crowd.server.url'] + '/SecurityServer',
48
+ props['application.name'],
49
+ props['application.password'],
50
+ :verify_cert => ['yes', 'true'].include?(props['ssl.verify']))
51
+ end
52
+
53
+ # Creates a new Crowd client.
54
+ #
55
+ # Parameters:
56
+ # * <tt>crowd_url</tt> - The URL to Crowd SecurityServer.
57
+ # * <tt>app_name</tt> - Application name.
58
+ # * <tt>app_password</tt> - Application password.
59
+ # * <tt>options</tt> - A Hash of options (described below).
60
+ #
61
+ # Options
62
+ # * <tt>:verify_cert</tt> - If <tt>false</tt> the peer SSL certificate is not verified.
63
+ #
64
+ # Example:
65
+ # Faction::Client.new('http://localhost:8085/crowd/services/SecurityServer', 'application', 'password')
66
+ #
67
+ def initialize(crowd_url, app_name, app_password, options = {})
68
+ @crowd_url = crowd_url
69
+ @crowd = Savon::Client.new(@crowd_url + (Client.debug? ? '?wsdl' : ''))
70
+ if options[:verify_cert] == false
71
+ @crowd.request.http.ssl_client_auth(:verify_mode => OpenSSL::SSL::VERIFY_NONE)
72
+ end
73
+ @app_name = app_name
74
+ @app_password = app_password
75
+ @app_token = nil
76
+ end
77
+
78
+ # See <tt>SecurityServerClient.authenticatePrincipal</tt>
79
+ def authenticate_principal(name, password, validation_factors = nil)
80
+ authenticated_crowd_call(:authenticate_principal,
81
+ { 'auth:application' => app_name,
82
+ 'auth:name' => name,
83
+ 'auth:credential' => {'auth:credential' => password},
84
+ 'auth:validationFactors' => convert_validation_factors(validation_factors)})
85
+ end
86
+
87
+ # See <tt>SecurityServerClient.createPrincipalToken</tt>
88
+ def create_principal_token(name, validation_factors = nil)
89
+ authenticated_crowd_call(:create_principal_token,
90
+ { 'auth:application' => app_name,
91
+ 'auth:name' => name,
92
+ 'auth:validationFactors' => convert_validation_factors(validation_factors)})
93
+ end
94
+
95
+ # See <tt>SecurityServerClient.invalidatePrincipalToken</tt>
96
+ def invalidate_principal_token(token)
97
+ authenticated_crowd_call(:invalidate_principal_token, token) && nil
98
+ end
99
+
100
+ # See <tt>SecurityServerClient.getCookieInfo</tt>
101
+ # Returns a <tt>Hash</tt> containing the cookie info fields.
102
+ def cookie_info
103
+ authenticated_crowd_call(:get_cookie_info)
104
+ end
105
+
106
+ def get_cookie_info
107
+ $stderr.puts('faction: get_cookie_info deprecated, use cookie_info instead')
108
+ cookie_info
109
+ end
110
+
111
+ # See <tt>SecurityServerClient.isValidPrincipalToken</tt>
112
+ def valid_principal_token?(token, validation_factors = nil)
113
+ authenticated_crowd_call(:is_valid_principal_token,
114
+ token,
115
+ {'auth:validationFactors' => convert_validation_factors(validation_factors)})
116
+ end
117
+
118
+ # See <tt>SecurityServer.updatePrincipalCredential</tt>
119
+ def update_principal_credential(name, new_password)
120
+ authenticated_crowd_call(:update_principal_credential,
121
+ name,
122
+ {'auth:credential' => new_password, 'auth:encryptedCredential' => false})
123
+ end
124
+
125
+ # See <tt>SecurityServer.findPrincipalByToken</tt>
126
+ # Returns the principal information as a <tt>Hash</tt>.
127
+ def find_principal_by_token(token)
128
+ simplify_soap_attributes(authenticated_crowd_call(:find_principal_by_token, token))
129
+ end
130
+
131
+ def find_principal_by_name(name)
132
+ simplify_soap_attributes(authenticated_crowd_call(:find_principal_by_name, name))
133
+ end
134
+
135
+ def find_principal_with_attributes_by_name(name)
136
+ simplify_soap_attributes(authenticated_crowd_call(:find_principal_with_attributes_by_name, name))
137
+ end
138
+
139
+ def find_group_by_name(name)
140
+ simplify_soap_group(authenticated_crowd_call(:find_group_by_name, name))
141
+ end
142
+
143
+ def find_group_memberships(name)
144
+ arrayify(authenticated_crowd_call(:find_group_memberships, name)[:string])
145
+ end
146
+
147
+ def group_names
148
+ authenticated_crowd_call(:find_all_group_names)[:string]
149
+ end
150
+
151
+ def principal_names
152
+ authenticated_crowd_call(:find_all_principal_names)[:string]
153
+ end
154
+
155
+ def granted_authorities
156
+ authenticated_crowd_call(:get_granted_authorities)[:string]
157
+ end
158
+
159
+ def cache_enabled?
160
+ authenticated_crowd_call(:is_cache_enabled)
161
+ end
162
+
163
+ def cache_time
164
+ authenticated_crowd_call(:get_cache_time)
165
+ end
166
+
167
+ def add_principal_to_group(principal, group)
168
+ authenticated_crowd_call(:add_principal_to_group, principal, group) && nil
169
+ end
170
+
171
+ private
172
+
173
+ CROWD_NAMESPACES = {
174
+ 'xmlns:wsdl' => 'urn:SecurityServer',
175
+ 'xmlns:auth' => 'http://authentication.integration.crowd.atlassian.com',
176
+ 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
177
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
178
+ }
179
+
180
+ USER_AGENT = "User-Agent" #:nodoc:
181
+ REMOTE_ADDRESS = "remote_address" #:nodoc:
182
+ REMOTE_HOST = "remote_host" #:nodoc:
183
+ X_FORWARDED_FOR = "X-Forwarded-For" #:nodoc:
184
+ RANDOM_NUMBER = "Random-Number" #:nodoc:
185
+ NAME = "NAME" #:nodoc:
186
+
187
+ VALIDATION_FACTOR_MAPPING = {
188
+ :user_agent => USER_AGENT,
189
+ :remote_address => REMOTE_ADDRESS,
190
+ :remote_host => REMOTE_HOST,
191
+ :forwarded_for => X_FORWARDED_FOR,
192
+ :random_number => RANDOM_NUMBER,
193
+ :name => NAME
194
+ }
195
+
196
+ def convert_validation_factors(in_validation_factors)
197
+ return nil if in_validation_factors.nil?
198
+ result = in_validation_factors.map do |name, value|
199
+ raise Faction::Exception, "Invalid validation factor #{name}" if !VALIDATION_FACTOR_MAPPING.include?(name)
200
+ {'auth:name' => VALIDATION_FACTOR_MAPPING[name], 'auth:value' => value}
201
+ end
202
+ {'auth:ValidationFactor' => result}
203
+ end
204
+
205
+ def simplify_soap_attributes(soap_object)
206
+ attributes = soap_object[:attributes][:soap_attribute].inject({}) do |hash, item|
207
+ hash[item[:name].to_sym] = item[:values][:string]
208
+ hash
209
+ end
210
+ soap_object.merge(:attributes => attributes)
211
+ end
212
+
213
+ def simplify_soap_group(soap_group)
214
+ soap_group.merge(:members => soap_group[:members][:string])
215
+ end
216
+
217
+ def arrayify(soap_object)
218
+ if soap_object.nil?
219
+ []
220
+ elsif soap_object.is_a? Array
221
+ soap_object
222
+ else
223
+ [soap_object]
224
+ end
225
+ end
226
+
227
+ def app_authentication
228
+ Hash['auth:name' => app_name,
229
+ 'auth:token' => app_token]
230
+ end
231
+
232
+ def ensure_app_token!
233
+ app_token
234
+ end
235
+
236
+ def app_token
237
+ @app_token ||= authenticate_application
238
+ end
239
+
240
+ def authenticate_application
241
+ response = crowd_call(:authenticate_application) do |soap|
242
+ soap.body.merge!('wsdl:in0' => {
243
+ 'auth:name' => app_name,
244
+ 'auth:credential' => {
245
+ 'auth:credential' => app_password,
246
+ 'auth:encryptedCredential' => false,
247
+ }})
248
+ end
249
+ response[:token]
250
+ end
251
+
252
+ def crowd_call(name, &block)
253
+ method = (Client.debug? ? name : "#{name}!").to_sym
254
+ response = @crowd.call(method) do |soap|
255
+ soap.namespaces.merge!(CROWD_NAMESPACES)
256
+ soap.body = {}
257
+ yield soap if block_given?
258
+ end
259
+ response.to_hash[:"#{name}_response"][:out]
260
+ end
261
+
262
+ def real_authenticated_crowd_call(name, *args)
263
+ ensure_app_token!
264
+ crowd_call(name) do |soap|
265
+ soap.body = {'wsdl:in0' => app_authentication}
266
+ order = ['wsdl:in0']
267
+ args.each_with_index do |arg, index|
268
+ soap.body["wsdl:in#{index + 1}"] = arg
269
+ order << "wsdl:in#{index + 1}"
270
+ end
271
+ soap.body[:order!] = order
272
+ end
273
+ end
274
+
275
+ def authenticated_crowd_call(name, *args)
276
+ begin
277
+ real_authenticated_crowd_call(name, *args)
278
+ rescue Savon::SOAPFault => f
279
+ # retry once
280
+ @app_token = nil
281
+ begin
282
+ real_authenticated_crowd_call(name, *args)
283
+ rescue Savon::SOAPFault => f
284
+ raise AuthenticationException, f.message
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,3 @@
1
+ module Faction
2
+ VERSION = '1.0.0'
3
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: faction
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Olli Helenius
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: savon
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - <
46
+ - !ruby/object:Gem::Version
47
+ version: '0.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - <
53
+ - !ruby/object:Gem::Version
54
+ version: '0.8'
55
+ description: A simple Savon-based client for Atlassian Crowd SOAP API
56
+ email:
57
+ - olli.helenius@onesto.fi
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - examples/log_in_and_out.rb
68
+ - faction.gemspec
69
+ - lib/faction.rb
70
+ - lib/faction/version.rb
71
+ homepage: https://github.com/onesto/faction
72
+ licenses:
73
+ - MIT
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.0.3
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: A simple Savon-based client for Atlassian Crowd SOAP API
95
+ test_files: []