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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Changelog.md +46 -0
- data/Dockerfile +25 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +9 -0
- data/Readme.md +51 -0
- data/config.ru +22 -0
- data/lib/helpers.rb +97 -0
- data/lib/models/account.rb +58 -0
- data/lib/models/cos.rb +5 -0
- data/lib/models/distribution_list.rb +33 -0
- data/lib/models/domain.rb +19 -0
- data/lib/models/zimbra_base.rb +33 -0
- data/lib/zimbra_rest_api.rb +42 -0
- data/lib/zimbra_rest_api/app.rb +207 -0
- data/lib/zimbra_rest_api/errors.rb +5 -0
- data/lib/zimbra_rest_api/utils.rb +20 -0
- data/lib/zimbra_rest_api/version.rb +3 -0
- data/lib/zimbra_rest_api/zimbra_object.rb +175 -0
- data/zimbra_rest_api.gemspec +31 -0
- metadata +185 -0
checksums.yaml
ADDED
@@ -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
|
data/Changelog.md
ADDED
@@ -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
|
+
```
|
data/Dockerfile
ADDED
@@ -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
data/LICENSE.txt
ADDED
@@ -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.
|
data/Rakefile
ADDED
data/Readme.md
ADDED
@@ -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
|
data/config.ru
ADDED
@@ -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
|
data/lib/helpers.rb
ADDED
@@ -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
|
data/lib/models/cos.rb
ADDED
@@ -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,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,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:
|