zimbra-rest-api 0.1.9

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3983b077d22f8e4aadacb07f1b9c2d3bcb8ef15d
4
+ data.tar.gz: 1fedc155082cbb417a38d42fc5f2de94cba22168
5
+ SHA512:
6
+ metadata.gz: 1008ba7d41a0dce623bea98946e1d10f529eb4b2e28c060e9789118c77480754efa080e41b9bdbfaa3cebbf5e0f8a3a2a440bda41940c6ff80deb908585f36f1
7
+ data.tar.gz: de154e19295ca91f709b36f198d1165d4e1a2b3369f242b77f0f036a51c90ea79871ba1a617298966e42cf07d6f76a4b1e58a778c81e9937e8b96a486416cf30
@@ -0,0 +1,5 @@
1
+ /envs.sh
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
@@ -0,0 +1,46 @@
1
+ ### v 0.1.9
2
+
3
+ #### Get DLs memberships for an Account
4
+
5
+ ```
6
+ get /accounts/:id/memberships
7
+ ```
8
+
9
+ Returns an Array of DLs Hashes:
10
+
11
+ ```
12
+ [{"id"=>"747972ab-a410-4f17-8d5e-db7be21d75e9",
13
+ "name"=>"abierta@customer.dev",
14
+ "via"=>nil},
15
+ {"id"=>"e92f0d3d-1733-45a2-8e05-967f2d5a0cbe",
16
+ "name"=>"restringida@customer.dev",
17
+ "via"=>nil},
18
+ {"id"=>"8b788848-e670-48b4-a72c-c9097b32a066",
19
+ "name"=>"restringida@zbox.cl",
20
+ "via"=>nil}]
21
+ ```
22
+
23
+
24
+ ### v 0.1.8
25
+
26
+ #### Enable and Disable Archiving for Accounts
27
+
28
+ ```
29
+ post /accounts/:id/archive/disable
30
+ post /accounts/:id/archive/enable, { cos_id: cos_id, archive_name: archive_name}
31
+ ```
32
+
33
+
34
+ ### v 0.1.4
35
+
36
+ #### new endpoint `/distribution_lists/:id/add_members`
37
+
38
+ ```json
39
+ members: { [email1, email2] }
40
+ ```
41
+
42
+ #### new endpoint `/distribution_lists/:id/remove_members`
43
+
44
+ ```json
45
+ members: { [email1, email2] }
46
+ ```
@@ -0,0 +1,25 @@
1
+ FROM phusion/passenger-ruby21:0.9.15
2
+ MAINTAINER Patricio Bruna <pbruna@itlinux.cl>
3
+
4
+ RUN rm -f /etc/service/nginx/down
5
+ RUN rm -f /etc/service/sshd/down
6
+ RUN mkdir -p /home/app/zimbra_pre_auth_router
7
+ RUN mkdir -p /home/app/zimbra_pre_auth_router/tmp
8
+
9
+ WORKDIR /home/app/zimbra_pre_auth_router
10
+ ADD Gemfile /home/app/zimbra_pre_auth_router/
11
+ ADD Gemfile.lock /home/app/zimbra_pre_auth_router/
12
+ RUN bundle install
13
+
14
+ ADD config/pbruna-ssh-key.pub /tmp/your_key
15
+ RUN cat /tmp/your_key >> /root/.ssh/authorized_keys && rm -f /tmp/your_key
16
+ RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
17
+
18
+ # Aquí para que no moleste al cache
19
+ ADD . /home/app/zimbra_pre_auth_router
20
+ ADD config/zimbra_preauth_router-nginx.conf /etc/nginx/sites-enabled/default
21
+ ADD config/nginx-env.conf /etc/nginx/main.d/nginx-env.conf
22
+
23
+ RUN chown 9999:9999 -R /home/app/zimbra_pre_auth_router
24
+
25
+ CMD ["/sbin/my_init"]
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in zimbra_rest_api.gemspec
4
+ gem 'ruby-zimbra', :github => 'pbruna/ruby-zimbra', :branch => :master
5
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Patricio Bruna
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,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |task|
5
+ task.libs << %w(test lib)
6
+ task.pattern = 'test/test_*.rb'
7
+ end
8
+
9
+ task default: :test
@@ -0,0 +1,51 @@
1
+ # ZimbraRestApi
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/zimbra_rest_api`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'zimbra_rest_api'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install zimbra_rest_api
22
+
23
+ ## Usage
24
+
25
+ ### Search
26
+ You can search al the objects like:
27
+
28
+ ```
29
+ get '/accounts/', zimbraIsAdminAccount: 'TRUE'
30
+ ```
31
+
32
+ or
33
+
34
+ ```
35
+ ldap_filter = '(|(zimbraMailDeliveryAddress=*@zboxapp.dev)(zimbraMailDeliveryAddress=*@customer1.dev))'
36
+ get '/accounts/', raw_ldap_filter: ldap_filter
37
+ ```
38
+
39
+ ## Development
40
+
41
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
42
+
43
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it ( https://github.com/[my-github-username]/zimbra_rest_api/fork )
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create a new Pull Request
@@ -0,0 +1,22 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+ require 'zimbra_rest_api'
4
+
5
+ ####### Configurarion Options ############################
6
+
7
+ ZimbraRestApi.zimbra_soap_url = ENV['zimbra_soap_url']
8
+ ZimbraRestApi.zimbra_admin_user = ENV['zimbra_admin_user']
9
+ ZimbraRestApi.zimbra_admin_password = ENV['zimbra_admin_password']
10
+ #ZimbraRestApi.api_id = "12345678"
11
+
12
+ ####### END CONFIGURATION ############################
13
+
14
+ ###### DONT TOUCH FROM HERE ######################
15
+
16
+ puts '------------------------------------------------'
17
+ puts 'Starting server with the following configuration'
18
+ puts "SOAP URL: #{ZimbraRestApi.zimbra_soap_url}"
19
+ puts "ADMIN USER: #{ZimbraRestApi.zimbra_admin_user}"
20
+ puts "------------------------------------------------\n"
21
+
22
+ run ZimbraRestApi::App
@@ -0,0 +1,97 @@
1
+ module ZimbraRestApi
2
+ module Helpers
3
+
4
+ def resource_index(resource, params = {})
5
+ object = object_factory(resource)
6
+ begin
7
+ result = object.all(params)
8
+ return json({}) if result.nil?
9
+ set_pagination_headers(result[:search_total], params)
10
+ json(result[:results])
11
+ rescue ZimbraRestApi::TO_MANY_RESULTS => e
12
+ result = { 'errors' => { e.to_s => e.message } }
13
+ json result
14
+ end
15
+ end
16
+
17
+ def resource_count(resource, params = {})
18
+ object = object_factory(resource)
19
+ params.merge!('max_results' => 0)
20
+ json object.count(params)
21
+ end
22
+
23
+ def resource_show(resource, id)
24
+ object = object_factory(resource)
25
+ result = object.find(id)
26
+ return json(result) if result
27
+ status 404
28
+ end
29
+
30
+ def resource_create(resource, params)
31
+ object = object_factory(resource)
32
+ begin
33
+ json object.create(params)
34
+ rescue Zimbra::HandsoapErrors::SOAPFault => e
35
+ json({ errors: [ e.message ]})
36
+ end
37
+ end
38
+
39
+ def resource_update(resource, id, params)
40
+ object = object_factory(resource)
41
+ result = object.find(id)
42
+ return status 404 if result.nil?
43
+ begin
44
+ json result.update_attributes(params)
45
+ rescue Exception => e
46
+ json({ errors: [ e.message ]})
47
+ end
48
+ end
49
+
50
+ def resource_delete(resource, id)
51
+ object = object_factory(resource)
52
+ result = object.find(id)
53
+ return status 404 if result.nil?
54
+ begin
55
+ result.delete
56
+ status 200
57
+ rescue Exception => e
58
+ json({ errors: [ e.message ]})
59
+ end
60
+ end
61
+
62
+ def resource_add_grant(resource, id, params)
63
+ object = object_factory(resource)
64
+ result = object.find(id)
65
+ return status 404 if result.nil?
66
+ begin
67
+ json result.add_grant(params)
68
+ rescue Exception => e
69
+ json({ errors: [ e.message ]})
70
+ end
71
+ end
72
+
73
+ def resource_revoke_grant(resource, id, params)
74
+ object = object_factory(resource)
75
+ result = object.find(id)
76
+ return status 404 if result.nil?
77
+ begin
78
+ json result.revoke_grant(params)
79
+ rescue Exception => e
80
+ json({ errors: [ e.message ]})
81
+ end
82
+ end
83
+
84
+ def set_pagination_headers(total, pagination)
85
+ headers('X-Total' => total.to_s,
86
+ 'X-Page' => (pagination['page'] || 1).to_s,
87
+ 'X-Per-Page' => (pagination['per_page'] || 10).to_s
88
+ )
89
+ end
90
+
91
+ def object_factory(resource)
92
+ "ZimbraRestApi::#{resource.camelize(true)}".constantize
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,58 @@
1
+ # Doc placeholder
2
+ module ZimbraRestApi
3
+ class Account < ZimbraBase
4
+
5
+ def add_alias(alias_name)
6
+ zmobject.add_alias(alias_name)
7
+ end
8
+
9
+ def delegated_auth_token
10
+ zmobject.delegated_auth_token
11
+ end
12
+
13
+ def disable_archive
14
+ zmobject.disable_archive
15
+ end
16
+
17
+ def enable_archive(cos_id = nil, archive_name = nil)
18
+ zmobject.enable_archive(cos_id, archive_name)
19
+ end
20
+
21
+ def mailbox
22
+ zmobject.mailbox
23
+ end
24
+
25
+ def memberships
26
+ results = zmobject.memberships
27
+ results.map { |r| {id: r.id, name: r.name, via: r.via} }
28
+ end
29
+
30
+ def remove_alias(alias_name)
31
+ zmobject.remove_alias(alias_name)
32
+ end
33
+
34
+ def self.create(params = {})
35
+ name = params.delete('name')
36
+ password = params.delete('password')
37
+ result = Zimbra::Account.create(name, password, params)
38
+ new(result)
39
+ end
40
+
41
+ def self.mailbox(account_id)
42
+ Zimbra::Account.mailbox account_id
43
+ end
44
+
45
+ def set_password(new_password)
46
+ zmobject.set_password new_password
47
+ end
48
+
49
+ def update_attributes(attributes)
50
+ if attributes['password']
51
+ set_password(attributes.delete('password'))
52
+ end
53
+ attributes.delete('password')
54
+ super
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ # Doc placeholder
2
+ module ZimbraRestApi
3
+ class Cos < ZimbraBase
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ # Doc placeholder
2
+ module ZimbraRestApi
3
+ class DistributionList < ZimbraBase
4
+
5
+ attr_reader :domain_id
6
+
7
+ def initialize(zmobject)
8
+ super
9
+ @domain_id = name.split(/@/)[1]
10
+ end
11
+
12
+ def add_members(members)
13
+ zmobject.add_members(members)
14
+ end
15
+
16
+ def remove_members(members)
17
+ zmobject.remove_members(members)
18
+ end
19
+
20
+ def modify_members(members)
21
+ zmobject.modify_members members
22
+ end
23
+
24
+ def update_attributes(attributes)
25
+ if attributes['members']
26
+ modify_members(attributes.delete('members'))
27
+ end
28
+ attributes.delete('members')
29
+ super
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ # Doc placeholder
2
+ module ZimbraRestApi
3
+ class Domain < ZimbraBase
4
+
5
+ def self.count_accounts(domain_id)
6
+ Zimbra::Domain.count_accounts domain_id
7
+ end
8
+
9
+ def count_accounts
10
+ zmobject.count_accounts
11
+ end
12
+
13
+ def set_max_accounts(total = 0, cos_quota = [])
14
+ result = zmobject.set_max_accounts total, cos_quota
15
+ Domain.new result
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ module ZimbraRestApi
2
+ class ZimbraBase
3
+ include ZimbraRestApi::ZimbraObject
4
+
5
+ attr_accessor :zmobject
6
+
7
+ def initialize(zmobject)
8
+ @zmobject = zmobject
9
+ @zmobject.acls # Force loading
10
+ instance_variables = get_instance_values(zmobject)
11
+ set_instance_variables(instance_variables)
12
+ end
13
+
14
+ private
15
+
16
+ def get_instance_values(object)
17
+ return nil if object.nil?
18
+ hash = {}
19
+ object.instance_variables.each do |v|
20
+ hash[v] = object.instance_variable_get(v)
21
+ end
22
+ hash
23
+ end
24
+
25
+ def set_instance_variables(instance_variables)
26
+ instance_variables.each do |name, value|
27
+ instance_variable_set(name, value)
28
+ self.class.send(:attr_accessor, name.to_s.gsub(/@/, ''))
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ require 'json'
2
+ require 'uri'
3
+ require 'uuid'
4
+ require 'zimbra'
5
+ require 'pp'
6
+ require_relative 'helpers'
7
+ require_relative 'zimbra_rest_api/zimbra_object'
8
+ require_relative 'zimbra_rest_api/utils'
9
+ require_relative 'zimbra_rest_api/errors'
10
+ require_relative 'models/zimbra_base'
11
+ require_relative 'models/domain'
12
+ require_relative 'models/account'
13
+ require_relative 'models/distribution_list'
14
+ require_relative 'models/cos'
15
+
16
+ # Doc placeholder
17
+ module ZimbraRestApi
18
+ autoload :App, 'zimbra_rest_api/app'
19
+
20
+ class << self
21
+ attr_accessor :zimbra_admin_user, :zimbra_admin_password
22
+ attr_accessor :api_id
23
+ attr_writer :zimbra_max_results
24
+ attr_reader :zimbra_soap_url
25
+
26
+ def authenticate!
27
+ Zimbra.admin_api_url = zimbra_soap_url
28
+ Zimbra.login(zimbra_admin_user, zimbra_admin_password)
29
+ end
30
+
31
+ def zimbra_max_results
32
+ @zimbra_max_results ||= 200
33
+ end
34
+
35
+ def zimbra_soap_url=(url)
36
+ uri = URI.parse(url)
37
+ fail URI::InvalidURIError unless uri.is_a?(URI::HTTP)
38
+ @zimbra_soap_url = url
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,207 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/cookies'
3
+ require 'sinatra/json'
4
+ require 'logger'
5
+
6
+ class Sinatra::Base
7
+ set :show_exceptions => false
8
+
9
+ error { |err|
10
+ Rack::Response.new(
11
+ [{'error' => err.message}.to_json],
12
+ 500,
13
+ {'Content-type' => 'application/json'}
14
+ ).finish
15
+ }
16
+ end
17
+
18
+ # Doc placeholder
19
+ module ZimbraRestApi
20
+ class App < Sinatra::Base
21
+ helpers Sinatra::Cookies, ZimbraRestApi::Helpers
22
+
23
+ RESOURCES = %w(domain account distribution_list cos)
24
+
25
+ before do
26
+ if ZimbraRestApi.api_id
27
+ error 401 unless env['HTTP_X_API_TOKEN'] == ZimbraRestApi.api_id
28
+ end
29
+ ZimbraRestApi.authenticate!
30
+ end
31
+
32
+ configure :production, :development do
33
+ enable :logging
34
+ enable :session
35
+ end
36
+
37
+ RESOURCES.each do |resource|
38
+ # Index route
39
+ get "/#{resource.plural}/?" do
40
+ resource_index(resource, params)
41
+ end
42
+
43
+ # Count route
44
+ get "/#{resource.plural}/count" do
45
+ resource_count(resource, request.params)
46
+ end
47
+
48
+ # Show route
49
+ get "/#{resource.plural}/:id/?" do
50
+ resource_show(resource, params['id'])
51
+ end
52
+
53
+ # Create route
54
+ post "/#{resource.plural}/?" do
55
+ resource_create(resource, params)
56
+ end
57
+
58
+ # Update route
59
+ put "/#{resource.plural}/:id/?" do
60
+ resource_update(resource, params[:id], request.params)
61
+ end
62
+
63
+ # Delete route
64
+ delete "/#{resource.plural}/:id/?" do
65
+ resource_delete(resource, params[:id])
66
+ end
67
+
68
+ # Add grants
69
+ post "/#{resource.plural}/:id/grants/add/?" do
70
+ resource_add_grant(resource, params[:id], request.params)
71
+ end
72
+
73
+ # Revoke grants
74
+ post "/#{resource.plural}/:id/grants/revoke/?" do
75
+ resource_revoke_grant(resource, params[:id], request.params)
76
+ end
77
+
78
+ end
79
+
80
+ # Domains nested routes
81
+
82
+ # Domain count_accounts
83
+ get '/domains/:id/count_accounts' do
84
+ # We need the id, so we lookup the domain if we do not get an UUID
85
+ if UUID.validate(params['id'])
86
+ json Domain.count_accounts(params['id'])
87
+ else
88
+ domain = Domain.find(params['id'])
89
+ json domain.count_accounts
90
+ end
91
+ end
92
+
93
+ # Domain accounts
94
+ get '/domains/:id/accounts' do
95
+ # Only lookup domain if id is an UUID
96
+ domain = params['id']
97
+ domain = Domain.find(params['id']) if UUID.validate(params['id'])
98
+ query = request.params.merge(domain: domain.to_s)
99
+ resource_index('account', query)
100
+ end
101
+
102
+ # Domain Distribution Lists
103
+ get '/domains/:id/distribution_lists' do
104
+ # Only lookup domain if id is an UUID
105
+ domain = params['id']
106
+ domain = Domain.find(params['id']) if UUID.validate(params['id'])
107
+ query = request.params.merge(domain: domain.to_s)
108
+ resource_index('distribution_list', query)
109
+ end
110
+
111
+ # Domain Add Accounts Quota
112
+ post '/domains/:id/accounts_quota' do
113
+ domain = Domain.find(params['id'])
114
+ max_accounts = request.params['total']
115
+ quota_by_cos = request.params['cos']
116
+ json domain.set_max_accounts(max_accounts, quota_by_cos)
117
+ end
118
+
119
+ # Accounts custom routes
120
+ # Account mailbox info
121
+ get '/accounts/:id/mailbox' do
122
+ # We need the id, so we lookup the account if we do not get an UUID
123
+ if UUID.validate(params['id'])
124
+ json Account.mailbox(params['id'])
125
+ else
126
+ account = Account.find(params['id'])
127
+ json account.mailbox
128
+ end
129
+ end
130
+
131
+ post '/accounts/:id/add_alias' do
132
+ account = Account.find(params['id'])
133
+ alias_name = request.params['alias_name']
134
+ if account.add_alias(alias_name)
135
+ json alias_name: alias_name
136
+ else
137
+ json errors: ["Alias not added for #{account.name}"]
138
+ end
139
+ end
140
+
141
+ post '/accounts/:id/remove_alias' do
142
+ account = Account.find(params['id'])
143
+ alias_name = request.params['alias_name']
144
+ if account.remove_alias(alias_name)
145
+ json alias_name: alias_name
146
+ else
147
+ json errors: ["Alias no removed for #{account.name}"]
148
+ end
149
+ end
150
+
151
+ get '/accounts/:id/delegated_token' do
152
+ account = Account.find(params['id'])
153
+ token = account.delegated_auth_token
154
+ json delegated_token: token
155
+ end
156
+
157
+ # Enable account archive
158
+ # params => cos_id, archive_name
159
+ post "/accounts/:id/archive/enable" do
160
+ account = Account.find(params['id'])
161
+ cos_id = request.params['cos_id']
162
+ archive_name = request.params['archive_name']
163
+ result = account.enable_archive(cos_id, archive_name)
164
+ json result: result
165
+ end
166
+
167
+ # Disable account archive
168
+ post "/accounts/:id/archive/disable" do
169
+ account = Account.find(params['id'])
170
+ result = account.disable_archive
171
+ json result: result
172
+ end
173
+
174
+ # Account DLs memberships
175
+ get '/accounts/:id/memberships' do
176
+ account = Account.find(params['id'])
177
+ memberships = account.memberships
178
+ json memberships
179
+ end
180
+
181
+ # DistributionList
182
+
183
+ # add_members
184
+ post '/distribution_lists/:id/add_members' do
185
+ dl = DistributionList.find params['id']
186
+ members = request.params['members']
187
+ begin
188
+ json DistributionList.new(dl.add_members members)
189
+ rescue Exception => e
190
+ json({ errors: [ e.message ]})
191
+ end
192
+ end
193
+
194
+ # add_members
195
+ post '/distribution_lists/:id/remove_members' do
196
+ dl = DistributionList.find params['id']
197
+ members = request.params['members']
198
+ begin
199
+ json DistributionList.new(dl.remove_members members)
200
+ rescue Exception => e
201
+ json({ errors: [ e.message ]})
202
+ end
203
+ end
204
+
205
+ run! if app_file == $PROGRAM_NAME
206
+ end
207
+ end
@@ -0,0 +1,5 @@
1
+ module ZimbraRestApi
2
+
3
+ class TO_MANY_RESULTS < StandardError; end
4
+
5
+ end
@@ -0,0 +1,20 @@
1
+ class String
2
+
3
+ def camelize(first_letter_in_uppercase = true)
4
+ if first_letter_in_uppercase
5
+ self.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
6
+ else
7
+ self[0] + camelize(self)[1..-1]
8
+ end
9
+ end
10
+
11
+ def constantize
12
+ Object.const_get self
13
+ end
14
+
15
+ def plural
16
+ return self if self.downcase == 'cos'
17
+ self.to_s + 's'
18
+ end
19
+
20
+ end
@@ -0,0 +1,3 @@
1
+ module ZimbraRestApi
2
+ VERSION = '0.1.9'
3
+ end
@@ -0,0 +1,175 @@
1
+ module ZimbraRestApi
2
+
3
+ # Class placeholder
4
+ module ZimbraObject
5
+ def self.included(base)
6
+ base.send :include, InstanceMethods
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module InstanceMethods
11
+ def to_json(arg = nil)
12
+ hash = {}
13
+ self.instance_variables.each do |var|
14
+ name = var.to_s.gsub(/@/, '')
15
+ hash[name] = self.instance_variable_get var
16
+ end
17
+ hash.to_json
18
+ end
19
+
20
+ def from_json! string
21
+ JSON.load(string).each do |var, val|
22
+ self.instance_variable_set var, val
23
+ end
24
+ end
25
+
26
+ def update_attributes(attributes)
27
+ fail ArgumentError.new('Hash expected') unless attributes.is_a?Hash
28
+ result = zmobject.modify(attributes)
29
+ self.class.new(result) if result
30
+ end
31
+
32
+ def delete
33
+ zmobject.delete
34
+ end
35
+
36
+ def acl_factory(attrs = {})
37
+ fail ArgumentError.new('Hash expected') unless attrs.is_a?Hash
38
+ return if attrs.empty?
39
+ attrs['grantee_class'] = Zimbra::ACL::TARGET_MAPPINGS[attrs['grantee_class']]
40
+ Zimbra::ACL.new(grantee_name: attrs['grantee_name'],
41
+ grantee_class: attrs['grantee_class'],
42
+ name: attrs['name']
43
+ )
44
+ end
45
+
46
+ def add_grant(grant)
47
+ acl = acl_factory(grant)
48
+ if Zimbra::Directory::add_grant(self.zmobject, acl)
49
+ self.class.find(self.id)
50
+ else
51
+ fail ZimbraRestApi::NotFound, "ZimbraRestApi::NotFound Grant #{acl.grantee_name}"
52
+ end
53
+ end
54
+
55
+ def revoke_grant(grant)
56
+ acl = acl_factory(grant)
57
+ if Zimbra::Directory::revoke_grant(self.zmobject, acl)
58
+ return self.class.find(self.id)
59
+ else
60
+ fail ZimbraRestApi::NotFound, "ZimbraRestApi::NotFound Grant #{acl.grantee_name}"
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ # Doc placeholder
67
+ module ClassMethods
68
+
69
+ def all(query = {}, object = nil)
70
+ zimbra_object = get_zimbra_object(object)
71
+ search_result = search(zimbra_object, query)
72
+ return nil if search_result.nil?
73
+ raw_els = search_result[:results]
74
+ search_result[:results] = raw_els.nil? ? [] : raw_els.map do |o|
75
+ new(o)
76
+ end
77
+ search_result
78
+ end
79
+
80
+ def count(query = {}, object = nil)
81
+ query ||= {}
82
+ query[:count_only] = true
83
+ query['per_page'] = 0
84
+ zimbra_object = get_zimbra_object(object)
85
+ result = search(zimbra_object, query)
86
+ !result.nil? ? result : { count: 0 }
87
+ end
88
+
89
+ def create(params = {}, object = nil)
90
+ zimbra_object = get_zimbra_object(object)
91
+ name = params.delete('name')
92
+ result = zimbra_object.create(name, params)
93
+ new(result)
94
+ end
95
+
96
+ def find(query, object = nil)
97
+ zimbra_object = get_zimbra_object(object)
98
+ if UUID.validate(query)
99
+ result = zimbra_object.find_by_id(query)
100
+ else
101
+ result = zimbra_object.find_by_name(query)
102
+ end
103
+ result.nil? ? nil : new(result)
104
+ end
105
+
106
+ def get_zimbra_object(object)
107
+ object ||= self.is_a?(Class) ? name : self.class.name
108
+ object.gsub!(/ZimbraRestApi::/, '')
109
+ "Zimbra::#{object.camelize}".constantize
110
+ end
111
+
112
+ def search(object, query)
113
+ zimbra_type = object.name.split(/::/).last.downcase
114
+ search_hash = build_search_hash(query)
115
+ search_hash.merge!(type: zimbra_type)
116
+ query = search_hash.delete(:query)
117
+ begin
118
+ Zimbra::Directory.search(query, search_hash)
119
+ rescue Zimbra::HandsoapErrors::SOAPFault => e
120
+ msg = 'number of results exceeded the limit: too many search results returned'
121
+ raise ZimbraRestApi::TO_MANY_RESULTS if e.message == msg
122
+ end
123
+ end
124
+
125
+ def build_search_hash(query = {})
126
+ query_dup = query.clone
127
+ sort_options_hash = get_sort_ops(query_dup)
128
+ count_only = query_dup.delete('count_only') || query_dup.delete(:count_only)
129
+ query_hash = {
130
+ domain: query_dup.delete('domain') || query_dup.delete(:domain),
131
+ query: hash_to_ldap(query_dup)
132
+ }
133
+ query_hash.merge(sort_options_hash).merge(count_only: count_only)
134
+ end
135
+
136
+ def get_sort_ops(query)
137
+ page = query.delete('page') || 0
138
+ limit = query.delete('per_page') || 0
139
+ max_results = query.delete('max_results') || ZimbraRestApi.zimbra_max_results
140
+ attrs = query.delete('attrs') || nil
141
+ offset = page.to_i <= 1 ? 0 : ((page.to_i - 1) * limit.to_i)
142
+ { limit: limit.to_i,
143
+ offset: offset.to_i, attrs: attrs, max_results: max_results }
144
+ end
145
+
146
+ def hash_to_ldap(query = {})
147
+ raw_filter = query.delete('raw_ldap_filter').to_s
148
+ if query.delete('inverse_filter')
149
+ result = query.map { |k, v| "(!(#{k}=#{v}))" }.join('')
150
+ else
151
+ result = query.map { |k, v| "(#{k}=#{v})" }.join('')
152
+ end
153
+ raw_filter << "(&#{result})"
154
+ raw_filter
155
+ end
156
+
157
+ def zimbra_attrs_to_load=(array)
158
+ klass_name = self.name.split(/::/)[1]
159
+ klass = "Zimbra::#{klass_name}".constantize
160
+ fail(ArgumentError, 'Must be an array') unless array.is_a?Array
161
+ klass.zimbra_attrs_to_load = array
162
+ end
163
+
164
+ def zimbra_attrs_to_load
165
+ klass_name = self.name.split(/::/)[1]
166
+ klass = "Zimbra::#{klass_name}".constantize
167
+ return [] if klass.zimbra_attrs_to_load.nil?
168
+ klass.zimbra_attrs_to_load
169
+ end
170
+ end
171
+ end
172
+
173
+ class NotFound < StandardError; end
174
+
175
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'zimbra_rest_api/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "zimbra-rest-api"
8
+ spec.version = ZimbraRestApi::VERSION
9
+ spec.authors = ["Patricio Bruna"]
10
+ spec.license = "MIT"
11
+ spec.email = ["pbruna@itlinux.cl"]
12
+
13
+ spec.summary = 'Zimbra REST API Proxy to Zimbra SOAP API'
14
+ spec.description = 'Zimbra REST API Proxy to Zimbra SOAP API.'
15
+ spec.homepage = 'https://github.com/pbruna/zimbra-rest-api'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'sinatra', '~> 1.4'
23
+ spec.add_dependency 'uuid', '~> 2.3'
24
+ spec.add_dependency 'httpclient', '~> 2.6'
25
+ spec.add_dependency 'sinatra-contrib', '~> 1.4'
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.9"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency 'minitest-reporters', '~> 1.0', '>= 1.0.19'
30
+ spec.add_development_dependency 'pry', '~> 0.10'
31
+ end
metadata ADDED
@@ -0,0 +1,185 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zimbra-rest-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.9
5
+ platform: ruby
6
+ authors:
7
+ - Patricio Bruna
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-02-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: uuid
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '2.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '2.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: httpclient
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sinatra-contrib
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '1.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '1.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: minitest-reporters
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ - - '>='
105
+ - !ruby/object:Gem::Version
106
+ version: 1.0.19
107
+ type: :development
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ~>
112
+ - !ruby/object:Gem::Version
113
+ version: '1.0'
114
+ - - '>='
115
+ - !ruby/object:Gem::Version
116
+ version: 1.0.19
117
+ - !ruby/object:Gem::Dependency
118
+ name: pry
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ~>
122
+ - !ruby/object:Gem::Version
123
+ version: '0.10'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ~>
129
+ - !ruby/object:Gem::Version
130
+ version: '0.10'
131
+ description: Zimbra REST API Proxy to Zimbra SOAP API.
132
+ email:
133
+ - pbruna@itlinux.cl
134
+ executables: []
135
+ extensions: []
136
+ extra_rdoc_files: []
137
+ files:
138
+ - .gitignore
139
+ - Changelog.md
140
+ - Dockerfile
141
+ - Gemfile
142
+ - Gemfile.lock
143
+ - LICENSE.txt
144
+ - Rakefile
145
+ - Readme.md
146
+ - config.ru
147
+ - lib/helpers.rb
148
+ - lib/models/account.rb
149
+ - lib/models/cos.rb
150
+ - lib/models/distribution_list.rb
151
+ - lib/models/domain.rb
152
+ - lib/models/zimbra_base.rb
153
+ - lib/zimbra_rest_api.rb
154
+ - lib/zimbra_rest_api/app.rb
155
+ - lib/zimbra_rest_api/errors.rb
156
+ - lib/zimbra_rest_api/utils.rb
157
+ - lib/zimbra_rest_api/version.rb
158
+ - lib/zimbra_rest_api/zimbra_object.rb
159
+ - zimbra_rest_api.gemspec
160
+ homepage: https://github.com/pbruna/zimbra-rest-api
161
+ licenses:
162
+ - MIT
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - '>='
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - '>='
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 2.4.6
181
+ signing_key:
182
+ specification_version: 4
183
+ summary: Zimbra REST API Proxy to Zimbra SOAP API
184
+ test_files: []
185
+ has_rdoc: