Skip to content

Commit

Permalink
[add] starter with user model and jwt_blacklist
Browse files Browse the repository at this point in the history
- username attribute is out of devise
- use pg instead of sqlite
- created .env.development
- modified readme
- manully generated session and registration controller
  • Loading branch information
julienemo committed Jun 7, 2020
1 parent b9f5c02 commit f49b2b5
Show file tree
Hide file tree
Showing 29 changed files with 893 additions and 83 deletions.
2 changes: 2 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DEVISE_JWT_SECRET_KEY="60cc8b7b4a08899e61f7f1bceed87095babd4f4af477a03b098b8c03bbc4db1d79f22b0a7d58bbe5ecfadc06cc29fb0abeaa317b4e2eb730ae1e92fef357c071"
PORT=8080
38 changes: 14 additions & 24 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,41 +1,31 @@
# frozen_string_literal: true

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.7.1'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.1'
# Use sqlite3 as the database for Active Record
gem 'sqlite3', '~> 1.4'
# Use Puma as the app server
gem 'pg', '>= 0.18', '< 2.0'
gem 'puma', '~> 4.1'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
# gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'
gem 'rack-cors', '~> 1.1', '>= 1.1.1'
gem 'rails', '~> 6.0.3', '>= 6.0.3.1'
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'
# this is a websocket-driver dependency
# earlier versions present vulnerability
gem 'websocket-extensions', '>= 0.1.5'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
# gem 'rack-cors'
gem 'devise', '>= 4.7.1'
gem 'devise-jwt', '~> 0.6.0'
gem 'dotenv-rails', groups: %i[development test]

group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
gem 'pry', '>=0.13.1', platforms: %i[mri mingw x64_mingw]
end

group :development do
gem 'listen', '~> 3.2'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring', '>= 2.1.0'
gem 'spring-watcher-listen', '~> 2.0.0'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
62 changes: 54 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,43 @@ GEM
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
bcrypt (3.1.13)
bootsnap (1.4.6)
msgpack (~> 1.0)
builder (3.2.4)
byebug (11.1.3)
coderay (1.1.3)
concurrent-ruby (1.1.6)
crass (1.0.6)
devise (4.7.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
devise-jwt (0.6.0)
devise (~> 4.0)
warden-jwt_auth (~> 0.4)
dotenv (2.7.5)
dotenv-rails (2.7.5)
dotenv (= 2.7.5)
railties (>= 3.2, < 6.1)
dry-auto_inject (0.7.0)
dry-container (>= 0.3.4)
dry-configurable (0.9.0)
concurrent-ruby (~> 1.0)
dry-core (~> 0.4, >= 0.4.7)
dry-container (0.7.2)
concurrent-ruby (~> 1.0)
dry-configurable (~> 0.1, >= 0.1.3)
dry-core (0.4.9)
concurrent-ruby (~> 1.0)
erubi (1.9.0)
ffi (1.13.0)
globalid (0.4.2)
activesupport (>= 4.2.0)
i18n (1.8.2)
i18n (1.8.3)
concurrent-ruby (~> 1.0)
jwt (2.2.1)
listen (3.2.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
Expand All @@ -87,9 +112,16 @@ GEM
nio4r (2.5.2)
nokogiri (1.10.9)
mini_portile2 (~> 2.4.0)
orm_adapter (0.5.0)
pg (1.2.3)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
puma (4.3.5)
nio4r (~> 2.0)
rack (2.2.2)
rack-cors (1.1.1)
rack (>= 2.0.0)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.0.3.1)
Expand Down Expand Up @@ -122,40 +154,54 @@ GEM
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
spring (2.1.0)
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
sprockets (4.0.0)
sprockets (4.0.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.4.2)
thor (1.0.1)
thread_safe (0.3.6)
tzinfo (1.2.7)
thread_safe (~> 0.1)
warden (1.2.8)
rack (>= 2.0.6)
warden-jwt_auth (0.4.2)
dry-auto_inject (~> 0.6)
dry-configurable (~> 0.9, < 0.11)
jwt (~> 2.1)
warden (~> 1.2)
websocket-driver (0.7.2)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.4)
websocket-extensions (0.1.5)
zeitwerk (2.3.0)

PLATFORMS
ruby

