gprov 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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