satz 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +4 -0
  3. data/README.md +48 -2
  4. data/lib/satz.rb +63 -4
  5. data/satz.gemspec +1 -1
  6. data/test/all.rb +61 -4
  7. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef06f388d1b3113f7d709320b4ea4606e5fc9c8c
4
- data.tar.gz: af63d424f6e74e95418804514075f72886777af5
3
+ metadata.gz: 5ef14c47b68ac506c9096dfa78e62d7e174d3dd4
4
+ data.tar.gz: eb243d56d8184dda598263589bcbfe7e5580bb2f
5
5
  SHA512:
6
- metadata.gz: 8ef33ff001e893d12588c6fbf6f5825434c7cf8599f14db51ddf12b807e95ccb0e6731f9ff4241a80e6b587e6bec31ec827c3f4ddd919fb853eef807317deccc
7
- data.tar.gz: 4a1df71be2425e688e51ed96dafb3d0a8d1d3be929f8f5c2917e8a7788382942d4c1018b5b1e7846f3d933179c98f6eec6df42a58a24a744598b555c8675dd90
6
+ metadata.gz: a1025b3c94d78c146c7cee02a24be964ae9b4fef849855d954e3dff793fc901dcd405b175155ea8fb159227a0b9d32ab61854f969242a0e41880aa7430b15fd6
7
+ data.tar.gz: cb37e3aabca0617d18c8730f815fc3997843a1528b3eca6834c985acd41fec8009486126d4072c229948308efd24a090cb1c2721477d036a20628486bde0b934
data/CHANGELOG CHANGED
@@ -1 +1,5 @@
1
+ 0.0.2
1
2
 
3
+ * Add customizable serializer
4
+
5
+ * Add authentication via Basic Auth
data/README.md CHANGED
@@ -19,7 +19,7 @@ Usage
19
19
  An example of a Satz application would look like this:
20
20
 
21
21
  ```ruby
22
- App = Satz.new do
22
+ App = Satz.define do
23
23
  on "players" do
24
24
  on :player_id do
25
25
  get do
@@ -55,15 +55,61 @@ In user defined objects, you can define the method `to_json` according
55
55
  to your needs. Most ORMs already provide meaningful definitions for
56
56
  that method.
57
57
 
58
+ Authentication
59
+ --------------
60
+
61
+ The `auth` method checks if Basic Auth headers were provided and
62
+ returns `nil` otherwise. If it's able to access the supplied
63
+ credentials, it yield the username and password and returns the
64
+ result (if it's not false) or nil.
65
+
66
+ Here's an example of how to use it:
67
+
68
+ ```ruby
69
+ @user = auth do |user, pass|
70
+
71
+ # Here you can use any method of your
72
+ # choice. The example is from Shield.
73
+ User.authenticate(user, pass)
74
+ end
75
+
76
+ on @user.nil? do
77
+ res.status = 401
78
+ reply(error: "Unauthorized")
79
+ end
80
+ ```
81
+
82
+ Anything defined after that `on` block will be executed only if the
83
+ authentication succeded.
84
+
85
+ Serialization
86
+ -------------
87
+
88
+ The default serializer is `JSON`, but it can be customized by
89
+ supplying a serializer:
90
+
91
+ ```ruby
92
+ Satz.serializer = MySerializer
93
+ ```
94
+
95
+ A serializer must respond to `load(arg)` and `dump(arg)`, and that's
96
+ the only restriction. Note that the supplied serializer will be used
97
+ by all `Satz` applications.
98
+
58
99
  API
59
100
  ---
60
101
 
61
102
  Apart from [Syro][syro]'s API, the following methods are available:
62
103
 
63
- `read`: Reads the body of the requst and parses it as JSON.
104
+ `auth`: Process Basic Auth headers and yield username and password.
105
+
106
+ `read`: Reads the body of the request and parses it as JSON.
64
107
 
65
108
  `reply`: Writes to the response its argument encoded as JSON.
66
109
 
110
+ Installation
111
+ ------------
112
+
67
113
  ```
68
114
  $ gem install satz
