ctws 0.1.5.alpha
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +295 -0
- data/Rakefile +21 -0
- data/app/auth/ctws/authenticate_user.rb +36 -0
- data/app/auth/ctws/authorize_api_request.rb +44 -0
- data/app/controllers/concerns/ctws/exception_handler.rb +58 -0
- data/app/controllers/concerns/ctws/response.rb +25 -0
- data/app/controllers/concerns/ctws/v1/exception_handler.rb +6 -0
- data/app/controllers/concerns/ctws/v1/response.rb +6 -0
- data/app/controllers/ctws/application_controller.rb +5 -0
- data/app/controllers/ctws/authentication_controller.rb +28 -0
- data/app/controllers/ctws/ctws_controller.rb +24 -0
- data/app/controllers/ctws/min_app_versions_controller.rb +55 -0
- data/app/controllers/ctws/users_controller.rb +39 -0
- data/app/controllers/ctws/v1/authentication_controller.rb +6 -0
- data/app/controllers/ctws/v1/ctws_controller.rb +9 -0
- data/app/controllers/ctws/v1/min_app_versions_controller.rb +6 -0
- data/app/controllers/ctws/v1/users_controller.rb +6 -0
- data/app/controllers/ctws/v2/ctws_controller.rb +9 -0
- data/app/controllers/ctws/v2/min_app_versions_controller.rb +6 -0
- data/app/jobs/ctws/application_job.rb +4 -0
- data/app/lib/ctws/json_web_token.rb +25 -0
- data/app/lib/ctws/message.rb +43 -0
- data/app/mailers/ctws/application_mailer.rb +6 -0
- data/app/models/ctws/application_record.rb +5 -0
- data/app/models/ctws/min_app_version.rb +22 -0
- data/config/initializers/ctws.rb +5 -0
- data/config/initializers/mime_types.rb +1 -0
- data/config/routes.rb +18 -0
- data/db/migrate/20170425110839_create_ctws_min_app_versions.rb +13 -0
- data/db/migrate/20170425110933_add_values_to_ctws_min_app_versions.rb +26 -0
- data/lib/ctws/engine.rb +13 -0
- data/lib/ctws/version.rb +3 -0
- data/lib/ctws.rb +18 -0
- data/lib/tasks/ctws_tasks.rake +4 -0
- data/spec/auth/ctws/authenticate_user_spec.rb +34 -0
- data/spec/auth/ctws/authorize_api_request_spec.rb +62 -0
- data/spec/controllers/ctws/ctws_controller_spec.rb +41 -0
- data/spec/controllers/ctws/users_controller_spec.rb +7 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +5 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/javascripts/cable.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/user_controller.rb +18 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/helpers/user_helper.rb +2 -0
- data/spec/dummy/app/jobs/application_job.rb +2 -0
- data/spec/dummy/app/mailers/application_mailer.rb +4 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/models/user.rb +4 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +34 -0
- data/spec/dummy/bin/update +29 -0
- data/spec/dummy/config/application.rb +15 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/cable.yml +9 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +54 -0
- data/spec/dummy/config/environments/production.rb +86 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/puma.rb +47 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config/spring.rb +6 -0
- data/spec/dummy/config.ru +5 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20170622072636_create_users.rb +10 -0
- data/spec/dummy/db/schema.rb +39 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +29 -0
- data/spec/dummy/log/test.log +31496 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories/ctws/ctws_user.rb +7 -0
- data/spec/factories/ctws_min_app_version.rb +9 -0
- data/spec/models/ctws/min_app_version_spec.rb +11 -0
- data/spec/models/ctws/user_spec.rb +8 -0
- data/spec/rails_helper.rb +88 -0
- data/spec/requests/ctws/authentication_spec.rb +47 -0
- data/spec/requests/ctws/min_app_version_spec.rb +169 -0
- data/spec/requests/ctws/users_spec.rb +46 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/ctws/controller_spec_helper.rb +29 -0
- data/spec/support/ctws/request_spec_helper.rb +10 -0
- metadata +367 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 0a79d5e21625b0567f9a3cca15eaee2828f01a34
|
|
4
|
+
data.tar.gz: 4ca11d4f89b12d6b0c353cbc0541efb9a7b2d07c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b7d96fa180bc72676e003910f0ef4cf6aec988db26625dbbd39d67a0e5b9a96675df1969ea574b74275761766fe0068d70eaa1f50ecfb552d0afffe5879626ae
|
|
7
|
+
data.tar.gz: 22a334939b4b7f159d62fa9d01f6db7cec8dae94e4f7232852aac0113ebfec742b09eb3971652574994e0211a44e3d7d9dd827f90e2ad4a26f493f53986371b3
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 CodiTramuntana
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# ctws
|
|
2
|
+
|
|
3
|
+
Rails gem to be used as Webservice RESTful JSON API with Rails 5.
|
|
4
|
+
|
|
5
|
+
With `MinAppVersion` and `User` resources, with token based authentication [JSON Web Tokens](https://jwt.io/).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
Add this line to your application's Gemfile:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
gem 'ctws'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
```bash
|
|
16
|
+
$ bundle
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
```bash
|
|
21
|
+
$ gem install ctws
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Mounting the Engine
|
|
25
|
+
|
|
26
|
+
To make the `ctws` engine's functionality accessible from within your application, it needs to be mounted in that application's `config/routes.rb` file:
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
mount Ctws::Engine, at: "/ws"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This line will mount the engine at `/ws` in the application. Making it accessible at `http://localhost:3000/ws` when the application runs with rails
|
|
33
|
+
server. It can be whatever you want.
|
|
34
|
+
|
|
35
|
+
## Engine setup
|
|
36
|
+
|
|
37
|
+
The engine contains migrations for the `ctws_min_app_version` and `xxx` table which need to be created in the application's database so that the engine's models can query them correctly.
|
|
38
|
+
To copy these migrations into the application run the following command from the Rails App root, then run these migrations:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
rails ctws:install:migrations
|
|
42
|
+
rails db:migrate
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If you want to revert engine's migrations before removing it. To revert all migrations from blorgh engine you can run code such as:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bin/rails db:migrate SCOPE=ctws VERSION=0
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Hook the application `User` model with the engine
|
|
52
|
+
|
|
53
|
+
By default the user model is `User` but you can change it by creating or editing the `ctws.rb` initializer file in `config/initializers` and put this content in it:
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
Ctws.user_class = "Account"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The application `User` model **must have the `email` attribute**.
|
|
60
|
+
|
|
61
|
+
The **`password` is optional** by default a user is validated with password, for `password` validation [`ActiveModel::SecurePassword::InstanceMethodsOnActivation authenticate`](https://apidock.com/rails/v4.2.7/ActiveModel/SecurePassword/InstanceMethodsOnActivation/authenticate) and [`Devise::Models::DatabaseAuthenticatable#valid_password?`](http://www.rubydoc.info/github/plataformatec/devise/Devise%2FModels%2FDatabaseAuthenticatable:valid_password%3F) User instance methods are supported.
|
|
62
|
+
|
|
63
|
+
To opt out the user validation with the password change it by creating or editing the `ctws.rb` initializer file in `config/initializers` and put this content in it:
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
Ctws.user_validate_with_password = false
|
|
67
|
+
```
|
|
68
|
+
You can edit your app's required fields for signup by creating or editing the `ctws.rb` initializer file in `config/initializers` and put your strong parameters:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
Ctws.user_class_strong_params = %i(email password password_confirmation
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Set the `JWT` expiry time
|
|
75
|
+
|
|
76
|
+
By default the token expiry time is 24h but you can change it by creating or editing the `ctws.rb` initializer file in `config/initializers` and put this content in it:
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
Ctws.jwt_expiration_time = 24.hours.from_now
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
<!--
|
|
83
|
+
Change the app's models so that they know that they are supposed to act like ctws:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
# app/models/user.rb
|
|
87
|
+
|
|
88
|
+
class User < ApplicationRecord
|
|
89
|
+
acts_as_ctws
|
|
90
|
+
end
|
|
91
|
+
```
|
|
92
|
+
-->
|
|
93
|
+
|
|
94
|
+
## Endpoints
|
|
95
|
+
|
|
96
|
+
| Endpoint | Functionality | Requires Authentication? |
|
|
97
|
+
| -------------------------------------------------- | -----------------------------------------------: | :-----------------------: |
|
|
98
|
+
| `GET /ctws/v1/min_app_version` | Get latest minimum app version for all platforms | No |
|
|
99
|
+
| `POST /ctws/signup` | Signup | No |
|
|
100
|
+
| `POST /ctws/login` | Login | No |
|
|
101
|
+
<!--
|
|
102
|
+
| `GET /ctws/v1/min_app_versions` | List all min_app_versions | Yes |
|
|
103
|
+
| `GET /ctws/v1/min_app_versions/:id ` | Get a min_app_version | Yes |
|
|
104
|
+
| `POST /ctws/v1/min_app_versions` | Creates a min_app_version | Yes |
|
|
105
|
+
| `PUT /ctws/v1/min_app_versions/:id` | Updates a min_app_version | Yes |
|
|
106
|
+
| `DELETE /ctws/v1/min_app_versions/:id` | Delete a min_app_version | Yes |
|
|
107
|
+
-->
|
|
108
|
+
|
|
109
|
+
### min_app_version
|
|
110
|
+
|
|
111
|
+
**request:**
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
curl localhost:3000/ws/v1/min_app_version
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**response:**
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
HTTP/1.1 200 OK
|
|
121
|
+
Cache-Control: max-age=0, private, must-revalidate
|
|
122
|
+
Content-Type: application/vnd.api+json; charset=utf-8
|
|
123
|
+
ETag: W/"8dcf1379b7ee203a6d72b3c7773d47f4"
|
|
124
|
+
Transfer-Encoding: chunked
|
|
125
|
+
X-Content-Type-Options: nosniff
|
|
126
|
+
X-Frame-Options: SAMEORIGIN
|
|
127
|
+
X-Request-Id: c1924546-0212-45fe-b86a-83ee3a3b2fa4
|
|
128
|
+
X-Runtime: 0.003897
|
|
129
|
+
X-XSS-Protection: 1; mode=block
|
|
130
|
+
|
|
131
|
+
{
|
|
132
|
+
"data": [
|
|
133
|
+
{
|
|
134
|
+
"id": 3,
|
|
135
|
+
"type": "min_app_version",
|
|
136
|
+
"attributes": {
|
|
137
|
+
"codename": "Second release",
|
|
138
|
+
"description": "Second release Description text",
|
|
139
|
+
"min_version": "0.0.2",
|
|
140
|
+
"platform": "android",
|
|
141
|
+
"store_uri": "htttps://fdsafdsafdsaf.cot",
|
|
142
|
+
"updated_at": "2017-06-22T17:53:31.252+02:00"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"type": "min_app_version",
|
|
147
|
+
"id": 1,
|
|
148
|
+
"attributes": {
|
|
149
|
+
"codename": "First Release",
|
|
150
|
+
"description": "You need to update your app. You will be redirected to the corresponding store",
|
|
151
|
+
"min_version": "0.0.1",
|
|
152
|
+
"platform": "ios",
|
|
153
|
+
"store_uri": "https://itunes.apple.com/",
|
|
154
|
+
"updated_at": "2017-06-21T14:29:59.348+02:00"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
### signup
|
|
161
|
+
|
|
162
|
+
**request:**
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
curl -X POST -F "email=user@example.com" -F "password=123456789" http://localhost:3000/ws/v1/signup
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Successful response:**
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
HTTP/1.1 201 Created
|
|
172
|
+
Cache-Control: max-age=0, private, must-revalidate
|
|
173
|
+
Content-Type: application/vnd.api+json; charset=utf-8
|
|
174
|
+
ETag: W/"ab43e77c2d67636c5c0cd707e661c311"
|
|
175
|
+
Transfer-Encoding: chunked
|
|
176
|
+
X-Content-Type-Options: nosniff
|
|
177
|
+
X-Frame-Options: SAMEORIGIN
|
|
178
|
+
X-Request-Id: 75f9d9cc-4ce2-4aed-9349-e995bb122f39
|
|
179
|
+
X-Runtime: 1.727068
|
|
180
|
+
X-XSS-Protection: 1; mode=block
|
|
181
|
+
|
|
182
|
+
{
|
|
183
|
+
"data": {
|
|
184
|
+
"type": "user",
|
|
185
|
+
"id": 20,
|
|
186
|
+
"attributes": {
|
|
187
|
+
"message": "Account created successfully",
|
|
188
|
+
"auth_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyMCwiZXhwIjoxNDk5ODU1MjQ5fQ.H9ljjShWOAv8b9xn9ZLKv-zgmH8xkPe6dkdhH4JrJPw",
|
|
189
|
+
"created_at": "2017-07-11T12:27:27.916+02:00"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Error response:**
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
HTTP/1.1 401 Unauthorized
|
|
199
|
+
Cache-Control: no-cache
|
|
200
|
+
Content-Type: application/vnd.api+json; charset=utf-8
|
|
201
|
+
Transfer-Encoding: chunked
|
|
202
|
+
X-Content-Type-Options: nosniff
|
|
203
|
+
X-Frame-Options: SAMEORIGIN
|
|
204
|
+
X-Request-Id: b0d91125-446d-4ed3-95cb-4a702ee24289
|
|
205
|
+
X-Runtime: 0.091940
|
|
206
|
+
X-XSS-Protection: 1; mode=block
|
|
207
|
+
|
|
208
|
+
{
|
|
209
|
+
"errors": {
|
|
210
|
+
"message": "Invalid credentials"
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### login
|
|
216
|
+
|
|
217
|
+
**request:**
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
curl -X POST -F "email=user@example.com" -F "password=123456789" http://localhost:3000/ws/v1/login
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Successful response:**
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
HTTP/1.1 200 OK
|
|
227
|
+
Cache-Control: max-age=0, private, must-revalidate
|
|
228
|
+
Content-Type: application/vnd.api+json; charset=utf-8
|
|
229
|
+
ETag: W/"4e7a5faaf9eb480a7a7dadb734d01da1"
|
|
230
|
+
Transfer-Encoding: chunked
|
|
231
|
+
X-Content-Type-Options: nosniff
|
|
232
|
+
X-Frame-Options: SAMEORIGIN
|
|
233
|
+
X-Request-Id: 565a8015-9087-480c-b5b6-1dbf634c7d83
|
|
234
|
+
X-Runtime: 0.278055
|
|
235
|
+
X-XSS-Protection: 1; mode=block
|
|
236
|
+
|
|
237
|
+
{
|
|
238
|
+
"data": {
|
|
239
|
+
"type": "authentication",
|
|
240
|
+
"attributes": {
|
|
241
|
+
"message": "Authenticated user successfully",
|
|
242
|
+
"auth_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxNiwiZXhwIjoxNDk5ODU2MDgyfQ.FOLNcInu0yxnp_dqVnyzfzGNwKyv_ERoflhW4cvTa60"
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Error response:**
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
HTTP/1.1 401 Unauthorized
|
|
252
|
+
Cache-Control: no-cache
|
|
253
|
+
Content-Type: application/vnd.api+json; charset=utf-8
|
|
254
|
+
Transfer-Encoding: chunked
|
|
255
|
+
X-Content-Type-Options: nosniff
|
|
256
|
+
X-Frame-Options: SAMEORIGIN
|
|
257
|
+
X-Request-Id: e95e4b63-3a83-409f-bd05-4cf2dea6136a
|
|
258
|
+
X-Runtime: 0.015463
|
|
259
|
+
X-XSS-Protection: 1; mode=block
|
|
260
|
+
|
|
261
|
+
{
|
|
262
|
+
"errors": {
|
|
263
|
+
"message": "Invalid credentials"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Tests
|
|
269
|
+
|
|
270
|
+
running `rspec` will run tests and output a report, first run migrations the tests:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
rails db:migrate RAILS_ENV=test
|
|
274
|
+
rspec
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Contributing
|
|
278
|
+
|
|
279
|
+
Bug reports and pull requests are welcome on GitHub. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
`ctws` is Copyright © 2017 CodiTramuntana SL. It is free software, and may be redistributed under the terms specified in the LICENSE file.
|
|
284
|
+
|
|
285
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
286
|
+
|
|
287
|
+
# About CodiTramuntana
|
|
288
|
+
|
|
289
|
+

|
|
290
|
+
|
|
291
|
+
Maintained by [CodiTramuntana](http://www.coditramuntana.com).
|
|
292
|
+
|
|
293
|
+
The names and logos for CodiTramuntana are trademarks of CodiTramuntana SL.
|
|
294
|
+
|
|
295
|
+
We love open source software!
|
data/Rakefile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
|
9
|
+
load 'rails/tasks/engine.rake'
|
|
10
|
+
|
|
11
|
+
Bundler::GemHelper.install_tasks
|
|
12
|
+
|
|
13
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
|
|
14
|
+
|
|
15
|
+
require 'rspec/core'
|
|
16
|
+
require 'rspec/core/rake_task'
|
|
17
|
+
|
|
18
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
|
19
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
|
20
|
+
|
|
21
|
+
task :default => :spec
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Ctws
|
|
2
|
+
class AuthenticateUser
|
|
3
|
+
def initialize(email, password="12346")
|
|
4
|
+
@email = email
|
|
5
|
+
@password = password
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Service entry point
|
|
9
|
+
def call
|
|
10
|
+
Ctws::JsonWebToken.encode(user_id: user.id) if user
|
|
11
|
+
|
|
12
|
+
# attrs_hash = {}
|
|
13
|
+
# Ctws.jwt_auth_token_attrs.each {|a| attrs_hash.merge!({"user_#{a}": user.try(a)})}
|
|
14
|
+
# Ctws::JsonWebToken.encode(attrs_hash) if user
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
attr_reader :email, :password
|
|
20
|
+
|
|
21
|
+
# verify user credentials
|
|
22
|
+
def user
|
|
23
|
+
user = Ctws.user_class.find_by(email: email)
|
|
24
|
+
|
|
25
|
+
if Ctws.user_validate_with_password
|
|
26
|
+
# try method of Active Record's has_secure_password or Devise valid_password?
|
|
27
|
+
authenticated = user.try(:authenticate, password) || user.try(:valid_password?, password)
|
|
28
|
+
elsif !Ctws.user_validate_with_password
|
|
29
|
+
authenticated = true
|
|
30
|
+
end
|
|
31
|
+
return user if user && authenticated
|
|
32
|
+
# raise Authentication error if credentials are invalid
|
|
33
|
+
raise(Ctws::ExceptionHandler::AuthenticationError, Ctws::Message.invalid_credentials)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Ctws
|
|
2
|
+
class AuthorizeApiRequest
|
|
3
|
+
def initialize(headers = {})
|
|
4
|
+
@headers = headers
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# Service entry point - return valid user object
|
|
8
|
+
def call
|
|
9
|
+
{
|
|
10
|
+
user: user
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
attr_reader :headers
|
|
17
|
+
|
|
18
|
+
def user
|
|
19
|
+
# check if user is in the database
|
|
20
|
+
# memoize user object
|
|
21
|
+
@user ||= Ctws.user_class.find(decoded_auth_token[:user_id]) if decoded_auth_token
|
|
22
|
+
# handle user not found
|
|
23
|
+
rescue ActiveRecord::RecordNotFound => e
|
|
24
|
+
# raise custom error
|
|
25
|
+
raise(
|
|
26
|
+
ExceptionHandler::InvalidToken,
|
|
27
|
+
("#{Ctws::Message.invalid_token} #{e.message}")
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# decode authentication token
|
|
32
|
+
def decoded_auth_token
|
|
33
|
+
@decoded_auth_token ||= Ctws::JsonWebToken.decode(http_auth_header)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# check for token in `Authorization` header
|
|
37
|
+
def http_auth_header
|
|
38
|
+
if headers['Authorization'].present?
|
|
39
|
+
return headers['Authorization'].split(' ').last
|
|
40
|
+
end
|
|
41
|
+
raise(ExceptionHandler::MissingToken, Ctws::Message.missing_token)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Ctws
|
|
2
|
+
# In the case where the record does not exist,
|
|
3
|
+
# ActiveRecord will throw an exception ActiveRecord::RecordNotFound.
|
|
4
|
+
# We'll rescue from this exception and return a 404 message.
|
|
5
|
+
# List of Rails Status Code Symbols http://billpatrianakos.me/blog/2013/10/13/list-of-rails-status-code-symbols
|
|
6
|
+
|
|
7
|
+
module ExceptionHandler
|
|
8
|
+
# provides the more graceful `included` method
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
# Define custom error subclasses - rescue catches `StandardErrors`
|
|
12
|
+
class AuthenticationError < StandardError; end
|
|
13
|
+
class MissingToken < StandardError; end
|
|
14
|
+
class InvalidToken < StandardError; end
|
|
15
|
+
class ExpiredSignature < StandardError; end
|
|
16
|
+
class UnprocessableEntity < StandardError; end
|
|
17
|
+
class RoutingError < StandardError; end
|
|
18
|
+
|
|
19
|
+
included do
|
|
20
|
+
# Define custom handlers
|
|
21
|
+
rescue_from ActiveRecord::RecordInvalid, with: :four_twenty_two
|
|
22
|
+
rescue_from ExceptionHandler::AuthenticationError, with: :unauthorized_request
|
|
23
|
+
rescue_from ExceptionHandler::MissingToken, with: :four_twenty_two
|
|
24
|
+
rescue_from ExceptionHandler::InvalidToken, with: :four_twenty_two
|
|
25
|
+
rescue_from ExceptionHandler::UnprocessableEntity, with: :four_twenty_two
|
|
26
|
+
rescue_from ExceptionHandler::ExpiredSignature, with: :four_ninety_eight
|
|
27
|
+
rescue_from ExceptionHandler::RoutingError, with: :not_found
|
|
28
|
+
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
|
29
|
+
rescue_from ActionController::RoutingError, with: :not_found
|
|
30
|
+
|
|
31
|
+
rescue_from ActiveRecord::RecordInvalid do |e|
|
|
32
|
+
json_response({ message: e.message }, :unprocessable_entity)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# JSON response with message; Status code 401 - Unauthorized
|
|
37
|
+
def unauthorized_request(e)
|
|
38
|
+
json_response({ message: e.message }, :unauthorized)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# JSON response with message; Status code 401 - Unauthorized
|
|
42
|
+
def not_found(e)
|
|
43
|
+
json_response({ message: e.message }, :not_found)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# JSON response with message; Status code 422 - unprocessable entity
|
|
47
|
+
def four_twenty_two(e)
|
|
48
|
+
json_response({ message: e.message }, :unprocessable_entity)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# JSON response with message; Status code 498 - Invalid Token
|
|
53
|
+
def four_ninety_eight(e)
|
|
54
|
+
json_response({ message: e.message }, :invalid_token)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Ctws
|
|
2
|
+
module Response
|
|
3
|
+
# responds with JSON and an HTTP status code (200 by default)
|
|
4
|
+
# json_response(@todo, :created)
|
|
5
|
+
def payload? object, status
|
|
6
|
+
case status
|
|
7
|
+
when :not_found, :unprocessable_entity, :unauthorized, :invalid_token
|
|
8
|
+
self.errors_payload(object)
|
|
9
|
+
else
|
|
10
|
+
self.data_payload(object)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def json_response(object = {}, status = :ok)
|
|
15
|
+
render json: self.payload?(object, status), status: status
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def data_payload(object)
|
|
19
|
+
{data: object}
|
|
20
|
+
end
|
|
21
|
+
def errors_payload(object)
|
|
22
|
+
{errors: object}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Ctws
|
|
2
|
+
class AuthenticationController < CtwsController
|
|
3
|
+
skip_before_action :authorize_request, only: :authenticate
|
|
4
|
+
|
|
5
|
+
# return auth token once user is authenticated
|
|
6
|
+
def authenticate
|
|
7
|
+
auth_token = Ctws::AuthenticateUser.new(auth_params[:email], auth_params[:password]).call
|
|
8
|
+
json_response auth_as_jsonapi(auth_token)
|
|
9
|
+
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def auth_as_jsonapi auth_token
|
|
15
|
+
{
|
|
16
|
+
type: controller_name,
|
|
17
|
+
attributes: {
|
|
18
|
+
message: Ctws::Message.authenticated_user_success,
|
|
19
|
+
auth_token: auth_token,
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def auth_params
|
|
25
|
+
params.permit(:email, :password)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require_dependency "ctws/application_controller"
|
|
2
|
+
|
|
3
|
+
module Ctws
|
|
4
|
+
class CtwsController < ApplicationController
|
|
5
|
+
include Response
|
|
6
|
+
include ExceptionHandler
|
|
7
|
+
# Generic API stuff here
|
|
8
|
+
|
|
9
|
+
# called before every action on controllers
|
|
10
|
+
before_action :authorize_request
|
|
11
|
+
skip_before_action :authorize_request, only: [:raise_not_found!]
|
|
12
|
+
attr_reader :current_user
|
|
13
|
+
|
|
14
|
+
def raise_not_found!
|
|
15
|
+
raise Ctws::ExceptionHandler::RoutingError, ("#{Ctws::Message.unmatched_route(params[:unmatched_route])}")
|
|
16
|
+
end
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# Check for valid request token and return user
|
|
20
|
+
def authorize_request
|
|
21
|
+
@current_user = (Ctws::AuthorizeApiRequest.new(request.headers).call)[:user]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module Ctws
|
|
2
|
+
class MinAppVersionsController < CtwsController
|
|
3
|
+
skip_before_action :authorize_request, only: [:min_app_version]
|
|
4
|
+
before_action :set_min_app_version, only: [:show, :update, :destroy]
|
|
5
|
+
|
|
6
|
+
# GET /min_app_version
|
|
7
|
+
def min_app_version
|
|
8
|
+
min_app_versions = []
|
|
9
|
+
MinAppVersion.group(:platform).each do |platform|
|
|
10
|
+
min_app_versions << platform.as_jsonapi
|
|
11
|
+
end
|
|
12
|
+
json_response min_app_versions
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# GET /min_app_versions
|
|
16
|
+
def index
|
|
17
|
+
json_response MinAppVersion.all
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# GET /min_app_versions/1
|
|
21
|
+
def show
|
|
22
|
+
json_response @min_app_version.as_jsonapi
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# POST /min_app_versions
|
|
26
|
+
def create
|
|
27
|
+
@min_app_version = MinAppVersion.create!(min_app_version_params)
|
|
28
|
+
json_response @min_app_version, :created
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# PATCH/PUT /min_app_versions/1
|
|
32
|
+
def update
|
|
33
|
+
@min_app_version.update(min_app_version_params)
|
|
34
|
+
head :no_content
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# DELETE /min_app_versions/1
|
|
38
|
+
def destroy
|
|
39
|
+
@min_app_version.destroy
|
|
40
|
+
head :no_content
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
# Use callbacks to share common setup or constraints between actions.
|
|
45
|
+
def set_min_app_version
|
|
46
|
+
@min_app_version = MinAppVersion.find(params[:id])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Only allow a trusted parameter "white list" through.
|
|
50
|
+
def min_app_version_params
|
|
51
|
+
params.require(:min_app_version).permit(:codename, :description, :platform, :min_version, :store_uri)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Ctws
|
|
2
|
+
class UsersController < CtwsController
|
|
3
|
+
skip_before_action :authorize_request, only: :create
|
|
4
|
+
|
|
5
|
+
# POST /signup
|
|
6
|
+
# return authenticated token upon signup
|
|
7
|
+
def create
|
|
8
|
+
# We use Active Record's create! method so that in the event there's an error,
|
|
9
|
+
# an exception will be raised and handled in the exception handler.
|
|
10
|
+
ctws_user = Ctws.user_class.create!(ctws_user_params)
|
|
11
|
+
if Ctws.user_validate_with_password
|
|
12
|
+
auth_token = Ctws::AuthenticateUser.new(ctws_user.email, ctws_user.password).call
|
|
13
|
+
elsif !Ctws.user_validate_with_password
|
|
14
|
+
auth_token = Ctws::AuthenticateUser.new(ctws_user.email).call
|
|
15
|
+
end
|
|
16
|
+
# response = { message: Ctws::Message.account_created, auth_token: auth_token }
|
|
17
|
+
|
|
18
|
+
json_response(user_as_jsonapi(ctws_user, auth_token), :created)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def user_as_jsonapi user, auth_token
|
|
24
|
+
{
|
|
25
|
+
type: ActiveModel::Naming.param_key(Ctws.user_class),
|
|
26
|
+
id: user.id,
|
|
27
|
+
attributes: {
|
|
28
|
+
message: Ctws::Message.account_created,
|
|
29
|
+
auth_token: auth_token,
|
|
30
|
+
created_at: user.created_at
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ctws_user_params
|
|
36
|
+
params.permit(Ctws.user_class_strong_params)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|