kennethkalmer-postini 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + "/../../lib/postini4r"
2
+
3
+ gem 'cucumber'
4
+ require 'cucumber'
5
+ gem 'rspec'
6
+ require 'spec'
@@ -0,0 +1,11 @@
1
+ module Matchers
2
+ def contain(expected)
3
+ simple_matcher("contain #{expected.inspect}") do |given, matcher|
4
+ matcher.failure_message = "expected #{given.inspect} to contain #{expected.inspect}"
5
+ matcher.negative_failure_message = "expected #{given.inspect} not to contain #{expected.inspect}"
6
+ given.index expected
7
+ end
8
+ end
9
+ end
10
+
11
+ World(Matchers)
@@ -0,0 +1,119 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ # requirements
5
+ require 'rubygems'
6
+ require 'handsoap'
7
+
8
+ require 'postini/exceptions'
9
+
10
+ # = Configuration
11
+ #
12
+ # This module just provides access to configuration details used by the rest of
13
+ # the gem.
14
+ #
15
+ # Current configuration values are:
16
+ # * api_key
17
+ # * system_number
18
+ # * username
19
+ # * password
20
+ #
21
+ # The +api_key+ is your unique API keys obtained from Postini via their Early
22
+ # Access program.
23
+ #
24
+ # Postini.api_key = "your48characterkey"
25
+ #
26
+ # The +system_number+ can be any known system number (known to your account).
27
+ # The gem uses the Postini Endpoint Resolver service, but in some cases needs a
28
+ # known system to work from. Refer to the "Locating Your System" section of the
29
+ # Early Access documentation for more information.
30
+ #
31
+ # Postini.system_number = 8
32
+ #
33
+ # The +username+ parameter is required if:
34
+ # 1. Your org is using POP authentication, and thus needs the admin user's
35
+ # authentication details, not the end-user's.
36
+ # 2. You omit the optional username and password string for the request
37
+ #
38
+ # Postini.username = 'administrat@jumboinc.com'
39
+ #
40
+ # The +password+ parameter is require if:
41
+ # 1. Your org is using POP authentication, and thus needs the admin
42
+ # user's authentication details, not the end-users's.
43
+ # 2. You can omit the optional username and password arguments for a
44
+ # specific request.
45
+ #
46
+ # Postini.password = 'secret'
47
+ #
48
+ # All of the above configurations can either be preloaded from a yaml
49
+ # file or environment variables. Create a yaml file with the following
50
+ # structure:
51
+ #
52
+ # api_key:
53
+ # system_number:
54
+ # username:
55
+ # password:
56
+ #
57
+ # And call +Postini.configure!('/path/to/file.yml')+ to load the
58
+ # values. Alternatively you can set the following environment variables
59
+ # and call +Postini.configure!+ without a path.
60
+ #
61
+ # POSTINI_API_KEY
62
+ # POSTINI_SYSTEM_NUMBER
63
+ # POSTINI_USERNAME
64
+ # POSTINI_PASSWORD
65
+ #
66
+ # = Debugging
67
+ #
68
+ # Lots to debug when an API is classified as "Early access", here are different
69
+ # controls for setting the debugging verbosity
70
+ #
71
+ # * logger = Ruby-logger compatibe logger
72
+ # * debug = true/false - Enable more verbose logging
73
+ #
74
+ # Specify a 'Logger' compatible logging facility in order to use the debug
75
+ # features of the gem. All the other settings increase the verbosity to varying
76
+ # degrees, and allow you to access some soap4r internals as well.
77
+ #
78
+ module Postini
79
+ VERSION = "0.1.0"
80
+
81
+ autoload :ConfigurationCheck, "postini/configuration_check"
82
+ autoload :Endpoints, "postini/endpoints"
83
+ autoload :EndpointResolverService, "postini/endpoint_resolver_service"
84
+ autoload :AutomatedBatchService, "postini/automated_batch_service"
85
+
86
+ # Don't debug by default
87
+ @debug = false
88
+
89
+ class << self
90
+
91
+ attr_accessor :api_key, :system_number, :username, :password, :debug, :logger
92
+
93
+ def configured? #:nodoc:
94
+ !self.api_key.nil? &&
95
+ !self.system_number.nil? &&
96
+ !self.username.nil? &&
97
+ !self.password.nil?
98
+ end
99
+
100
+ # Configure the library from yaml configuration file or
101
+ # environment variables
102
+ def configure!( yaml = nil )
103
+ data = if yaml
104
+ YAML.load_file( yaml )
105
+ else
106
+ {
107
+ 'api_key' => ENV['POSTINI_API_KEY'],
108
+ 'system_number' => ENV['POSTINI_SYSTEM_NUMBER'],
109
+ 'username' => ENV['POSTINI_USERNAME'],
110
+ 'password' => ENV['POSTINI_PASSWORD']
111
+ }
112
+ end
113
+
114
+ data.each do |k,v|
115
+ self.send( "#{k}=", v )
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,464 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Postini
3
+ class AutomatedBatchService < Handsoap::Service
4
+
5
+ include ConfigurationCheck
6
+
7
+ endpoint Postini::Endpoints.automated_batch
8
+
9
+ on_create_document do |doc|
10
+ doc.alias 'aut', 'http://postini.com/PSTN/SOAPAPI/v2/automatedbatch'
11
+ end
12
+
13
+ def on_fault( fault )
14
+ exception = RemoteException.delegate( fault.reason )
15
+
16
+ # Handle specifics, translate to higher level exception
17
+
18
+ # or raise
19
+ raise exception
20
+ end
21
+
22
+ # public methods
23
+
24
+ # Associates an additional address with a user's primary email
25
+ # address. The alias receives the same filtering and shares the
26
+ # same User Ouarantine as the user’s primary email address.
27
+ #
28
+ # * confirm - If an existing user address is being overwritten to
29
+ # become an alias address, use confirm
30
+ #
31
+ # Raises a Postini::BatchError if something goes wrong
32
+ def add_alias( user_address, alias_address, confirm = true )
33
+ response = invoke("aut:addalias") do |message|
34
+ build_auth!( message )
35
+ message.add('userAddressOrId', user_address)
36
+ message.add('aliasAddress', alias_address)
37
+ message.add('confirm', 'confirm') if confirm
38
+ end
39
+
40
+ true
41
+ end
42
+ requires_configured :add_alias
43
+
44
+ # Adds the domain record +name+ to the organization
45
+ # +orgid+. Enclose +orgid+ in double quotes or preceded with a
46
+ # '\' symbol if it contains a quote ('), double quote ("),
47
+ # backslash (\), apostrophe, commas, #, = symbols.
48
+ #
49
+ # Raises a Postini::BatchException if something goes wrong
50
+ def add_domain( orgid, name )
51
+ response = invoke( "aut:adddomain" ) do |message|
52
+ build_auth!( message )
53
+ message.add('orgNameOrId', orgid )
54
+ message.add('args') do |args|
55
+ args.add('domain', name)
56
+ end
57
+ end
58
+
59
+ true
60
+ end
61
+ requires_configured :add_domain
62
+
63
+ # Adds a user (+address+) to an organization (+org+). Enclose
64
+ # +orgid+ in double quotes or preceded with a
65
+ # '\' symbol if it contains a quote ('), double quote ("),
66
+ # backslash (\), apostrophe, commas, #, = symbols.
67
+ #
68
+ # Raises a Postini::BatchException if something foes wrong
69
+ def add_user( orgid, address, welcome = false )
70
+ response = invoke("aut:adduser") do |message|
71
+ build_auth!( message )
72
+ message.add('userAddress', address)
73
+ message.add('args') do |args|
74
+ args.add('org', orgid)
75
+ args.add('welcome', welcome)
76
+ end
77
+ end
78
+
79
+ true
80
+ end
81
+ requires_configured :add_user
82
+
83
+ # This operation is an Service Management API’s AutomatedBatch
84
+ # utility. It checks the user’s email, API license key, and
85
+ # password authentication tokens used for connection and
86
+ # authorization validation between the web service and the client.
87
+ #
88
+ # * email - The email address entered by the user.
89
+ # * apiKey - A unique customer and product ID.
90
+ # * password -- The PMP password entered by the user.
91
+ #
92
+ # If the organization is configured for POP authorization, always
93
+ # use the administrator's email login and password which will be
94
+ # in the PMP format. An end user's POP password will fail.
95
+ def check_auth( email = nil, password = nil, api_key = nil )
96
+ response = invoke("aut:checkauth") do |message|
97
+ build_auth!( message, email, password, api_key )
98
+ end
99
+
100
+ return true
101
+ end
102
+ requires_configured :check_auth
103
+
104
+ # Removes the specified alias completely from the email security
105
+ # service.
106
+ def delete_alias( alias_address )
107
+ response = invoke("aut:deletealias") do |message|
108
+ build_auth!( message )
109
+ message.add('aliasAddress', alias_address)
110
+ end
111
+
112
+ return true
113
+ end
114
+ requires_configured :delete_alias
115
+
116
+ # Removes the domain from the organizational hierarchy. All the
117
+ # users for the domain needs to be removed first by #delete_user.
118
+ #
119
+ # Raises a Postini::BatchException if something goes wrong
120
+ def delete_domain( name )
121
+ response = invoke("aut:deletedomain") do |message|
122
+ build_auth!( message )
123
+ message.add('domainNameOrId', name)
124
+ end
125
+
126
+ true
127
+ end
128
+ requires_configured :delete_domain
129
+
130
+ # Delete a user record from the email security service.
131
+ #
132
+ # Raises a Postini::BatchExceptio if something goes wrong
133
+ def delete_user( address )
134
+ response = invoke("aut:deleteuser") do |message|
135
+ build_auth!( message )
136
+ message.add('userAddressOrId', address)
137
+ end
138
+
139
+ true
140
+ end
141
+ requires_configured :delete_user
142
+
143
+ # Retrieve the details of the domain from the email security
144
+ # service as a hash with the following keys:
145
+ #
146
+ # :org => string - Name of the parent org
147
+ # :substrip => true/false - Whether sub domain stripping in enabled
148
+ # :aliased_to => nil/string - Alias of another domain
149
+ # :aliased_as => array - List of domain aliases
150
+ # :id => integer - Domain id
151
+ # :name => string - Domain name
152
+ def display_domain( name )
153
+ response = invoke("aut:displaydomain") do |message|
154
+ build_auth!( message )
155
+ message.add('domainNameOrId', name )
156
+ end
157
+
158
+ parse_display_domain_results( response.document.xpath('//tns:displaydomainResponse', tns).first )
159
+ end
160
+ requires_configured :display_domain
161
+
162
+ # Retrieve the details of the user from the email securty service
163
+ # as a hash with following keys:
164
+ #
165
+ # :active => bool
166
+ # :address => string
167
+ # :approved_recipients => array
168
+ # :approved_senders => array
169
+ # :blocked_senders => array
170
+ # :create_method => integer
171
+ # :created_date => Time
172
+ # :filter_adult => string
173
+ # :filter_bulk => string
174
+ # :filter_getrich => string
175
+ # :filter_offers => string
176
+ # :filter_racial => string
177
+ # :initial_password => string
178
+ # :junkmail_filter => bool
179
+ # :lang_locale => string
180
+ # :lastmod_date => Time
181
+ # :message_encryption => string
182
+ # :message_limit => string
183
+ # :message_limited => bool
184
+ # :message_count => integer
185
+ # :notice_address => string
186
+ # :org => string
187
+ # :password => string
188
+ # :timezone => string
189
+ # :id => integer
190
+ # :virus_notify => string
191
+ # :virus_state => bool
192
+ # :weblocked => bool
193
+ # :welcome_count => integer
194
+ # :wireless_state => string
195
+ def display_user( address )
196
+ response = invoke("aut:displayuser") do |message|
197
+ build_auth!( message )
198
+ message.add('userAddressOrId', address)
199
+ end
200
+
201
+ parse_display_user_results( response.document.xpath('//tns:displayuserResponse', tns).first )
202
+ end
203
+ requires_configured :display_user
204
+
205
+ # Modify a domain by moving it to a new organization, setting or
206
+ # removing aliases, or enabling/disabling subdomain
207
+ # stripping. Changes are passed as a hash with the following keys:
208
+ #
209
+ # :org => string - New organization to move domain to
210
+ # :substrip => bool - New new value for substrip
211
+ # :aliases => array - Add new domain aliases to the array, to
212
+ # remove an existing domain alias prepend the name
213
+ # with a hyphen (-)
214
+ def modify_domain( name, changes = {} )
215
+ valid_changes = {}
216
+ valid_changes['neworg'] = changes[:org] if changes.has_key?(:org)
217
+ if changes.has_key?(:substrip)
218
+ valid_changes['substrip'] = ( changes[:substrip] == true ? 'yes' : 'no' )
219
+ end
220
+ valid_changes['alias'] = changes[:aliases].to_a.join(', ') if changes.has_key?(:aliases)
221
+
222
+ response = invoke("aut:modifydomain") do |message|
223
+ build_auth!( message )
224
+ message.add('domainNameOrId', name)
225
+ message.add('domainModifications') do |mods|
226
+ valid_changes.each do |k,v|
227
+ mods.add( k, v )
228
+ end
229
+ end
230
+ end
231
+
232
+ display_domain( name )
233
+ end
234
+ requires_configured :modify_domain
235
+
236
+ # Modify a user extensively by providing any of the following keys
237
+ # in the changes hash
238
+ #
239
+ # :active => bool
240
+ # :address => string (change the email address)
241
+ # :approved_recipients => array (full or changes)
242
+ # :approved_senders => array (full or changes)
243
+ # :blocked_senders => array (full or changes)
244
+ # :filter_adult => string
245
+ # :filter_bulk => string
246
+ # :filter_getrich => string
247
+ # :filter_offers => string
248
+ # :filter_racial => string
249
+ # :initial_password => string
250
+ # :junkemail_filter => bool
251
+ # :lang_locale => string
252
+ # :message_limit => integer
253
+ # :message_limited => bool
254
+ # :notice_address => string
255
+ # :org => string
256
+ # :password => string
257
+ # :virus_notify => bool
258
+ # :virus_state => bool
259
+ # :weblocked => bool
260
+ # :wireless_state => string
261
+ #
262
+ # Notes on array parameters: Provide the full set of values, or an
263
+ # array of diffs (new entries prepended with a plus (+) and
264
+ # entries to be removed prepended with a hyphen (-).
265
+ #
266
+ # Notes on *all* parameters: I don't use all of these, nor will I
267
+ # ever, so please test them well and report any bugs to me.
268
+ def modify_user( address, changes = {} )
269
+ valid_changes = {}
270
+ valid_changes[:orgid] = changes[:org] if changes.has_key?(:org)
271
+
272
+ # Vanilla string copies
273
+ [ :active, :address, :filter_adult, :filter_bulk, :filter_getrich,
274
+ :filter_offers, :filter_racial, :initial_password, :lang_locale,
275
+ :notice_address, :password, :wireless_state, :message_limit
276
+ ].each do |k|
277
+
278
+ valid_changes[k] = changes[k] if changes.has_key?(k)
279
+ end
280
+
281
+ # Booleans need some special handling
282
+ [ :active, :junkemail_filter, :message_limited, :virus_notify,
283
+ :virus_state, :weblocked
284
+ ].each do |k|
285
+
286
+ if changes.has_key?(k)
287
+ valid_changes[k] = ( changes[k] == true ? 'yes' : 'no' )
288
+ end
289
+ end
290
+
291
+ # Join the arrays
292
+ [ :approved_senders, :approved_recipients, :blocked_senders ].each do |k|
293
+
294
+ if changes.has_key?( k )
295
+ valid_changes[k] = changes[k].join(', ')
296
+ end
297
+ end
298
+
299
+ # pray
300
+ response = invoke("aut:modifyuser") do |message|
301
+ build_auth!( message )
302
+ message.add('userAddressOrId', address)
303
+ message.add('userModifications') do |mods|
304
+ valid_changes.each do |k,v|
305
+ mods.add( k.to_s, v )
306
+ end
307
+ end
308
+ end
309
+
310
+ address = valid_changes[:address] || address
311
+
312
+ display_user( address )
313
+ end
314
+ requires_configured :modify_user
315
+
316
+ # Determines if connections to the web service and the web service
317
+ # client are not blocked. It is a simple round trip test.
318
+ #
319
+ # * true - The connection is successful. If it fails, the
320
+ # development tool will throw either an error, or warning
321
+ # depending upon the type of failure.
322
+ # * false - To test the exception handling between the web service
323
+ # and the application, use test<false> which will
324
+ # complete the roundtrip between the servers and return
325
+ # a StatusException.
326
+ #
327
+ # The Endpoint Resolver web service is not used with the test command.
328
+ def test( pass = true )
329
+ response = invoke("aut:test") do |message|
330
+ message.add('should_work', pass)
331
+ end
332
+
333
+ parse_test_results( response.document.xpath('//tns:testResponse', tns).first )
334
+ end
335
+ requires_configured :test
336
+
337
+ private
338
+
339
+ def tns
340
+ { 'tns' => 'http://postini.com/PSTN/SOAPAPI/v2/automatedbatch' }
341
+ end
342
+
343
+ def build_auth!( message, email = nil, password = nil, api_key = nil )
344
+ message.add('authElem') do |auth|
345
+ auth.add('apiKey', api_key || Postini.api_key)
346
+ auth.add('email', email || Postini.username )
347
+ auth.add('pword', password || Postini.password )
348
+ end
349
+ end
350
+
351
+ def to_array( string )
352
+ array = string.split(/[,\s+]/)
353
+ array.delete_if { |e| e == 'empty' }
354
+ array
355
+ end
356
+
357
+ # helpers
358
+
359
+ def parse_test_results( node )
360
+ node.xpath('./confirmation_message/text()').to_s
361
+ end
362
+
363
+ def parse_display_domain_results( node )
364
+ # <tns:displaydomainResponse>
365
+ # <domainRecord>
366
+ # <aliasedto>postini4r1.co.za</aliasedto>
367
+ # <aliasedfrom>postini4rone.co.za</aliasedfrom>
368
+ # <domainid>10000</domainid>
369
+ # <domainname>postini4r1.co.za</domainname>
370
+ # <org>postini-0.stage.example.org</org>
371
+ # <substrip>0</substrip>
372
+ # </domainRecord>
373
+ # </tns:displaydomainResponse>
374
+
375
+ data = {
376
+ :id => node.xpath('./domainRecord/domainid/text()').to_s.to_i,
377
+ :name => node.xpath('./domainRecord/domainname/text()').to_s,
378
+ :org => node.xpath('./domainRecord/org/text()').to_s,
379
+ :substrip => node.xpath('./domainRecord/substrip/text()').to_s == '1'
380
+ }
381
+
382
+ unless node.xpath('./domainRecord/aliasedto/text()').empty?
383
+ data[:aliased_to] = node.xpath('./domainRecord/aliasedto/text()').to_s
384
+ end
385
+
386
+ unless node.xpath('./domainRecord/aliasedfrom/text()').empty?
387
+ data[:aliased_from] = to_array( node.xpath('./domainRecord/aliasedfrom/text()').to_s )
388
+ end
389
+
390
+ data
391
+ end
392
+
393
+ def parse_display_user_results( node )
394
+ # <tns:displayuserResponse>
395
+ # <userRecord>
396
+ # <active>yes</active>
397
+ # <address>info@example.com</address>
398
+ # <approved_recipients>empty</approved_recipients>
399
+ # <approved_senders>empty</approved_senders>
400
+ # <blocked_senders>empty</blocked_senders>
401
+ # <create_method>3</create_method>
402
+ # <created_date>123456789</created_date>
403
+ # <filter_adult>moderately aggressive</filter_adult>
404
+ # <filter_bulk>leniently aggressive</filter_bulk>
405
+ # <filter_getrich>moderately aggressive</filter_getrich>
406
+ # <filter_offers>moderately aggressive</filter_offers>
407
+ # <filter_racial>moderately aggressive</filter_racial>
408
+ # <initial_password/>
409
+ # <junkmail_filter>on</junkmail_filter>
410
+ # <lang_locale/>
411
+ # <lastmod_date>123456789</lastmod_date>
412
+ # <message_count>0</message_count>
413
+ # <message_limit/>
414
+ # <message_limited>no</message_limited>
415
+ # <notice_address/>
416
+ # <orgid>Example Org Users</orgid>
417
+ # <password>randomhashrepresentation</password>
418
+ # <timezone>Europe/Lisbon</timezone>
419
+ # <user_id>1</user_id>
420
+ # <virus_notify>Organization default</virus_notify>
421
+ # <virus_state>on</virus_state>
422
+ # <weblocked>no</weblocked>
423
+ # <welcome_count>1</welcome_count>
424
+ # <wireless_state>unavailable</wireless_state>
425
+ # </userRecord>
426
+ # </tns:displayuserResponse>
427
+
428
+ data = {
429
+ :active => node.xpath('./userRecord/active/text()').to_s == 'yes',
430
+ :address => node.xpath('./userRecord/address/text()').to_s,
431
+ :approved_recipients => to_array( node.xpath('./userRecord/approved_recipients/text()').to_s ),
432
+ :approved_senders => to_array( node.xpath('./userRecord/approved_senders/text()').to_s ),
433
+ :blocked_senders => to_array( node.xpath('./userRecord/blocked_senders/text()').to_s ),
434
+ :create_method => node.xpath('./userRecord/create_method/text()').to_s.to_i,
435
+ :created_date => Time.at( node.xpath('./userRecord/created_date/text()').to_s.to_i ),
436
+ :filter_adult => node.xpath('./userRecord/filter_adult/text()').to_s,
437
+ :filter_bulk => node.xpath('./userRecord/filter_bulk/text()').to_s,
438
+ :filter_getrich => node.xpath('./userRecord/filter_getrich/text()').to_s,
439
+ :filter_offers => node.xpath('./userRecord/filter_offers/text()').to_s,
440
+ :filter_racial => node.xpath('./userRecord/filter_racial/text()').to_s,
441
+ :initial_password => node.xpath('./userRecord/initial_password/text()').to_s,
442
+ :junkmail_filter => node.xpath('./userRecord/junkmail_filter/text()').to_s == 'on',
443
+ :lang_locale => node.xpath('./userRecord/lang_local/text()').to_s,
444
+ :lastmod_date => Time.at( node.xpath('./userRecord/lastmod_date/text()').to_s.to_i ),
445
+ :message_encryption => node.xpath('./userRecord/message_encryption/text()').to_s,
446
+ :message_limit => node.xpath('./userRecord/message_limit/text()').to_s,
447
+ :message_limited => node.xpath('./userRecord/message_limited/text()').to_s == 'yes',
448
+ :message_count => node.xpath('./userRecord/message_count/text()').to_s.to_i,
449
+ :notice_address => node.xpath('./userRecord/notice_address/text()').to_s,
450
+ :org => node.xpath('./userRecord/orgid/text()').to_s,
451
+ :password => node.xpath('./userRecord/password/text()').to_s,
452
+ :timezone => node.xpath('./userRecord/timezone/text()').to_s,
453
+ :id => node.xpath('./userRecord/user_id/text()').to_s,
454
+ :virus_notify => node.xpath('./userRecord/virus_notify/text()').to_s,
455
+ :virus_state => node.xpath('./userRecord/virus_state/text()').to_s,
456
+ :weblocked => node.xpath('./userRecord/weblocked/text()').to_s == 'yes',
457
+ :welcome_count => node.xpath('./userRecord/welcome_count/text()').to_s.to_i,
458
+ :wireless_state => node.xpath('./userRecord/wireless_state/text()').to_s
459
+ }
460
+
461
+ data
462
+ end
463
+ end
464
+ end