69
115
  ```
data/lib/satz.rb CHANGED
@@ -22,24 +22,83 @@
22
22
 
23
23
  require "syro"
24
24
  require "json"
25
+ require "base64"
25
26
 
26
27
  class Satz
28
+
29
+ # The JSON module from stdlib provides a safe way of loading data
30
+ # with the `parse` method, but the most widespread API for encoding
31
+ # and decoding data is with the `load` and `dump` methods, which in
32
+ # the case of JSON are unsafe. This module wraps the JSON constant
33
+ # from stdlib in order to expose a safe version of `load`. As the
34
+ # serializer is configurable, the user is expected to provide
35
+ # objects that respond safely to those methods.
36
+ module Serializer
37
+ def self.load(value)
38
+ JSON.load(value, nil, create_additions: false)
39
+ end
40
+
41
+ def self.dump(value)
42
+ JSON.dump(value)
43
+ end
44
+ end
45
+
46
+ @@serializer = Serializer
47
+
48
+ def self.serializer
49
+ @@serializer
50
+ end
51
+
52
+ # Modify the serializer to be used for all Satz applications.
53
+ # The serializer object must reply to `load(arg)` and `dump(arg)`.
54
+ def self.serializer=(value)
55
+ @@serializer = value
56
+ end
57
+
27
58
  class Deck < Syro::Deck
59
+ HTTP_AUTHORIZATION = "HTTP_AUTHORIZATION".freeze
60
+
61
+ # Checks the Basic Auth headers and yields
62
+ # user and pass if credentials are provided.
63
+ # Returns nil if there are no credentials or
64
+ # if the block returns false or nil.
65
+ #
66
+ # Usage:
67
+ #
68
+ # @user = auth do |user, pass|
69
+ # User.authenticate(user, pass)
70
+ # end
71
+ #
72
+ # on @user.nil? do
73
+ # res.status = 401
74
+ # reply(error: "Unauthorized")
75
+ # end
76
+ #
77
+ def auth
78
+ http_auth = env.fetch(HTTP_AUTHORIZATION) do
79
+ return nil
80
+ end
81
+
82
+ cred = http_auth.split(" ")[1]
83
+ user, pass = Base64.decode64(cred).split(":")
84
+
85
+ yield(user, pass) || nil
86
+ end
28
87
 
29
- # Respond by default with JSON.
88
+ # Respond by default with JSON. The default charset
89
+ # for "application/json" is UTF-8.
30
90
  def default_headers
31
91
  { "Content-Type" => "application/json" }
32
92
  end
33
93
 
34
94
  # Read JSON data from the POST request.
35
95
  def read
36
- body = req.body.read
37
- body && JSON.parse(body)
96
+ Satz.serializer.load(req.body.read)
38
97
  end
39
98
 
40
99
  # Write JSON data to the response.
41
100
  def reply(data)
42
- res.write(JSON.dump(data))
101
+ res.write(Satz.serializer.dump(data))
43
102
  end
44
103
  end
45
104
 
data/satz.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "satz"
3
- s.version = "0.0.1"
3
+ s.version = "0.0.2"
4
4
  s.summary = "Framework for JSON microservices"
5
5
  s.description = "Framework for JSON microservices"
6
6
  s.authors = ["Michel Martens"]
data/test/all.rb CHANGED
@@ -1,22 +1,37 @@
1
- App = Satz.define do
1
+ A1 = Satz.define do
2
2
  get do
3
- reply(true)
3
+ reply(read)
4
4
  end
5
5
 
6
6
  post do
7
7
  reply(read)
8
8
  end
9
+
10
+ on "protected" do
11
+ @user = auth do |user, pass|
12
+ user == "foo" && pass == "bar"
13
+ end
14
+
15
+ on @user.nil? do
16
+ res.status = 401
17
+ reply(error: "Unauthorized")
18
+ end
19
+
20
+ get do
21
+ reply(true)
22
+ end
23
+ end
9
24
  end
10
25
 
11
26
  setup do
12
- Driver.new(App)
27
+ Driver.new(A1)
13
28
  end
14
29
 
15
30
  test "get" do |app|
16
31
  app.get("/")
17
32
 
18
33
  assert_equal 200, app.last_response.status
19
- assert_equal %Q(true), app.last_response.body
34
+ assert_equal %Q(null), app.last_response.body
20
35
  end
21
36
 
22
37
  test "post" do |app|
@@ -27,3 +42,45 @@ test "post" do |app|
27
42
  assert_equal data, app.last_response.body
28
43
  assert_equal 200, app.last_response.status
29
44
  end
45
+
46
+ test "auth" do |app|
47
+ app.get("/protected")
48
+
49
+ assert_equal %Q({"error":"Unauthorized"}), app.last_response.body
50
+ assert_equal 401, app.last_response.status
51
+
52
+ app.authorize("foo", "bar")
53
+
54
+ app.get("/protected")
55
+
56
+ assert_equal %Q(true), app.last_response.body
57
+ end
58
+
59
+ module Reverser
60
+ def self.load(data)
61
+ data
62
+ end
63
+
64
+ def self.dump(data)
65
+ data.reverse
66
+ end
67
+ end
68
+
69
+ Satz.serializer = Reverser
70
+
71
+ A2 = Satz.define do
72
+ get do
73
+ reply("hello")
74
+ end
75
+ end
76
+
77
+ setup do
78
+ Driver.new(A2)
79
+ end
80
+
81
+ test "get" do |app|
82
+ app.get("/")
83
+
84
+ assert_equal 200, app.last_response.status
85
+ assert_equal %Q(olleh), app.last_response.body
86
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: satz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michel Martens
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-11 00:00:00.000000000 Z
11
+ date: 2016-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: syro