gprov 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +14 -0
- data/README.markdown +7 -0
- data/Rakefile +5 -0
- data/lib/gprov.rb +4 -0
- data/lib/gprov/auth.rb +1 -0
- data/lib/gprov/auth/clientlogin.rb +65 -0
- data/lib/gprov/connection.rb +88 -0
- data/lib/gprov/error.rb +27 -0
- data/lib/gprov/provision.rb +9 -0
- data/lib/gprov/provision/customerid.rb +44 -0
- data/lib/gprov/provision/entrybase.rb +82 -0
- data/lib/gprov/provision/entrybase/classmethods.rb +52 -0
- data/lib/gprov/provision/entrybase/xmlattr.rb +92 -0
- data/lib/gprov/provision/feed.rb +44 -0
- data/lib/gprov/provision/group.rb +147 -0
- data/lib/gprov/provision/member.rb +48 -0
- data/lib/gprov/provision/orgmember.rb +84 -0
- data/lib/gprov/provision/orgunit.rb +89 -0
- data/lib/gprov/provision/owner.rb +46 -0
- data/lib/gprov/provision/user.rb +132 -0
- data/lib/gprov/version.rb +3 -0
- data/spec/gdata/auth/clientlogin_spec.rb +47 -0
- data/spec/gdata/connection_spec.rb +62 -0
- data/spec/gdata/provision/entrybase_spec.rb +39 -0
- data/spec/spec_helper.rb +10 -0
- metadata +118 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
# = gprov/provision/user.rb: implementation of the gprov provisioning userentry
|
2
|
+
#
|
3
|
+
# == Overview
|
4
|
+
#
|
5
|
+
# implementation of the gprov provisioning userentry
|
6
|
+
#
|
7
|
+
# == Authors
|
8
|
+
#
|
9
|
+
# Adrien Thebo
|
10
|
+
#
|
11
|
+
# == Copyright
|
12
|
+
#
|
13
|
+
# 2011 Puppet Labs
|
14
|
+
#
|
15
|
+
require 'gprov'
|
16
|
+
require 'gprov/provision/feed'
|
17
|
+
require 'gprov/provision/entrybase'
|
18
|
+
module GProv
|
19
|
+
module Provision
|
20
|
+
class User < GProv::Provision::EntryBase
|
21
|
+
|
22
|
+
# The :title attribute is only used after the account has been created
|
23
|
+
# TODO implement :none access for attributes. This should be hidden.
|
24
|
+
xmlattr :title, :type => :string, :xpath => "xmlns:title/text()"
|
25
|
+
|
26
|
+
xmlattr :user_name, :xpath => "apps:login/@userName"
|
27
|
+
xmlattr :suspended, :xpath => "apps:login/@suspended"
|
28
|
+
xmlattr :ip_whitelisted, :xpath => "apps:login/@ipWhitelisted"
|
29
|
+
xmlattr :admin, :xpath => "apps:login/@admin"
|
30
|
+
xmlattr :agreed_to_terms, :xpath => "apps:login/@agreedToTerms"
|
31
|
+
xmlattr :limit, :xpath => "apps:quota/@limit"
|
32
|
+
xmlattr :family_name, :xpath => "apps:name/@familyName"
|
33
|
+
xmlattr :given_name, :xpath => "apps:name/@givenName"
|
34
|
+
xmlattr :change_password_at_next_login, :xpath => "apps:login/@changePasswordAtNextLogin"
|
35
|
+
|
36
|
+
# Adds explicit ordering to attributes for cleaner output
|
37
|
+
def self.attribute_names
|
38
|
+
[
|
39
|
+
:user_name,
|
40
|
+
:given_name,
|
41
|
+
:family_name,
|
42
|
+
:admin,
|
43
|
+
:agreed_to_terms,
|
44
|
+
:change_password_at_next_login,
|
45
|
+
:suspended,
|
46
|
+
:ip_whitelisted,
|
47
|
+
:limit
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
# These attributes appear to never be sent from google but can be
|
52
|
+
# posted back
|
53
|
+
attr_accessor :password, :hash_function_name
|
54
|
+
|
55
|
+
# Retrieves all users within a domain
|
56
|
+
def self.all(connection)
|
57
|
+
feed = GProv::Provision::Feed.new(connection, "/:domain/user/2.0", "/xmlns:feed/xmlns:entry")
|
58
|
+
entries = feed.fetch
|
59
|
+
entries.map { |xml| new(:status => :clean, :connection => connection, :source => xml) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.get(connection, title, options={})
|
63
|
+
response = connection.get("/:domain/user/2.0/#{title}")
|
64
|
+
|
65
|
+
document = Nokogiri::XML(response.body)
|
66
|
+
xml = document.root
|
67
|
+
|
68
|
+
new(:status => :clean, :connection => connection, :source => xml)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Generate a nokogiri representation of this user
|
72
|
+
def to_nokogiri
|
73
|
+
base_document = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
74
|
+
xml.entry('xmlns:atom' => 'http://www.w3.org/2005/Atom',
|
75
|
+
'xmlns:apps' => 'http://schemas.google.com/apps/2006' ) {
|
76
|
+
|
77
|
+
# Namespaces cannot be used until they are declared, so we need to
|
78
|
+
# retroactively declare the namespace of the parent
|
79
|
+
xml.parent.namespace = xml.parent.namespace_definitions.select {|ns| ns.prefix == "atom"}.first
|
80
|
+
xml.category("scheme" => "http://schemas.google.com/g/2005#kind",
|
81
|
+
"term" =>"http://schemas.google.com/apps/2006#user")
|
82
|
+
xml['apps'].login(login_attributes)
|
83
|
+
xml['apps'].quota("limit" => @limit) if @limit
|
84
|
+
xml['apps'].name("familyName" => @family_name, "givenName" => @given_name)
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
base_document
|
89
|
+
end
|
90
|
+
|
91
|
+
def create!
|
92
|
+
response = connection.post("/:domain/user/2.0", {:body => to_nokogiri.to_xml})
|
93
|
+
status = :clean
|
94
|
+
end
|
95
|
+
|
96
|
+
def update!
|
97
|
+
response = connection.put("/:domain/user/2.0/#{title}", {:body => to_nokogiri.to_xml})
|
98
|
+
status = :clean
|
99
|
+
end
|
100
|
+
|
101
|
+
def delete!
|
102
|
+
response = connection.delete("/:domain/user/2.0/#{title}")
|
103
|
+
status = :deleted
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Map object properties to xml tag attributes
|
109
|
+
def login_attributes
|
110
|
+
attrs = {}
|
111
|
+
|
112
|
+
attr_pairs = [
|
113
|
+
{"userName" => @user_name},
|
114
|
+
{"suspended" => @suspended},
|
115
|
+
{"ipWhitelisted" => @ip_whitelisted},
|
116
|
+
{"admin" => @admin},
|
117
|
+
{"changePasswordAtNextLogin" => @change_password_at_next_login},
|
118
|
+
{"agreedToTerms" => @agreed_to_terms},
|
119
|
+
{'password' => @password},
|
120
|
+
{'hashFunctionName' => @hash_function_name},
|
121
|
+
]
|
122
|
+
|
123
|
+
attr_pairs.each do |pair|
|
124
|
+
key = pair.keys.first
|
125
|
+
attrs.merge!(pair) unless pair[key].nil?
|
126
|
+
end
|
127
|
+
|
128
|
+
attrs
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GProv::Auth::ClientLogin do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@klass = GProv::Auth::ClientLogin
|
7
|
+
@instance = @klass.new('test', 'password', 'service')
|
8
|
+
@dummy_form = {:body => {
|
9
|
+
"accountType" => "HOSTED",
|
10
|
+
"Email" => "test",
|
11
|
+
"Passwd" => "password",
|
12
|
+
"service" => "service",
|
13
|
+
}}
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should use the google ClientLogin uri" do
|
17
|
+
GProv::Auth::ClientLogin.base_uri.should == "https://www.google.com/accounts/ClientLogin"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have a token method" do
|
21
|
+
@instance.respond_to?(:token).should == true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should post valid form data to the uri specified base_uri" do
|
25
|
+
stub_response = stub :response
|
26
|
+
stub_response.stubs(:code).returns 200
|
27
|
+
stub_response.stubs(:body).returns "Auth=dummy\n"
|
28
|
+
@klass.expects(:post).with('', @dummy_form).returns stub_response
|
29
|
+
@instance.token
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return nil if authorization failed" do
|
33
|
+
stub_response = stub :response
|
34
|
+
stub_response.stubs(:code).returns 403
|
35
|
+
@klass.expects(:post).with('', @dummy_form).returns stub_response
|
36
|
+
@instance.token.should be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should return the token if authorization succeeded" do
|
40
|
+
stub_response = stub :response
|
41
|
+
stub_response.stubs(:code).returns 200
|
42
|
+
stub_response.stubs(:body).returns "Auth=dummy\n"
|
43
|
+
@klass.expects(:post).with('', @dummy_form).returns stub_response
|
44
|
+
@instance.token.should == "dummy"
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GProv::Connection do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@klass = GProv::Connection
|
7
|
+
@instance = @klass.new("domain", "token")
|
8
|
+
|
9
|
+
@expected_options = {:headers => {
|
10
|
+
'Authorization' => "GoogleLogin auth=token",
|
11
|
+
'Content-Type' => 'application/atom+xml',
|
12
|
+
}}
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should use to google apps API url as the base uri" do
|
16
|
+
@klass.base_uri.should == "https://apps-apis.google.com/a/feeds"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have a domain accessor" do
|
20
|
+
@instance.respond_to?(:domain).should == true
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should expose the default headers" do
|
24
|
+
@instance.default_headers.should == @expected_options
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
describe "http instance method" do
|
29
|
+
[:put, :get, :post, :delete].each do |verb|
|
30
|
+
describe %Q{"#{verb}"} do
|
31
|
+
before do
|
32
|
+
xml = %Q{<?xml version="1.0" encoding="UTF-8"?>\n<test xml="pointy"/>}
|
33
|
+
@stub_request = mock
|
34
|
+
@stub_request.stubs(:code).returns 200
|
35
|
+
@stub_request.stubs(:success?).returns true
|
36
|
+
@stub_request.stubs(:class).returns HTTParty::Response
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should be an instance method" do
|
40
|
+
@instance.respond_to?(verb).should == true
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should be forwarded to the class" do
|
44
|
+
@klass.expects(verb).returns @stub_request
|
45
|
+
@instance.send(verb, '')
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should return the http response" do
|
49
|
+
@klass.expects(verb).returns @stub_request
|
50
|
+
output = @instance.send(verb, "/url")
|
51
|
+
output.class.should == HTTParty::Response
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should interpolate the :domain substring" do
|
55
|
+
@klass.expects(verb).with("/domain", @expected_options).returns @stub_request
|
56
|
+
@instance.send(verb, "/:domain")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class FakeEntry < GProv::Provision::EntryBase
|
4
|
+
|
5
|
+
xml_attr_accessor :test, :xpath => "/foo/bar/text()"
|
6
|
+
xml_attr_accessor :test_transform, :xpath => "/foo/bar/text()", :transform => lambda {|x| x.upcase}
|
7
|
+
end
|
8
|
+
|
9
|
+
describe GProv::Provision::EntryBase do
|
10
|
+
|
11
|
+
before do
|
12
|
+
@klass = GProv::Provision::EntryBase
|
13
|
+
@test_klass = FakeEntry
|
14
|
+
@instance = @test_klass.new
|
15
|
+
end
|
16
|
+
|
17
|
+
describe GProv::Provision::EntryBase::ClassMethods do
|
18
|
+
|
19
|
+
[:xml_attr_accessor, :xml_to_hash, :attributes].each do |method|
|
20
|
+
it "method #{method} should be a class method" do
|
21
|
+
FakeEntry.respond_to?(method).should be_true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "xml_attr_accessor should receive a symbol and hash of attributes" do
|
26
|
+
@test_klass.attributes[:test].should == {:xpath => "/foo/bar/text()"}
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "xml_to_hash" do
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
[:status, :connection].each do |method|
|
35
|
+
it "method #{method} should be an instance method" do
|
36
|
+
@instance.respond_to?(method).should be_true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gprov
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Adrien Thebo
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-02-02 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: httparty
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 27
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 8
|
32
|
+
version: "0.8"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: nokogiri
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 5
|
44
|
+
segments:
|
45
|
+
- 1
|
46
|
+
- 5
|
47
|
+
version: "1.5"
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
description: Ruby bindings to the Google Provisioning API
|
51
|
+
email: adrien@puppetlabs.com
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files: []
|
57
|
+
|
58
|
+
files:
|
59
|
+
- lib/gprov/auth/clientlogin.rb
|
60
|
+
- lib/gprov/auth.rb
|
61
|
+
- lib/gprov/connection.rb
|
62
|
+
- lib/gprov/error.rb
|
63
|
+
- lib/gprov/provision/customerid.rb
|
64
|
+
- lib/gprov/provision/entrybase/classmethods.rb
|
65
|
+
- lib/gprov/provision/entrybase/xmlattr.rb
|
66
|
+
- lib/gprov/provision/entrybase.rb
|
67
|
+
- lib/gprov/provision/feed.rb
|
68
|
+
- lib/gprov/provision/group.rb
|
69
|
+
- lib/gprov/provision/member.rb
|
70
|
+
- lib/gprov/provision/orgmember.rb
|
71
|
+
- lib/gprov/provision/orgunit.rb
|
72
|
+
- lib/gprov/provision/owner.rb
|
73
|
+
- lib/gprov/provision/user.rb
|
74
|
+
- lib/gprov/provision.rb
|
75
|
+
- lib/gprov/version.rb
|
76
|
+
- lib/gprov.rb
|
77
|
+
- spec/gdata/auth/clientlogin_spec.rb
|
78
|
+
- spec/gdata/connection_spec.rb
|
79
|
+
- spec/gdata/provision/entrybase_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
- LICENSE
|
82
|
+
- Rakefile
|
83
|
+
- README.markdown
|
84
|
+
homepage: http://github.com/adrienthebo/ruby-gprov
|
85
|
+
licenses: []
|
86
|
+
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
hash: 3
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
version: "0"
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
hash: 3
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
110
|
+
requirements: []
|
111
|
+
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 1.8.10
|
114
|
+
signing_key:
|
115
|
+
specification_version: 3
|
116
|
+
summary: Ruby bindings to the Google Provisioning API
|
117
|
+
test_files: []
|
118
|
+
|