ctws 0.1.5.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +295 -0
  4. data/Rakefile +21 -0
  5. data/app/auth/ctws/authenticate_user.rb +36 -0
  6. data/app/auth/ctws/authorize_api_request.rb +44 -0
  7. data/app/controllers/concerns/ctws/exception_handler.rb +58 -0
  8. data/app/controllers/concerns/ctws/response.rb +25 -0
  9. data/app/controllers/concerns/ctws/v1/exception_handler.rb +6 -0
  10. data/app/controllers/concerns/ctws/v1/response.rb +6 -0
  11. data/app/controllers/ctws/application_controller.rb +5 -0
  12. data/app/controllers/ctws/authentication_controller.rb +28 -0
  13. data/app/controllers/ctws/ctws_controller.rb +24 -0
  14. data/app/controllers/ctws/min_app_versions_controller.rb +55 -0
  15. data/app/controllers/ctws/users_controller.rb +39 -0
  16. data/app/controllers/ctws/v1/authentication_controller.rb +6 -0
  17. data/app/controllers/ctws/v1/ctws_controller.rb +9 -0
  18. data/app/controllers/ctws/v1/min_app_versions_controller.rb +6 -0
  19. data/app/controllers/ctws/v1/users_controller.rb +6 -0
  20. data/app/controllers/ctws/v2/ctws_controller.rb +9 -0
  21. data/app/controllers/ctws/v2/min_app_versions_controller.rb +6 -0
  22. data/app/jobs/ctws/application_job.rb +4 -0
  23. data/app/lib/ctws/json_web_token.rb +25 -0
  24. data/app/lib/ctws/message.rb +43 -0
  25. data/app/mailers/ctws/application_mailer.rb +6 -0
  26. data/app/models/ctws/application_record.rb +5 -0
  27. data/app/models/ctws/min_app_version.rb +22 -0
  28. data/config/initializers/ctws.rb +5 -0
  29. data/config/initializers/mime_types.rb +1 -0
  30. data/config/routes.rb +18 -0
  31. data/db/migrate/20170425110839_create_ctws_min_app_versions.rb +13 -0
  32. data/db/migrate/20170425110933_add_values_to_ctws_min_app_versions.rb +26 -0
  33. data/lib/ctws/engine.rb +13 -0
  34. data/lib/ctws/version.rb +3 -0
  35. data/lib/ctws.rb +18 -0
  36. data/lib/tasks/ctws_tasks.rake +4 -0
  37. data/spec/auth/ctws/authenticate_user_spec.rb +34 -0
  38. data/spec/auth/ctws/authorize_api_request_spec.rb +62 -0
  39. data/spec/controllers/ctws/ctws_controller_spec.rb +41 -0
  40. data/spec/controllers/ctws/users_controller_spec.rb +7 -0
  41. data/spec/dummy/Rakefile +6 -0
  42. data/spec/dummy/app/assets/config/manifest.js +5 -0
  43. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  44. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  45. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  46. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  47. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  48. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  49. data/spec/dummy/app/controllers/user_controller.rb +18 -0
  50. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  51. data/spec/dummy/app/helpers/user_helper.rb +2 -0
  52. data/spec/dummy/app/jobs/application_job.rb +2 -0
  53. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  54. data/spec/dummy/app/models/application_record.rb +3 -0
  55. data/spec/dummy/app/models/user.rb +4 -0
  56. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  57. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  58. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  59. data/spec/dummy/bin/bundle +3 -0
  60. data/spec/dummy/bin/rails +4 -0
  61. data/spec/dummy/bin/rake +4 -0
  62. data/spec/dummy/bin/setup +34 -0
  63. data/spec/dummy/bin/update +29 -0
  64. data/spec/dummy/config/application.rb +15 -0
  65. data/spec/dummy/config/boot.rb +5 -0
  66. data/spec/dummy/config/cable.yml +9 -0
  67. data/spec/dummy/config/database.yml +25 -0
  68. data/spec/dummy/config/environment.rb +5 -0
  69. data/spec/dummy/config/environments/development.rb +54 -0
  70. data/spec/dummy/config/environments/production.rb +86 -0
  71. data/spec/dummy/config/environments/test.rb +42 -0
  72. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  73. data/spec/dummy/config/initializers/assets.rb +11 -0
  74. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  75. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  76. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  77. data/spec/dummy/config/initializers/inflections.rb +16 -0
  78. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  79. data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
  80. data/spec/dummy/config/initializers/session_store.rb +3 -0
  81. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  82. data/spec/dummy/config/locales/en.yml +23 -0
  83. data/spec/dummy/config/puma.rb +47 -0
  84. data/spec/dummy/config/routes.rb +5 -0
  85. data/spec/dummy/config/secrets.yml +22 -0
  86. data/spec/dummy/config/spring.rb +6 -0
  87. data/spec/dummy/config.ru +5 -0
  88. data/spec/dummy/db/development.sqlite3 +0 -0
  89. data/spec/dummy/db/migrate/20170622072636_create_users.rb +10 -0
  90. data/spec/dummy/db/schema.rb +39 -0
  91. data/spec/dummy/db/test.sqlite3 +0 -0
  92. data/spec/dummy/log/development.log +29 -0
  93. data/spec/dummy/log/test.log +31496 -0
  94. data/spec/dummy/public/404.html +67 -0
  95. data/spec/dummy/public/422.html +67 -0
  96. data/spec/dummy/public/500.html +66 -0
  97. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  98. data/spec/dummy/public/apple-touch-icon.png +0 -0
  99. data/spec/dummy/public/favicon.ico +0 -0
  100. data/spec/factories/ctws/ctws_user.rb +7 -0
  101. data/spec/factories/ctws_min_app_version.rb +9 -0
  102. data/spec/models/ctws/min_app_version_spec.rb +11 -0
  103. data/spec/models/ctws/user_spec.rb +8 -0
  104. data/spec/rails_helper.rb +88 -0
  105. data/spec/requests/ctws/authentication_spec.rb +47 -0
  106. data/spec/requests/ctws/min_app_version_spec.rb +169 -0
  107. data/spec/requests/ctws/users_spec.rb +46 -0
  108. data/spec/spec_helper.rb +27 -0
  109. data/spec/support/ctws/controller_spec_helper.rb +29 -0
  110. data/spec/support/ctws/request_spec_helper.rb +10 -0
  111. 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
+ ![CodiTramuntana's Logo](https://avatars0.githubusercontent.com/u/27996979?v=3&u=b0256e23ae7b2f237e3d1b5f2b2abdfe3092b24c&s=400)
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,6 @@
1
+ module Ctws
2
+ module V1
3
+ class ExceptionHandler < Ctws::ExceptionHandler
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Ctws
2
+ module V1
3
+ module Response < Ctws::Response
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Ctws
2
+ class ApplicationController < ActionController::API
3
+ # protect_from_forgery with: :exception
4
+ end
5
+ 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
@@ -0,0 +1,6 @@
1
+ module Ctws
2
+ module V1
3
+ class AuthenticationController < Ctws::AuthenticationController
4
+ end
5
+ end
6
+ end