gprov 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright 2011 Puppet Labs
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
data/README.markdown ADDED
@@ -0,0 +1,7 @@
1
+ Ruby-GProv
2
+ ==========
3
+
4
+ Description
5
+ -----------
6
+
7
+ Ruby implementation of the [Google Provisioning API](http://code.google.com/googleapps/domain/gdata_provisioning_api_v2.0_reference.html).
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
2
+ require 'gprov'
3
+ Dir["tasks/rake/**/*"].each do |rakefile|
4
+ load rakefile
5
+ end
data/lib/gprov.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'gprov/version'
2
+ require 'gprov/auth'
3
+ require 'gprov/connection'
4
+ require 'gprov/provision'
data/lib/gprov/auth.rb ADDED
@@ -0,0 +1 @@
1
+ require 'gprov/auth/clientlogin'
@@ -0,0 +1,65 @@
1
+ # = gprov/auth/clientlogin.rb Implements the google clientLogin authentication method
2
+ #
3
+ # == Overview
4
+ #
5
+ # Implements the Google clientLogin method as documented in
6
+ # http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html
7
+ #
8
+ # == Authors
9
+ #
10
+ # Adrien Thebo
11
+ #
12
+ # == Copyright
13
+ #
14
+ # 2011 Puppet Labs
15
+ #
16
+ require 'httparty'
17
+ module GProv
18
+ module Auth
19
+ class ClientLogin
20
+ include HTTParty
21
+ base_uri 'https://www.google.com/accounts/ClientLogin'
22
+
23
+ # Instantiates a new ClientLogin object.
24
+ #
25
+ # Arguments:
26
+ # * email: the email account to use for authentication
27
+ # * password
28
+ # * service: the Google service to generate authentication for
29
+ # * options: An additional hash of parameters
30
+ # * debug: turns on debug information for the request/response
31
+ #
32
+ # TODO make service an optional field
33
+ def initialize(email, password, service, options={})
34
+ @email = email
35
+ @password = password
36
+ @service = service
37
+ @options = options
38
+
39
+ # additional options parsing
40
+ if @options[:debug]
41
+ self.class.debug_output $stderr
42
+ end
43
+ end
44
+
45
+ # Given an instantiated ClientLogin object, performs the actual
46
+ # request/response handling of the authentication information.
47
+ #
48
+ # TODO More comprehensive error checking for this.
49
+ # TODO CAPTCHA handling
50
+ def token
51
+ form_data = {
52
+ "accountType" => "HOSTED",
53
+ "Email" => @email,
54
+ "Passwd" => @password,
55
+ "service" => @service,
56
+ }
57
+
58
+ response = self.class.post('', {:body => form_data})
59
+ if response.code == 200 and response.body =~ /Auth=(.*)\n/
60
+ $1
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,88 @@
1
+ # = gprov/connection.rb: common interface for the google apps API
2
+ #
3
+ # == Overview
4
+ #
5
+ # Provides a single point of access for making http requests against the google
6
+ # apps API.
7
+ #
8
+ # This adds the correct authorization header and content-type for make
9
+ # requests against the google API work correctly, so that calling classes
10
+ # don't need to handle authentication, formatting, or basic error handling.
11
+ #
12
+ # == Authors
13
+ #
14
+ # Adrien Thebo
15
+ #
16
+ # == Copyright
17
+ #
18
+ # 2011 Puppet Labs
19
+ #
20
+ require 'httparty'
21
+ require 'gprov/error'
22
+
23
+ module GProv
24
+ class Connection
25
+ include HTTParty
26
+ base_uri "https://apps-apis.google.com/a/feeds/"
27
+
28
+ attr_reader :domain
29
+ def initialize(domain, token, options = {})
30
+ @domain = domain
31
+ @token = token
32
+ @options = options
33
+
34
+ if @options[:debug]
35
+ self.class.debug_output $stderr
36
+ end
37
+ end
38
+
39
+ def default_headers
40
+ {:headers => {
41
+ 'Authorization' => "GoogleLogin auth=#{@token}",
42
+ 'Content-Type' => 'application/atom+xml',
43
+ }}
44
+ end
45
+
46
+ # Forward instance level http methods to the class, after adding
47
+ # authorization and content-type information.
48
+ [:put, :get, :post, :delete].each do |verb|
49
+ define_method verb do |path, *args|
50
+
51
+ # Assign default arguments and validate passed arguments
52
+ if path.nil?
53
+ raise "#{self.class}##{verb} requires a non-nil path"
54
+ end
55
+ options = *args
56
+ options ||= {}
57
+ options.merge! default_headers
58
+
59
+ # Interpolate the :domain substring into a url to allow for the domain
60
+ # to be in an arbitary position of a request
61
+ path.gsub!(":domain", @domain)
62
+
63
+ if options[:noop] or @options[:noop]
64
+ $stderr.puts "Would have attempted the following call"
65
+ $stderr.puts "#{verb} #{path} #{options.inspect}"
66
+ else
67
+ # Return the request to the calling class so that the caller can
68
+ # determine the outcome of the request.
69
+ output = self.class.send(verb, path, options)
70
+ case output.code
71
+ when 401
72
+ raise GProv::Error::TokenInvalid.new(output)
73
+ when 403
74
+ raise GProv::Error::InputInvalid.new(output)
75
+ when 503
76
+ raise GProv::Error::QuotaExceeded.new(output)
77
+ else
78
+ if ! output.success?
79
+ raise GProv::Error.new(output)
80
+ else
81
+ output
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,27 @@
1
+ # = gprov/error.rb
2
+ #
3
+ # == Overview
4
+ #
5
+ # Common definition of possible gprov errors.
6
+ #
7
+ # == Authors
8
+ #
9
+ # Adrien Thebo
10
+ #
11
+ # == Copyright
12
+ #
13
+ # 2011 Puppet Labs
14
+ #
15
+ require 'gprov'
16
+ module GProv
17
+ class Error < Exception
18
+ attr_reader :request
19
+
20
+ def initialize(request = nil)
21
+ @request = request
22
+ end
23
+ class TokenInvalid < GProv::Error; end
24
+ class InputInvalid < GProv::Error; end
25
+ class QuotaExceeded < GProv::Error; end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ require 'gprov/provision/user'
2
+ require 'gprov/provision/group'
3
+ require 'gprov/provision/customerid'
4
+ require 'gprov/provision/orgunit'
5
+
6
+ module GProv
7
+ module Provision
8
+ end
9
+ end
@@ -0,0 +1,44 @@
1
+ # = gprov/customerid.rb
2
+ #
3
+ # == Overview
4
+ #
5
+ # Retrieves a customerid string for an accompanying domain
6
+ #
7
+ # == Authors
8
+ #
9
+ # Adrien Thebo
10
+ #
11
+ # == Copyright
12
+ #
13
+ # 2011 Puppet Labs
14
+ #
15
+ require 'gprov'
16
+ require 'gprov/provision/entrybase'
17
+ module GProv
18
+ module Provision
19
+ class CustomerID < GProv::Provision::EntryBase
20
+
21
+ xmlattr :customer_id, :xpath => %Q{apps:property[@name = "customerId"]/@value}
22
+ xmlattr :name, :xpath => %Q{apps:property[@name = "name"]/@value}
23
+ xmlattr :description, :xpath => %Q{apps:property[@name = "description"]/@value}
24
+
25
+ xmlattr :customer_org_unit_name do
26
+ xpath %Q{apps:property[@name = "customerOrgUnitName"]/@value}
27
+ end
28
+
29
+ xmlattr :customer_org_unit_description do
30
+ xpath %Q{apps:property[@name = "customerOrgUnitDescription"]/@value}
31
+ end
32
+
33
+ def self.get(connection, options={})
34
+ response = connection.get("/customer/2.0/customerId")
35
+
36
+ document = Nokogiri::XML(response.body)
37
+ xml = document.root
38
+
39
+ new(:status => :clean, :connection => connection, :source => xml)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,82 @@
1
+ # = gprov/provision/entrybase.rb: base class for provisioning api objects
2
+ #
3
+ # == Overview
4
+ #
5
+ # Provides the top level constructs for mapping XML feeds to objects and back
6
+ #
7
+ # == Authors
8
+ #
9
+ # Adrien Thebo
10
+ #
11
+ # == Copyright
12
+ #
13
+ # 2011 Puppet Labs
14
+ #
15
+ require 'nokogiri'
16
+ require 'gprov/provision/entrybase/classmethods'
17
+ module GProv
18
+ module Provision
19
+ class EntryBase
20
+
21
+ extend GProv::Provision::EntryBase::ClassMethods
22
+
23
+ # Status with respect to google.
24
+ # TODO protected?
25
+ # values: :new, :clean, :dirty, :deleted
26
+ attr_reader :status
27
+ attr_reader :connection
28
+
29
+ # Instantiates a new entry object.
30
+ #
31
+ # Possible data sources:
32
+ # * Hash of attribute names and values
33
+ # * A nokogiri node containing the root of the object
34
+ def initialize(opts={})
35
+
36
+ @status = (opts[:status] || :new)
37
+
38
+ if opts[:connection]
39
+ @connection = opts[:connection]
40
+ else
41
+ raise ArgumentError, "#{self.class}.new requires a connection parameter"
42
+ end
43
+
44
+ case source = opts[:source]
45
+ when Hash
46
+ attributes_from_hash source
47
+ when Nokogiri::XML::Node
48
+ hash = xml_to_hash(source)
49
+ attributes_from_hash hash
50
+ when NilClass
51
+ # New object!
52
+ else
53
+ raise
54
+ end
55
+ end
56
+
57
+ # Takes all xml_attr_accessors defined and an xml document and
58
+ # extracts the values from the xml into a hash.
59
+ def xml_to_hash(xml)
60
+ h = {}
61
+ if attrs = self.class.attributes
62
+ attrs.inject(h) do |hash, attr|
63
+ hash[attr.name] = attr.parse(xml)
64
+ hash
65
+ end
66
+ end
67
+ h
68
+ end
69
+
70
+ # Maps hash key/value pairs to object attributes
71
+ def attributes_from_hash(hash)
72
+ hash.each_pair do |k, v|
73
+ if respond_to? "#{k}=".intern
74
+ send("#{k}=".intern, v)
75
+ else
76
+ raise ArgumentError, %Q{Received invalid attribute "#{k}"}
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,52 @@
1
+ # = gprov/provision/entrybase/classmethods.rb
2
+ #
3
+ # == Overview
4
+ #
5
+ # Generates the DSL style behavior for entrybase and adds some convenience
6
+ # methods
7
+ #
8
+ # == Authors
9
+ #
10
+ # Adrien Thebo
11
+ #
12
+ # == Copyright
13
+ #
14
+ # 2011 Puppet Labs
15
+ #
16
+ require 'nokogiri'
17
+ require 'gprov/provision/entrybase/xmlattr'
18
+ module GProv
19
+ module Provision
20
+ class EntryBase
21
+ module ClassMethods
22
+ # Generates xmlattrs and encapsulates parsing logic
23
+ def xmlattr(name, options={}, &block)
24
+ attr = GProv::Provision::EntryBase::XMLAttr.new(name, options)
25
+ attr.instance_eval &block if block_given?
26
+ @attrs ||= []
27
+ @attrs << attr
28
+ attr_accessor name
29
+ end
30
+
31
+ # This is a class method because xmlattr objects are not directly
32
+ # exposed, so parsing needs to happen in the class.
33
+
34
+ # Provides an ordered list of xml attributes. Mainly used to give
35
+ # a list of attributes in a specific order.
36
+ def attributes
37
+ @attrs
38
+ end
39
+
40
+ def attribute_names
41
+ @attrs.map {|a| a.name }
42
+ end
43
+
44
+ # Transforms standard ruby attribute names to something slightly more
45
+ # human readable.
46
+ def attribute_titles
47
+ attribute_names.map {|f| f.to_s.capitalize.sub(/$/, ":").gsub(/_/, " ") }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,92 @@
1
+ # = gprov/provision/entrybase/xmlattr.rb: attribute accessors with xml annotations
2
+ #
3
+ # == Overview
4
+ #
5
+ # Defines a data type and provides the logic for extracting and formatting
6
+ # object information from xml data
7
+ #
8
+ # Attribute accessors are not directly defined, because this class was designed
9
+ # to be used DSL style
10
+ #
11
+ # == Examples
12
+ #
13
+ # xmlattr :demo, :type => :numeric, :xpath => "example/xpath/text()"
14
+ #
15
+ # xmlattr :demo do
16
+ # type :numeric
17
+ # xpath "example/xpath/text()"
18
+ # end
19
+ #
20
+ # == Authors
21
+ #
22
+ # Adrien Thebo
23
+ #
24
+ # == Copyright
25
+ #
26
+ # 2011 Puppet Labs
27
+ #
28
+ require 'nokogiri'
29
+
30
+ module GProv
31
+ module Provision
32
+ class EntryBase
33
+ class XMLAttr
34
+ attr_reader :name
35
+ def initialize(name, options={})
36
+ @name = name
37
+ @type = :string
38
+ methodhash(options)
39
+ end
40
+
41
+ def xpath(val=nil)
42
+ @xpath = val if val
43
+ @xpath
44
+ end
45
+
46
+ def type(val=nil)
47
+
48
+ if [:numeric, :string, :bool].include? val
49
+ @type = val
50
+ else
51
+ raise ArgumentException
52
+ end
53
+
54
+ @type
55
+ end
56
+
57
+ def parse(xml)
58
+ @value = xml.at_xpath(@xpath).to_s
59
+ format
60
+ end
61
+
62
+ private
63
+
64
+ def format
65
+ case @type
66
+ when :numeric
67
+ @value = @value.to_i
68
+ when :string
69
+ # no op
70
+ when :bool
71
+ if @value == "true"
72
+ @value = true
73
+ else # XXX sketchy
74
+ @value = false
75
+ end
76
+ end
77
+ @value
78
+ end
79
+
80
+ def methodhash(hash)
81
+ hash.each_pair do |method, value|
82
+ if respond_to? method
83
+ send method, value
84
+ else
85
+ raise ArgumentError, %Q{Received invalid attribute "#{method}"}
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end