cloudant 0.1.1 → 0.1.2
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 +4 -4
- data/README.md +18 -2
- data/lib/cloudant.rb +2 -0
- data/lib/cloudant/api.rb +6 -0
- data/lib/cloudant/client.rb +6 -5
- data/lib/cloudant/query_builder.rb +25 -27
- data/lib/cloudant/security.rb +89 -0
- data/lib/cloudant/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 323e21dbdef047f55e8659ab87b86849773f33bd
|
4
|
+
data.tar.gz: de9591fe10e75a28b093a6650ab3e82f2c6c854f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7965e9c118e21a7dc576b6bdee230ef47503b76fe434eb9b90068f8c9aa00bb756fbc6828874a5c507ade1ca86f276852a00aebf990c105f55248b7c4dc8857
|
7
|
+
data.tar.gz: 8306676a225c5d36ffff93a7c230d80f78f2425fe2e1f2de80c09908b3c7aa7aec32459f0dd33714827e1ee2c3814f64392b35ad77d21a876b5fa753f1b4a8ec
|
data/README.md
CHANGED
@@ -75,7 +75,9 @@ response = client.create(doc)
|
|
75
75
|
# Find a single document by id
|
76
76
|
doc_id = response['id']
|
77
77
|
client.doc(doc_id)
|
78
|
-
|
78
|
+
# Options can also be passed along with the id (View Cloudant docs for a full list)
|
79
|
+
client.doc(doc_id, :revs => true))
|
80
|
+
|
79
81
|
# Update a single document (requires an existing doc's id and current rev)
|
80
82
|
new_doc = {'id'=>'1', 'rev'=>'1-bf74a68c2105f3d9d0245fc836ca9f3', 'field_two'=>'more content'}
|
81
83
|
client.update(new_doc)
|
@@ -146,10 +148,24 @@ client.view(database,view_to_query, :reduce => false, :include_docs => true)
|
|
146
148
|
# => {"total_rows"=>2, "offset"=>0, "rows"=>[{"id"=>"5d8e6c99198dfdde8accd8e019ba052", "key"=>"5d8e6c99198dfdde8accd8e019ba052", "value"=>1, "doc"=>{"_id"=>"5d8e6c99198dfdde8accd8e019ba052", "_rev"=>"1-7ebdb5b82e1cc4eaf2e27a711e9857c6", "a"=>10, "b"=>92, "c"=>31}}, {"id"=>"5d8e6c99898dcdd08accd8e019badab", "key"=>"5d8e6c99898dcdd0daccd8e019badab", "value"=>1, "doc"=>{"_id"=>"5d8e6c99898dcdd8daccd8e019badab", "_rev"=>"1-d36298f4391da575df61e170af2efa34", "b"=>12, "c"=>33}}]}
|
147
149
|
```
|
148
150
|
|
151
|
+
**Database Security and Authorization**
|
152
|
+
```ruby
|
153
|
+
# View permissions for the current user
|
154
|
+
client.permissions
|
155
|
+
|
156
|
+
# View all users and their permissions for a given database
|
157
|
+
client.roles
|
158
|
+
|
159
|
+
# Create a new set of API keys
|
160
|
+
client.create_api_keys
|
161
|
+
|
162
|
+
# Create a new user and assign it one or more permissions
|
163
|
+
client.new_user(["reader", "writer"])
|
164
|
+
```
|
165
|
+
|
149
166
|
## To Do
|
150
167
|
|
151
168
|
- Add database replication functionality - `/_replicator`
|
152
|
-
- Allow new user creation - `/_security`
|
153
169
|
- Add more robust options handling for various queries (expanding the `QueryBuilder` module, as used in view querying)
|
154
170
|
- Currently, options have to be added to a query string by the user.
|
155
171
|
- Add support for `attachments`
|
data/lib/cloudant.rb
CHANGED
data/lib/cloudant/api.rb
ADDED
data/lib/cloudant/client.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module Cloudant
|
2
2
|
class Client
|
3
|
-
|
3
|
+
include Cloudant::API
|
4
4
|
attr_accessor :database, :base_uri
|
5
|
+
attr_reader :username, :password
|
5
6
|
|
6
7
|
def initialize(args)
|
7
8
|
@username = args[:username]
|
@@ -16,7 +17,7 @@ module Cloudant
|
|
16
17
|
# Retrieve all docs from the database
|
17
18
|
def all_docs(*opts)
|
18
19
|
q = "#{database}/_all_docs"
|
19
|
-
q <<
|
20
|
+
q << build_query_string(opts.first,"all_docs") if opts && opts.any? && opts.first.is_a?(Hash)
|
20
21
|
|
21
22
|
@conn.query({url_path: q, method: :get})
|
22
23
|
end
|
@@ -24,7 +25,7 @@ module Cloudant
|
|
24
25
|
# Accepts a single document id and returns it if found
|
25
26
|
def get_doc(id,*opts)
|
26
27
|
q = "#{database}/#{id}"
|
27
|
-
q <<
|
28
|
+
q << build_query_string(opts.first,"doc") if opts && opts.any? && opts.first.is_a?(Hash)
|
28
29
|
|
29
30
|
@conn.query({url_path: q, method: :get})
|
30
31
|
end
|
@@ -94,7 +95,7 @@ module Cloudant
|
|
94
95
|
# Get a hash {"results" => []}, containing a hash of seq, id, changes
|
95
96
|
def changes(*opts)
|
96
97
|
q = "#{database}/_changes"
|
97
|
-
q <<
|
98
|
+
q << build_query_string(opts.first,"changes") if opts && opts.any? && opts.first.is_a?(Hash)
|
98
99
|
|
99
100
|
@conn.query({url_path: q, method: :get})
|
100
101
|
end
|
@@ -163,7 +164,7 @@ module Cloudant
|
|
163
164
|
# if it has one, or rows containing keys, ids, and values if not.
|
164
165
|
def view(ddoc,view,*opts)
|
165
166
|
q = "#{database}/_design/#{ddoc}/_view/#{view}"
|
166
|
-
q <<
|
167
|
+
q << build_query_string(opts.first,"view") if opts && opts.any? && opts.first.is_a?(Hash)
|
167
168
|
|
168
169
|
@conn.query({url_path: q, method: :get})
|
169
170
|
end
|
@@ -1,36 +1,34 @@
|
|
1
1
|
module Cloudant
|
2
2
|
module QueryBuilder
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
fields = get_fields(type)
|
3
|
+
# TODO: Add a check to determine if options value is valid for key.
|
4
|
+
# ex: {include_docs: true} is valid, {include_docs: 6} is not.
|
5
|
+
def build_query_string(opts,type)
|
6
|
+
query_str = ""
|
7
|
+
fields = get_fields(type)
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
fields.each do |field|
|
10
|
+
key = field
|
11
|
+
val = opts[field].to_s
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
query_str
|
13
|
+
current = "#{key}=#{val}" if val != ""
|
14
|
+
query_str << "&" << current if (query_str != "" && current)
|
15
|
+
query_str << "?" << current if (query_str == "" && current)
|
20
16
|
end
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
18
|
+
query_str
|
19
|
+
end
|
20
|
+
|
21
|
+
# TODO: This will be expanded to calls other than /_view.
|
22
|
+
def get_fields(type)
|
23
|
+
case type
|
24
|
+
when "view"
|
25
|
+
return [:reduce,:include_docs,:descending,:endkey,:endkey_docid,:group,:group_level,:inclusive_end,:key,:keys,:limit,:skip,:stale,:startkey,:startkey_docid]
|
26
|
+
when "all_docs"
|
27
|
+
return [:include_docs,:descending,:endkey,:conflicts,:inclusive_end,:key,:limit,:skip,:startkey,:keys]
|
28
|
+
when "changes"
|
29
|
+
return [:include_docs,:descending,:feed,:filter,:heartbeat,:conflicts,:limit,:since,:style,:timeout,:doc_ids]
|
30
|
+
when "doc"
|
31
|
+
return [:local_seq,:attachments,:att_encoding_info,:atts_since,:conflicts,:deleted_conflicts,:latest,:meta,:open_revs,:rev,:revs,:revs_info]
|
34
32
|
end
|
35
33
|
end
|
36
34
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Cloudant
|
2
|
+
module Security
|
3
|
+
# The Security Module contains methods to read and modify existing users,
|
4
|
+
# permissions, and credentials.
|
5
|
+
# The default credentials provided upon account creaton have _admin level
|
6
|
+
# access to all account databases; any subsequent users or API keys created
|
7
|
+
# must have permissions explicitly set.
|
8
|
+
#
|
9
|
+
# View permissions for the current user
|
10
|
+
# Can only be accessed after performing cookie auth
|
11
|
+
def permissions
|
12
|
+
@conn.query({url_path: "_session", method: :get})
|
13
|
+
end
|
14
|
+
|
15
|
+
# View existing user permissions in the database
|
16
|
+
# Returns {"cloudant" => {"key" => ["_permission"]}}
|
17
|
+
def roles
|
18
|
+
@conn.query({url_path: "_api/v2/db/#{database}/_security", method: :get})
|
19
|
+
end
|
20
|
+
|
21
|
+
# Grant or revoke permissions
|
22
|
+
# Accepts a document: {"cloudant" => {"key" => ["_permission"]}}
|
23
|
+
def update_roles(doc)
|
24
|
+
@conn.query({url_path: "_api/v2/db/#{database}/_security", opts: doc, method: :put})
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns {"password" => "str", "key" => "str", "ok" => true}
|
28
|
+
def create_api_keys
|
29
|
+
@conn.query({url_path: "_api/v2/api_keys", method: :post})
|
30
|
+
end
|
31
|
+
|
32
|
+
# Methd to create and authorize a new set of credentials.
|
33
|
+
# :new_user accepts and array of either symbols or hashes, corresponding to the roles
|
34
|
+
# available in Cloudant as see in all_roles below.
|
35
|
+
# Returns the credentials and roles {"password" => "str", "key" => "str", "ok" => true, "roles": []}
|
36
|
+
def new_user(user_roles)
|
37
|
+
checked = Security.check_roles(user_roles)
|
38
|
+
|
39
|
+
if checked
|
40
|
+
users = roles
|
41
|
+
keys = create_api_keys
|
42
|
+
|
43
|
+
existing_users = users["cloudant"]
|
44
|
+
users["cloudant"] = {} unless existing_users # If no users exist a blank has is returned instead of {"cloudant": {}}
|
45
|
+
|
46
|
+
users["cloudant"][keys["key"]] = checked
|
47
|
+
keys["roles"] = checked
|
48
|
+
|
49
|
+
update_roles(users)
|
50
|
+
else
|
51
|
+
raise ArgumentError.new('invalid - permitted roles: reader, writer, admin, replicator, db_updates, design, shards, security')
|
52
|
+
end
|
53
|
+
|
54
|
+
keys
|
55
|
+
end
|
56
|
+
|
57
|
+
# Accepts a string - a key with permissions already existing in the database
|
58
|
+
# If the key isn't found within the database, no changes are made.
|
59
|
+
def delete_user(user)
|
60
|
+
users = roles
|
61
|
+
existing = users["cloudant"]
|
62
|
+
|
63
|
+
existing.delete(user) if existing
|
64
|
+
update_roles(users)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Checks input array to make sure it contains only valid roles.
|
68
|
+
# Any invalid roles will be removed. If there are a mix of valid and invalid
|
69
|
+
# roles in the array, the new user will be created with only the valid roles.
|
70
|
+
# If the input is empty, or no valid roles are present, no user will be created.
|
71
|
+
def self.check_roles(roles)
|
72
|
+
all_roles = ["_reader","_writer","_admin","_replicator","_db_updates","_design","_shards","_security"]
|
73
|
+
validated = []
|
74
|
+
|
75
|
+
if roles && roles.is_a?(Array)
|
76
|
+
roles.each do |role|
|
77
|
+
role_str = role.to_s
|
78
|
+
role_str = role_str[1..-1] if role_str[0] == "_"
|
79
|
+
role_str = "_#{role_str}"
|
80
|
+
|
81
|
+
validated << role_str if all_roles.include?(role_str)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
validated = nil if validated.empty?
|
86
|
+
validated
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/cloudant/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Yanai
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -112,9 +112,11 @@ files:
|
|
112
112
|
- bin/setup
|
113
113
|
- cloudant.gemspec
|
114
114
|
- lib/cloudant.rb
|
115
|
+
- lib/cloudant/api.rb
|
115
116
|
- lib/cloudant/client.rb
|
116
117
|
- lib/cloudant/connection.rb
|
117
118
|
- lib/cloudant/query_builder.rb
|
119
|
+
- lib/cloudant/security.rb
|
118
120
|
- lib/cloudant/version.rb
|
119
121
|
homepage: https://github.com/AlexYanai/cloudant
|
120
122
|
licenses:
|