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