zimbra-rest-api 0.1.9

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