DEPENDENCIES
bootsnap (>= 1.4.2)
byebug
devise (>= 4.7.1)
devise-jwt (~> 0.6.0)
dotenv-rails
listen (~> 3.2)
pg (>= 0.18, < 2.0)
pry (>= 0.13.1)
puma (~> 4.1)
rack-cors (~> 1.1, >= 1.1.1)
rails (~> 6.0.3, >= 6.0.3.1)
spring
spring (>= 2.1.0)
spring-watcher-listen (~> 2.0.0)
sqlite3 (~> 1.4)
tzinfo-data
websocket-extensions (>= 0.1.5)

RUBY VERSION
ruby 2.7.1p83
Expand Down
75 changes: 62 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,73 @@
# README
### Rails API

This README would normally document whatever steps are necessary to get the
application up and running.

Things you may want to cover:
* * *
### Endpoints

* Ruby version
* * *

* System dependencies
#### Public (without token)
**POST /sign_up**

* Configuration
data-form body:
```
{
user[username]: "something",
user[email]: "[email protected]",
user[password]: "something"
}
```

* Database creation
After sign-up, user needs to sign-in to get token

* Database initialization

* How to run the test suite
**POST /sign_in**

* Services (job queues, cache servers, search engines, etc.)
data-form body:
```
{
user[email]: "[email protected]",
user[password]: "something"
}
```
Token is in the "Authorization" header

* Deployment instructions
* * *

* ...
#### Private (token mandatory)

the following requests need authorization header

**DELETE /sign_out**
no body

**GET /users**
visualize public info for all users

**GET /users/id**
visualize public info for specified user

**GET /profile**
visualize own profile, all info except for password

**PATCH /users/id**

only allowed on one's own profile

data-form body:
```
{
user[username]: "something"
}
```

**DELETE /users/id**

only allowed on one's own profile

answer
```{
id: 3
status: 'user deleted'
}
```
33 changes: 33 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,35 @@
class ApplicationController < ActionController::API
before_action :sanitize_devise_params, if: :devise_controller?
before_action :authenticate_user!
respond_to :json

rescue_from ActionController::InvalidAuthenticityToken,
with: :invalid_auth_token

def sanitize_devise_params
devise_parameter_sanitizer.permit(:sign_up, keys: %i[username])
end

def render_resource(resource)
if resource.errors.empty?
render json: resource
else
validation_error(resource)
end
end

def validation_error(resource)
render json: {
error: 'Bad request',
status: '400',
code: '100',
details: resource.errors
}, status: :bad_request
end

def invalid_auth_token
respond_to do |format|
format.json { head 401 }
end
end
end
13 changes: 13 additions & 0 deletions app/controllers/profiles_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class ProfilesController < ApplicationController
before_action :set_user

def index
render json: @user
end

private

def set_user
@user = current_user
end
end
10 changes: 10 additions & 0 deletions app/controllers/registrations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class RegistrationsController < Devise::RegistrationsController
respond_to :json

def create
build_resource(sign_up_params)

resource.save
render json: resource
end
end
13 changes: 13 additions & 0 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class SessionsController < Devise::SessionsController
respond_to :json

private

def respond_with(resource, _opts = {})
render json: resource
end

def respond_to_on_destroy
head :no_content
end
end
53 changes: 53 additions & 0 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
class UsersController < ApplicationController
before_action :set_user, only: [:show, :update, :destroy]
before_action :current_user_can_modify_and_delete, only: [:update, :destroy]
def index
@users = User.all.map { |user| public_info(user) }

render json: @users
end

def show
render json: public_info(@user)
end

def update
if @user.update(user_params)
render json: @user
else
render json: { error: @user.errors }, status: :unprocessable_entity
end
end

def destroy
id = @user.id
@user.destroy
render json: { id: id, status: 'user deleted' }
end

private

def set_user
@user = User.find(params[:id])
rescue ActiveRecord::RecordNotFound
render json: { error: 'Not Found', detail: 'The resource you are looking for does not exist'}
end

def user_params
params.require(:user).permit(:first_name, :last_name, :username)
# in postman, to write body in "raw" that contains a scope, do key; user[something]
end

def public_info(user)
{
id: user.id,
username: user.username
}
end

def current_user_can_modify_and_delete
if current_user != @user
render json: { error: "Unauthorized", detail: "Can only do this to one's own profile" }
end
end
end
Loading

0 comments on commit f49b2b5

Please sign in to comment.