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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 617903591a37ae1748f18b5eae04fa09b888e1c9
4
- data.tar.gz: f63d86f9d26940307786fcd17cb4bdf55c0c3ac5
3
+ metadata.gz: 323e21dbdef047f55e8659ab87b86849773f33bd
4
+ data.tar.gz: de9591fe10e75a28b093a6650ab3e82f2c6c854f
5
5
  SHA512:
6
- metadata.gz: 24f9c643c868ec3cbc735640bc226b8ff92e5325bad4093c7c71b05743faa5f6be9100533841890fe7ddc9244b9261d90b322b015d69b1c2385bffae32edc844
7
- data.tar.gz: d73ed0447c65737c12f60c0793edc85eff2c77185e917196c126be47ba3b23a6b8203092b6512ef35dd7d8fc94e6189eac129a9234c1b772c98b63f31c349694
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`
@@ -5,5 +5,7 @@ require 'rest-client'
5
5
 
6
6
  require "cloudant/version"
7
7
  require "cloudant/query_builder"
8
+ require "cloudant/security"
9
+ require "cloudant/api"
8
10
  require "cloudant/client"
9
11
  require "cloudant/connection"
@@ -0,0 +1,6 @@
1
+ module Cloudant
2
+ module API
3
+ include Cloudant::QueryBuilder
4
+ include Cloudant::Security
5
+ end
6
+ end
@@ -1,7 +1,8 @@
1
1
  module Cloudant
2
2
  class Client
3
- attr_reader :username, :password
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 << QueryBuilder.build(opts.first,"all_docs") if opts && opts.any? && opts.first.is_a?(Hash)
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 << QueryBuilder.build(opts.first,"doc") if opts && opts.any? && opts.first.is_a?(Hash)
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 << QueryBuilder.build(opts.first,"changes") if opts && opts.any? && opts.first.is_a?(Hash)
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 << QueryBuilder.build(opts.first,"view") if opts && opts.any? && opts.first.is_a?(Hash)
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
- class << self
4
- # TODO: Add a check to determine if options value is valid for key.
5
- # ex: {include_docs: true} is valid, {include_docs: 6} is not.
6
- def build(opts,type)
7
- query_str = ""
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
- fields.each do |field|
11
- key = field
12
- val = opts[field].to_s
9
+ fields.each do |field|
10
+ key = field
11
+ val = opts[field].to_s
13
12
 
14
- current = "#{key}=#{val}" if val != ""
15
- query_str << "&" << current if (query_str != "" && current)
16
- query_str << "?" << current if (query_str == "" && current)
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
- # TODO: This will be expanded to calls other than /_view.
23
- def get_fields(type)
24
- case type
25
- when "view"
26
- return [:reduce,:include_docs,:descending,:endkey,:endkey_docid,:group,:group_level,:inclusive_end,:key,:keys,:limit,:skip,:stale,:startkey,:startkey_docid]
27
- when "all_docs"
28
- return [:include_docs,:descending,:endkey,:conflicts,:inclusive_end,:key,:limit,:skip,:startkey,:keys]
29
- when "changes"
30
- return [:include_docs,:descending,:feed,:filter,:heartbeat,:conflicts,:limit,:since,:style,:timeout,:doc_ids]
31
- when "doc"
32
- return [:local_seq,:attachments,:att_encoding_info,:atts_since,:conflicts,:deleted_conflicts,:latest,:meta,:open_revs,:rev,:revs,:revs_info]
33
- end
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
@@ -1,3 +1,3 @@
1
1
  module Cloudant
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
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.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-15 00:00:00.000000000 Z
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: