algoliasearch 1.2.4 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f84365c51f1592ac0bd372b32ef668eae0831ec
4
- data.tar.gz: 315a6fbb785510696b8a9d1717de2cf09e208fd2
3
+ metadata.gz: ffcea70e1e16052463f966167245f0d0412bb1d1
4
+ data.tar.gz: a37adf050adb92682c8ddd48990b093606ba800d
5
5
  SHA512:
6
- metadata.gz: 3e4bf583e3651c8b02c76e8bc6a50c038c54ab40766bc14ad1ca8ad6f8608e90984827aa4f8a5474d956af1ac4ebf9e5ff48e93696132fd12bb229769ad193e3
7
- data.tar.gz: 6adcdf51f0a2702b5a7adbdebae0387a6d05a2c6957a44cdb388e37c0650aba41df48223ab110dccbaf13b7b53f39f16a5b211b5bbcc991e9d57e1303b7fb49d
6
+ metadata.gz: fc1fbead2ee31e7e503b7864d7779c9455d99f1925de7f5ca765c0a0229dc0c4b81680a9b814c029a0b06152b8729565dae1f8ecb342a07c46f650771ec4df07
7
+ data.tar.gz: 79a73010bbaa3b99227c9693ea9f71ba9c6627b02faf4dd4a9f772bef98b0cb9af637e67e65049fa797d056bd22cafa6415b6a28db23164a1810c1e7a3a3288f
data/ChangeLog CHANGED
@@ -1,5 +1,10 @@
1
1
  CHANGELOG
2
2
 
3
+ 2014-02-24 1.2.5
4
+
5
+ * Ability to generate secured API key from a list of tags + optional user_token
6
+ * Ability to specify a list of indexes targeted by the user key
7
+
3
8
  2014-02-21 1.2.4
4
9
 
5
10
  * Added delete_objects
data/README.md CHANGED
@@ -295,6 +295,8 @@ The server response will look like:
295
295
 
296
296
 
297
297
 
298
+
299
+
298
300
  Get an object
299
301
  -------------
300
302
 
@@ -494,13 +496,18 @@ You can also create an API Key with advanced restrictions:
494
496
  Note: If you are sending the query through your servers, you must use the `Algolia.with_rate_limits("EndUserIP", "APIKeyWithRateLimit") do ... end` block to enable rate-limit.
495
497
 
496
498
  * Specify the maximum number of hits this API key can retrieve in one call. Defaults to 0 (unlimited). This parameter can be used to protect you from attempts at retrieving your entire content by massively querying the index.
499
+ * Specify the list of targeted indexes. Defaults to all indexes if empty of blank.
497
500
 
498
501
  ```ruby
499
502
  # Creates a new global API key that is valid for 300 seconds
500
503
  res = Algolia.add_user_key(["search"], 300)
501
504
  puts res['key']
502
- # Creates a new index specific API key valid for 300 seconds, with a rate limit of 100 calls per hour per IP and a maximum of 20 hits
503
- res = index.add_user_key(["search"], 300, 100, 20)
505
+ # Creates a new index specific API key:
506
+ # - valid for 300 seconds
507
+ # - rate limit of 100 calls per hour per IP
508
+ # - maximum of 20 hits
509
+ # - valid on 'my_index1' and 'my_index2'
510
+ res = index.add_user_key(["search"], 300, 100, 20, ['my_index1', 'my_index2'])
504
511
  puts res['key']
505
512
  ```
506
513
 
@@ -520,6 +527,49 @@ Algolia.delete_user_key("f420238212c54dcfad07ea0aa6d5c45f")
520
527
  index.delete_user_key("71671c38001bf3ac857bc82052485107")
521
528
  ```
522
529
 
530
+ You may have a single index containing per-user data. In that case, all records should be tagged with their associated user_id in order to add a `tagFilters=(public,user_42)` filter at query time to retrieve only what a user has access to. If you're using the [JavaScript client](http://github.com/algolia/algoliasearch-client-js), it will result in a security breach since the user is able to modify the `tagFilters` you've set modifying the code from the browser. To keep using the JavaScript client (recommended for optimal latency) and target secured records, you can generate secured API key from your backend:
531
+
532
+ ```ruby
533
+ # generate a public API key for user 42. Here, records are tagged with:
534
+ # - 'public' if they are visible by all users
535
+ # - 'user_XXXX' if they are visible by user XXXX
536
+ public_key = Algolia.generate_secured_api_key 'YourSearchOnlyApiKey', '(public,user_42)'
537
+ ```
538
+
539
+ This public API key must then be used in your JavaScript code as follow:
540
+
541
+ ```javascript
542
+ <script type="text/javascript">
543
+ var algolia = new AlgoliaSearch('YourApplicationID', '<%= public_api_key %>');
544
+ algolia.setSecurityTags('(public,user_42)'); // must be same than those used at generation-time
545
+ algolia.initIndex('YourIndex').search($('#q').val(), function(success, content) {
546
+ // [...]
547
+ });
548
+ </script>
549
+ ```
550
+
551
+ You can mix rate limits and secured API keys setting an extra `user_token` attribute both at API key generation-time and query-time. When set, a uniq user will be identified by her `IP + user_token` instead of only her `IP`. It allows you to restrict a single user to perform maximum `N` API calls per hour, even if she share her `IP` with another user.
552
+
553
+ ```ruby
554
+ # generate a public API key for user 42. Here, records are tagged with:
555
+ # - 'public' if they are visible by all users
556
+ # - 'user_XXXX' if they are visible by user XXXX
557
+ public_key = Algolia.generate_secured_api_key 'YourRateLimitedApiKey', '(public,user_42)', 'user_42'
558
+ ```
559
+
560
+ This public API key must then be used in your JavaScript code as follow:
561
+
562
+ ```javascript
563
+ <script type="text/javascript">
564
+ var algolia = new AlgoliaSearch('YourApplicationID', '<%= public_api_key %>');
565
+ algolia.setSecurityTags('(public,user_42)'); // must be same than those used at generation-time
566
+ algolia.setUserToken('user_42') // must be same than the one used at generation-time
567
+ algolia.initIndex('YourIndex').search($('#q').val(), function(success, content) {
568
+ // [...]
569
+ });
570
+ </script>
571
+ ```
572
+
523
573
  Copy or rename an index
524
574
  -------------
525
575
 
@@ -6,12 +6,12 @@
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "algoliasearch"
9
- s.version = "1.2.4"
9
+ s.version = "1.2.5"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Algolia"]
14
- s.date = "2014-02-21"
14
+ s.date = "2014-02-24"
15
15
  s.description = "A simple Ruby client for the algolia.com REST API"
16
16
  s.email = "contact@algolia.com"
17
17
  s.extra_rdoc_files = [
@@ -148,6 +148,22 @@ module Algolia
148
148
  end
149
149
  end
150
150
 
151
+ #
152
+ # Generate a secured and public API Key from a list of tagFilters and an
153
+ # optional user token identifying the current user
154
+ #
155
+ # @param private_api_key your private API Key
156
+ # @param tag_filters the list of tags applied to the query (used as security)
157
+ # @param user_token an optional token identifying the current user
158
+ #
159
+ def Algolia.generate_secured_api_key(private_api_key, tag_filters, user_token = nil)
160
+ if tag_filters.is_a?(Array)
161
+ tag_filters = tag_filters.map { |t| t.is_a?(Array) ? "(#{t.join(',')})" : t }.join(',')
162
+ end
163
+ raise ArgumentError.new('Attribute "tag_filters" must be a list of tags') if !tag_filters.is_a?(String)
164
+ Digest::SHA256.hexdigest "#{private_api_key}#{tag_filters}#{user_token.to_s}"
165
+ end
166
+
151
167
  #
152
168
  # List all existing indexes
153
169
  # return an Answer object with answer in the form
@@ -213,9 +229,17 @@ module Algolia
213
229
  # @param validity the number of seconds after which the key will be automatically removed (0 means no time limit for this key)
214
230
  # @param maxQueriesPerIPPerHour the maximum number of API calls allowed from an IP address per hour (0 means unlimited)
215
231
  # @param maxHitsPerQuery the maximum number of hits this API key can retrieve in one call (0 means unlimited)
232
+ # @param indexes the optional list of targeted indexes
216
233
  #
217
- def Algolia.add_user_key(acls, validity = 0, maxQueriesPerIPPerHour = 0, maxHitsPerQuery = 0)
218
- Algolia.client.post(Protocol.keys_uri, {"acl" => acls, "validity" => validity.to_i, "maxQueriesPerIPPerHour" => maxQueriesPerIPPerHour.to_i, "maxHitsPerQuery" => maxHitsPerQuery.to_i}.to_json)
234
+ def Algolia.add_user_key(acls, validity = 0, maxQueriesPerIPPerHour = 0, maxHitsPerQuery = 0, indexes = nil)
235
+ params = {
236
+ :acl => acls,
237
+ :validity => validity.to_i,
238
+ :maxQueriesPerIPPerHour => maxQueriesPerIPPerHour.to_i,
239
+ :maxHitsPerQuery => maxHitsPerQuery.to_i
240
+ }
241
+ params[:indexes] = indexes if indexes
242
+ Algolia.client.post(Protocol.keys_uri, params.to_json)
219
243
  end
220
244
 
221
245
  # Delete an existing user key
@@ -1,3 +1,3 @@
1
1
  module Algolia
2
- VERSION = "1.2.3"
2
+ VERSION = "1.2.5"
3
3
  end
@@ -214,6 +214,9 @@ describe 'Client' do
214
214
  end
215
215
 
216
216
  it "should get logs" do
217
+ host = Algolia.client.send(:thread_local_hosts).first
218
+ puts host[:session].get(host[:base_url] + Algolia::Protocol.logs(0, 10), { :header => Algolia.client.headers }).content
219
+
217
220
  res = Algolia.get_logs
218
221
 
219
222
  res['logs'].size.should > 0
@@ -561,4 +564,15 @@ describe 'Client' do
561
564
  logs['logs'][0].should have_key('sha1')
562
565
  logs['logs'][0]['sha1'].should be_a(String)
563
566
  end
567
+
568
+ it 'should generate secured api keys' do
569
+ key = Algolia.generate_secured_api_key('my_api_key', '(public,user1)')
570
+ key.should eq(Digest::SHA256.hexdigest 'my_api_key(public,user1)')
571
+ key = Algolia.generate_secured_api_key('my_api_key', '(public,user1)', 42)
572
+ key.should eq(Digest::SHA256.hexdigest 'my_api_key(public,user1)42')
573
+ key = Algolia.generate_secured_api_key('my_api_key', ['public'])
574
+ key.should eq(Digest::SHA256.hexdigest 'my_api_keypublic')
575
+ key = Algolia.generate_secured_api_key('my_api_key', ['public', ['premium','vip']])
576
+ key.should eq(Digest::SHA256.hexdigest 'my_api_keypublic,(premium,vip)')
577
+ end
564
578
  end
metadata CHANGED
@@ -1,85 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: algoliasearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 1.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Algolia
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-21 00:00:00.000000000 Z
11
+ date: 2014-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httpclient
15
- requirement: !ruby/object:Gem::Requirement
15
+ version_requirements: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '2.3'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
20
+ requirement: !ruby/object:Gem::Requirement
23
21
  requirements:
24
22
  - - ~>
25
23
  - !ruby/object:Gem::Version
26
24
  version: '2.3'
25
+ prerelease: false
26
+ type: :runtime
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: json
29
- requirement: !ruby/object:Gem::Requirement
29
+ version_requirements: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: 1.5.1
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
34
+ requirement: !ruby/object:Gem::Requirement
37
35
  requirements:
38
36
  - - '>='
39
37
  - !ruby/object:Gem::Version
40
38
  version: 1.5.1
39
+ prerelease: false
40
+ type: :runtime
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: travis
43
- requirement: !ruby/object:Gem::Requirement
43
+ version_requirements: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
51
49
  requirements:
52
50
  - - '>='
53
51
  - !ruby/object:Gem::Version
54
52
  version: '0'
53
+ prerelease: false
54
+ type: :development
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
- requirement: !ruby/object:Gem::Requirement
57
+ version_requirements: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
62
+ requirement: !ruby/object:Gem::Requirement
65
63
  requirements:
66
64
  - - '>='
67
65
  - !ruby/object:Gem::Version
68
66
  version: '0'
67
+ prerelease: false
68
+ type: :development
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rdoc
71
- requirement: !ruby/object:Gem::Requirement
71
+ version_requirements: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - '>='
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
76
+ requirement: !ruby/object:Gem::Requirement
79
77
  requirements:
80
78
  - - '>='
81
79
  - !ruby/object:Gem::Version
82
80
  version: '0'
81
+ prerelease: false
82
+ type: :development
83
83
  description: A simple Ruby client for the algolia.com REST API
84
84
  email: contact@algolia.com
85
85
  executables: []
@@ -115,7 +115,7 @@ homepage: http://github.com/algolia/algoliasearch-client-ruby
115
115
  licenses:
116
116
  - MIT
117
117
  metadata: {}
118
- post_install_message:
118
+ post_install_message:
119
119
  rdoc_options: []
120
120
  require_paths:
121
121
  - lib
@@ -130,9 +130,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
130
  - !ruby/object:Gem::Version
131
131
  version: '0'
132
132
  requirements: []
133
- rubyforge_project:
134
- rubygems_version: 2.1.11
135
- signing_key:
133
+ rubyforge_project:
134
+ rubygems_version: 2.2.1
135
+ signing_key:
136
136
  specification_version: 4
137
137
  summary: A simple Ruby client for the algolia.com REST API
138
138
  test_files: []