diff --git a/.rubocop.yml b/.rubocop.yml
index c69a64de8..295ae2d66 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -3,7 +3,7 @@ AllCops:
- "vendor/**/*"
- "db/schema.rb"
UseCache: false
- TargetRubyVersion: 2.2
+ TargetRubyVersion: 2.4
##################### Style ####################################
diff --git a/Gemfile b/Gemfile
index 881bcefca..d5f785c6f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -15,6 +15,9 @@ gem 'bootstrap_form'
gem 'yomu'
gem 'recaptcha', require: 'recaptcha/rails'
gem 'sanitize', '~> 4.4'
+
+# Gems for OAuth2 subsystem
+gem 'doorkeeper', '~> 4.4'
gem 'omniauth'
gem 'omniauth-linkedin-oauth2'
diff --git a/Gemfile.lock b/Gemfile.lock
index c5f2bd7e6..12e8c7223 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -216,6 +216,8 @@ GEM
discard (1.0.0)
activerecord (>= 4.2, < 6)
docile (1.1.5)
+ doorkeeper (4.4.1)
+ railties (>= 4.2)
erubi (1.7.1)
execjs (2.7.0)
factory_bot (4.8.2)
@@ -563,6 +565,7 @@ DEPENDENCIES
devise_invitable
devise_security_extension!
discard (~> 1.0)
+ doorkeeper (~> 4.4)
factory_bot_rails
faker
figaro
diff --git a/app/controllers/api/api_controller.rb b/app/controllers/api/api_controller.rb
index 630e7fa4c..e1c5d76a3 100644
--- a/app/controllers/api/api_controller.rb
+++ b/app/controllers/api/api_controller.rb
@@ -1,12 +1,12 @@
+# frozen_string_literal: true
+
module Api
class ApiController < ActionController::API
attr_reader :iss
attr_reader :token
attr_reader :current_user
- before_action :load_token, except: %i(authenticate status health)
- before_action :load_iss, except: %i(authenticate status health)
- before_action :authenticate_request!, except: %i(authenticate status health)
+ before_action :authenticate_request!, except: %i(status health)
rescue_from StandardError do |e|
logger.error e.message
@@ -47,30 +47,8 @@ module Api
render json: response, status: :ok
end
- def authenticate
- if auth_params[:grant_type] == 'password'
- user = User.find_by_email(auth_params[:email])
- unless user && user.valid_password?(auth_params[:password])
- raise StandardError, 'Default: Wrong user password'
- end
- payload = { user_id: user.id }
- token = CoreJwt.encode(payload)
- render json: { token_type: 'bearer', access_token: token }
- else
- raise StandardError, 'Default: Wrong grant type in request'
- end
- end
-
private
- def load_token
- if request.headers['Authorization']
- @token =
- request.headers['Authorization'].scan(/Bearer (.*)$/).flatten.last
- end
- raise StandardError, 'Common: No token in the header' unless @token
- end
-
def azure_jwt_auth
return unless iss =~ %r{windows.net/|microsoftonline.com/}
token_payload, = Api::AzureJwt.decode(token)
@@ -81,6 +59,12 @@ module Api
end
def authenticate_request!
+ @token = request.headers['Authorization']&.sub('Bearer ', '')
+ raise StandardError, 'Common: No token in the header' unless @token
+
+ @iss = CoreJwt.read_iss(token)
+ raise JWT::InvalidPayload, 'Common: Missing ISS in the token' unless @iss
+
Extends::API_PLUGABLE_AUTH_METHODS.each do |auth_method|
method(auth_method).call
return true if current_user
@@ -105,11 +89,6 @@ module Api
end
end
- def load_iss
- @iss = CoreJwt.read_iss(token)
- raise JWT::InvalidPayload, 'Common: Missing ISS in the token' unless @iss
- end
-
def auth_params
params.permit(:grant_type, :email, :password)
end
diff --git a/app/models/user.rb b/app/models/user.rb
index c124181ca..1156e0174 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -198,6 +198,13 @@ class User < ApplicationRecord
has_many :zip_exports, inverse_of: :user, dependent: :destroy
has_many :datatables_teams, class_name: '::Views::Datatables::DatatablesTeam'
+ has_many :access_grants, class_name: 'Doorkeeper::AccessGrant',
+ foreign_key: :resource_owner_id,
+ dependent: :delete_all
+ has_many :access_tokens, class_name: 'Doorkeeper::AccessToken',
+ foreign_key: :resource_owner_id,
+ dependent: :delete_all
+
# If other errors besides parameter "avatar" exist,
# they will propagate to "avatar" also, so remove them
# and put all other (more specific ones) in it
diff --git a/app/services/api/core_jwt.rb b/app/services/api/core_jwt.rb
index b4f7e9f60..cf872df9b 100644
--- a/app/services/api/core_jwt.rb
+++ b/app/services/api/core_jwt.rb
@@ -30,5 +30,13 @@ module Api
return true if time_left < (Api.configuration.core_api_token_ttl.to_i / 2)
false
end
+
+ # Method used by Doorkeeper for custom tokens
+ def self.generate(options = {})
+ encode(
+ { user_id: options[:resource_owner_id] },
+ options[:expires_in].seconds.from_now.to_i
+ )
+ end
end
end
diff --git a/app/views/doorkeeper/authorizations/error.html.erb b/app/views/doorkeeper/authorizations/error.html.erb
new file mode 100644
index 000000000..5b2f8284d
--- /dev/null
+++ b/app/views/doorkeeper/authorizations/error.html.erb
@@ -0,0 +1,9 @@
+
+
+
+
+ <%= @pre_auth.error_response.body[:error_description] %>
+
+
diff --git a/app/views/doorkeeper/authorizations/new.html.erb b/app/views/doorkeeper/authorizations/new.html.erb
new file mode 100644
index 000000000..b37bd506d
--- /dev/null
+++ b/app/views/doorkeeper/authorizations/new.html.erb
@@ -0,0 +1,42 @@
+
+
+
+
+
+ <%= raw t('.prompt', client_name: content_tag(:strong, class: 'text-info') { @pre_auth.client.name }) %>
+
+
+ <% if @pre_auth.scopes.count > 0 %>
+
+
<%= t('.able_to') %>:
+
+
+ <% @pre_auth.scopes.each do |scope| %>
+ - <%= t scope, scope: [:doorkeeper, :scopes] %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= form_tag oauth_authorization_path, method: :post do %>
+ <%= hidden_field_tag :client_id, @pre_auth.client.uid %>
+ <%= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri %>
+ <%= hidden_field_tag :state, @pre_auth.state %>
+ <%= hidden_field_tag :response_type, @pre_auth.response_type %>
+ <%= hidden_field_tag :scope, @pre_auth.scope %>
+ <%= submit_tag t('doorkeeper.authorizations.buttons.authorize'), class: "btn btn-success btn-lg btn-block" %>
+ <% end %>
+ <%= form_tag oauth_authorization_path, method: :delete do %>
+ <%= hidden_field_tag :client_id, @pre_auth.client.uid %>
+ <%= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri %>
+ <%= hidden_field_tag :state, @pre_auth.state %>
+ <%= hidden_field_tag :response_type, @pre_auth.response_type %>
+ <%= hidden_field_tag :scope, @pre_auth.scope %>
+ <%= submit_tag t('doorkeeper.authorizations.buttons.deny'), class: "btn btn-danger btn-lg btn-block" %>
+ <% end %>
+
+
+
diff --git a/app/views/doorkeeper/authorizations/show.html.erb b/app/views/doorkeeper/authorizations/show.html.erb
new file mode 100644
index 000000000..e6e37e3b2
--- /dev/null
+++ b/app/views/doorkeeper/authorizations/show.html.erb
@@ -0,0 +1,9 @@
+
+
+
+
+ <%= params[:code] %>
+
+
diff --git a/config/application.rb b/config/application.rb
index 901fb0806..d76340c7b 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -42,5 +42,11 @@ module Scinote
# SciNote Core Application version
VERSION = File.read(Rails.root.join('VERSION')).strip.freeze
+
+ # Doorkeeper overrides
+ config.to_prepare do
+ # Only Authorization endpoint
+ Doorkeeper::AuthorizationsController.layout 'sign_in_halt'
+ end
end
end
diff --git a/config/initializers/api.rb b/config/initializers/api.rb
index 4ab3e3898..d65a540b9 100644
--- a/config/initializers/api.rb
+++ b/config/initializers/api.rb
@@ -3,7 +3,7 @@ Api.configure do |config|
config.core_api_sign_alg = ENV['CORE_API_SIGN_ALG']
end
if ENV['CORE_API_TOKEN_TTL']
- config.core_api_token_ttl = ENV['CORE_API_TOKEN_TTL']
+ config.core_api_token_ttl = ENV['CORE_API_TOKEN_TTL'].to_i.seconds
end
if ENV['CORE_API_TOKEN_ISS']
config.core_api_token_iss = ENV['CORE_API_TOKEN_ISS']
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
new file mode 100644
index 000000000..9153fbabd
--- /dev/null
+++ b/config/initializers/doorkeeper.rb
@@ -0,0 +1,136 @@
+Doorkeeper.configure do
+ # Change the ORM that doorkeeper will use (needs plugins)
+ orm :active_record
+
+ # This block will be called to check whether the resource owner is authenticated or not.
+ resource_owner_authenticator do
+ current_user || warden.authenticate!(scope: :user)
+ end
+
+ # If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
+ # admin_authenticator do
+ # # Put your admin authentication logic here.
+ # # Example implementation:
+ # Admin.find_by_id(session[:admin_id]) || redirect_to(new_admin_session_url)
+ # end
+
+ # Authorization Code expiration time (default 10 minutes).
+ authorization_code_expires_in 10.minutes
+
+ # Access token expiration time (default 2 hours).
+ # If you want to disable expiration, set this to nil.
+ access_token_expires_in 2.hours
+
+ # Assign a custom TTL for implicit grants.
+ # custom_access_token_expires_in do |oauth_client|
+ # oauth_client.application.additional_settings.implicit_oauth_expiration
+ # end
+
+ # Use a custom class for generating the access token.
+ # https://github.com/doorkeeper-gem/doorkeeper#custom-access-token-generator
+ access_token_generator 'Api::CoreJwt'
+
+ # The controller Doorkeeper::ApplicationController inherits from.
+ # Defaults to ActionController::Base.
+ # https://github.com/doorkeeper-gem/doorkeeper#custom-base-controller
+ # base_controller 'DoorkeeperCustomController'
+
+ # Reuse access token for the same resource owner within an application (disabled by default)
+ # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
+ # reuse_access_token
+
+ # Issue access tokens with refresh token (disabled by default)
+ use_refresh_token
+
+ # Provide support for an owner to be assigned to each registered application (disabled by default)
+ # Optional parameter confirmation: true (default false) if you want to enforce ownership of
+ # a registered application
+ # Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support
+ # enable_application_owner confirmation: false
+
+ # Define access token scopes for your provider
+ # For more information go to
+ # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
+ # default_scopes :public
+ # optional_scopes :write, :update
+
+ # Change the way client credentials are retrieved from the request object.
+ # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
+ # falls back to the `:client_id` and `:client_secret` params from the `params` object.
+ # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated
+ # for more information on customization
+ # client_credentials :from_basic, :from_params
+
+ # Change the way access token is authenticated from the request object.
+ # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
+ # falls back to the `:access_token` or `:bearer_token` params from the `params` object.
+ # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated
+ # for more information on customization
+ # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
+
+ # Change the native redirect uri for client apps
+ # When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider
+ # The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL
+ # (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
+ #
+ # native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
+
+ # Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
+ # by default in non-development environments). OAuth2 delegates security in
+ # communication to the HTTPS protocol so it is wise to keep this enabled.
+ #
+ # Callable objects such as proc, lambda, block or any object that responds to
+ # #call can be used in order to allow conditional checks (to allow non-SSL
+ # redirects to localhost for example).
+ #
+ # force_ssl_in_redirect_uri !Rails.env.development?
+ #
+ # force_ssl_in_redirect_uri { |uri| uri.host != 'localhost' }
+
+ # Specify what redirect URI's you want to block during creation. Any redirect
+ # URI is whitelisted by default.
+ #
+ # You can use this option in order to forbid URI's with 'javascript' scheme
+ # for example.
+ #
+ # forbid_redirect_uri { |uri| uri.scheme.to_s.downcase == 'javascript' }
+
+ # Specify what grant flows are enabled in array of Strings. The valid
+ # strings and the flows they enable are:
+ #
+ # "authorization_code" => Authorization Code Grant Flow
+ # "implicit" => Implicit Grant Flow
+ # "password" => Resource Owner Password Credentials Grant Flow
+ # "client_credentials" => Client Credentials Grant Flow
+ #
+ # If not specified, Doorkeeper enables authorization_code and
+ # client_credentials.
+ #
+ # implicit and password grant flows have risks that you should understand
+ # before enabling:
+ # http://tools.ietf.org/html/rfc6819#section-4.4.2
+ # http://tools.ietf.org/html/rfc6819#section-4.4.3
+ #
+ grant_flows %w(authorization_code)
+
+ # Hook into the strategies' request & response life-cycle in case your
+ # application needs advanced customization or logging:
+ #
+ # before_successful_strategy_response do |request|
+ # puts "BEFORE HOOK FIRED! #{request}"
+ # end
+ #
+ # after_successful_strategy_response do |request, response|
+ # puts "AFTER HOOK FIRED! #{request}, #{response}"
+ # end
+
+ # Under some circumstances you might want to have applications auto-approved,
+ # so that the user skips the authorization step.
+ # For example if dealing with a trusted application.
+ # skip_authorization do |resource_owner, client|
+ # client.superapp? or resource_owner.admin?
+ # end
+
+ # WWW-Authenticate Realm (default "Doorkeeper").
+ # realm "Doorkeeper"
+end
diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml
new file mode 100644
index 000000000..e475b3712
--- /dev/null
+++ b/config/locales/doorkeeper.en.yml
@@ -0,0 +1,128 @@
+en:
+ activerecord:
+ attributes:
+ doorkeeper/application:
+ name: 'Name'
+ redirect_uri: 'Redirect URI'
+ errors:
+ models:
+ doorkeeper/application:
+ attributes:
+ redirect_uri:
+ fragment_present: 'cannot contain a fragment.'
+ invalid_uri: 'must be a valid URI.'
+ relative_uri: 'must be an absolute URI.'
+ secured_uri: 'must be an HTTPS/SSL URI.'
+ forbidden_uri: 'is forbidden by the server.'
+
+ doorkeeper:
+ applications:
+ confirmations:
+ destroy: 'Are you sure?'
+ buttons:
+ edit: 'Edit'
+ destroy: 'Destroy'
+ submit: 'Submit'
+ cancel: 'Cancel'
+ authorize: 'Authorize'
+ form:
+ error: 'Whoops! Check your form for possible errors'
+ help:
+ confidential: 'Application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.'
+ redirect_uri: 'Use one line per URI'
+ native_redirect_uri: 'Use %{native_redirect_uri} if you want to add localhost URIs for development purposes'
+ scopes: 'Separate scopes with spaces. Leave blank to use the default scopes.'
+ edit:
+ title: 'Edit application'
+ index:
+ title: 'Your applications'
+ new: 'New Application'
+ name: 'Name'
+ callback_url: 'Callback URL'
+ confidential: 'Confidential?'
+ confidentiality:
+ 'yes': 'Yes'
+ 'no': 'No'
+ new:
+ title: 'New Application'
+ show:
+ title: 'Application: %{name}'
+ application_id: 'Application Id'
+ secret: 'Secret'
+ scopes: 'Scopes'
+ confidential: 'Confidential'
+ callback_urls: 'Callback urls'
+ actions: 'Actions'
+
+ authorizations:
+ buttons:
+ authorize: 'Authorize'
+ deny: 'Deny'
+ error:
+ title: 'An error has occurred'
+ new:
+ title: 'Authorization required'
+ prompt: 'Authorize %{client_name} to use your account?'
+ able_to: 'This application will be able to'
+ show:
+ title: 'Authorization code'
+
+ authorized_applications:
+ confirmations:
+ revoke: 'Are you sure?'
+ buttons:
+ revoke: 'Revoke'
+ index:
+ title: 'Your authorized applications'
+ application: 'Application'
+ created_at: 'Created At'
+ date_format: '%Y-%m-%d %H:%M:%S'
+
+ errors:
+ messages:
+ # Common error messages
+ invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'
+ invalid_redirect_uri: "The requested redirect uri is malformed or doesn't match client redirect URI."
+ unauthorized_client: 'The client is not authorized to perform this request using this method.'
+ access_denied: 'The resource owner or authorization server denied the request.'
+ invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
+ server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'
+ temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'
+
+ # Configuration error messages
+ credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'
+ resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfigured.'
+
+ # Access grant errors
+ unsupported_response_type: 'The authorization server does not support this response type.'
+
+ # Access token errors
+ invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.'
+ invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'
+ unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.'
+
+ invalid_token:
+ revoked: "The access token was revoked"
+ expired: "The access token expired"
+ unknown: "The access token is invalid"
+
+ flash:
+ applications:
+ create:
+ notice: 'Application created.'
+ destroy:
+ notice: 'Application deleted.'
+ update:
+ notice: 'Application updated.'
+ authorized_applications:
+ destroy:
+ notice: 'Application revoked.'
+
+ layouts:
+ admin:
+ nav:
+ oauth2_provider: 'OAuth2 Provider'
+ applications: 'Applications'
+ home: 'Home'
+ application:
+ title: 'OAuth authorization required'
diff --git a/config/routes.rb b/config/routes.rb
index ddca6879a..624ce5778 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,7 @@
Rails.application.routes.draw do
+ use_doorkeeper do
+ skip_controllers :applications, :authorized_applications, :token_info
+ end
require 'subdomain'
def draw(routes_name)
@@ -539,7 +542,6 @@ Rails.application.routes.draw do
namespace :api, defaults: { format: 'json' } do
get 'health', to: 'api#health'
get 'status', to: 'api#status'
- post 'auth/token', to: 'api#authenticate'
namespace :v1 do
resources :teams, only: %i(index show) do
end
diff --git a/db/migrate/20180813120338_create_doorkeeper_tables.rb b/db/migrate/20180813120338_create_doorkeeper_tables.rb
new file mode 100644
index 000000000..d52ebe5fb
--- /dev/null
+++ b/db/migrate/20180813120338_create_doorkeeper_tables.rb
@@ -0,0 +1,71 @@
+class CreateDoorkeeperTables < ActiveRecord::Migration[5.1]
+ def change
+ create_table :oauth_applications do |t|
+ t.string :name, null: false
+ t.string :uid, null: false
+ t.string :secret, null: false
+ t.text :redirect_uri, null: false
+ t.string :scopes, null: false, default: ''
+ t.boolean :confidential, null: false, default: true
+ t.timestamps null: false
+ end
+
+ add_index :oauth_applications, :uid, unique: true
+
+ create_table :oauth_access_grants do |t|
+ t.integer :resource_owner_id, null: false
+ t.references :application, null: false
+ t.string :token, null: false
+ t.integer :expires_in, null: false
+ t.text :redirect_uri, null: false
+ t.datetime :created_at, null: false
+ t.datetime :revoked_at
+ t.string :scopes
+ end
+
+ add_index :oauth_access_grants, :token, unique: true
+ add_foreign_key :oauth_access_grants, :users, column: :resource_owner_id
+ add_foreign_key(
+ :oauth_access_grants,
+ :oauth_applications,
+ column: :application_id
+ )
+
+ create_table :oauth_access_tokens do |t|
+ t.integer :resource_owner_id
+ t.references :application
+
+ # If you use a custom token generator you may need to change this column
+ # from string to text, so that it accepts tokens larger than 255
+ # characters. More info on custom token generators in:
+ # https://github.com/doorkeeper-gem/doorkeeper/tree/v3.0.0.rc1#custom-access-token-generator
+ #
+ t.text :token, null: false
+ # t.string :token, null: false
+
+ t.string :refresh_token
+ t.integer :expires_in
+ t.datetime :revoked_at
+ t.datetime :created_at, null: false
+ t.string :scopes
+
+ # If there is a previous_refresh_token column,
+ # refresh tokens will be revoked after a related access token is used.
+ # If there is no previous_refresh_token column,
+ # previous tokens are revoked as soon as a new access token is created.
+ # Comment out this line if you'd rather have refresh tokens
+ # instantly revoked.
+ t.string :previous_refresh_token, null: false, default: ""
+ end
+
+ add_index :oauth_access_tokens, :token, unique: true
+ add_index :oauth_access_tokens, :resource_owner_id
+ add_index :oauth_access_tokens, :refresh_token, unique: true
+ add_foreign_key :oauth_access_tokens, :users, column: :resource_owner_id
+ add_foreign_key(
+ :oauth_access_tokens,
+ :oauth_applications,
+ column: :application_id
+ )
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index ce0aa9260..345e78dde 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180524091143) do
+ActiveRecord::Schema.define(version: 20180813120338) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -240,6 +240,47 @@ ActiveRecord::Schema.define(version: 20180524091143) do
t.index ["created_at"], name: "index_notifications_on_created_at"
end
+ create_table "oauth_access_grants", force: :cascade do |t|
+ t.integer "resource_owner_id", null: false
+ t.bigint "application_id", null: false
+ t.string "token", null: false
+ t.integer "expires_in", null: false
+ t.text "redirect_uri", null: false
+ t.datetime "created_at", null: false
+ t.datetime "revoked_at"
+ t.string "scopes"
+ t.index ["application_id"], name: "index_oauth_access_grants_on_application_id"
+ t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true
+ end
+
+ create_table "oauth_access_tokens", force: :cascade do |t|
+ t.integer "resource_owner_id"
+ t.bigint "application_id"
+ t.text "token", null: false
+ t.string "refresh_token"
+ t.integer "expires_in"
+ t.datetime "revoked_at"
+ t.datetime "created_at", null: false
+ t.string "scopes"
+ t.string "previous_refresh_token", default: "", null: false
+ t.index ["application_id"], name: "index_oauth_access_tokens_on_application_id"
+ t.index ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true
+ t.index ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id"
+ t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true
+ end
+
+ create_table "oauth_applications", force: :cascade do |t|
+ t.string "name", null: false
+ t.string "uid", null: false
+ t.string "secret", null: false
+ t.text "redirect_uri", null: false
+ t.string "scopes", default: "", null: false
+ t.boolean "confidential", default: true, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true
+ end
+
create_table "projects", id: :serial, force: :cascade do |t|
t.string "name", null: false
t.integer "visibility", default: 0, null: false
@@ -864,6 +905,10 @@ ActiveRecord::Schema.define(version: 20180524091143) do
add_foreign_key "my_modules", "users", column: "last_modified_by_id"
add_foreign_key "my_modules", "users", column: "restored_by_id"
add_foreign_key "notifications", "users", column: "generator_user_id"
+ add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id"
+ add_foreign_key "oauth_access_grants", "users", column: "resource_owner_id"
+ add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id"
+ add_foreign_key "oauth_access_tokens", "users", column: "resource_owner_id"
add_foreign_key "projects", "teams"
add_foreign_key "projects", "users", column: "archived_by_id"
add_foreign_key "projects", "users", column: "created_by_id"
diff --git a/docker-compose.yml b/docker-compose.yml
index 6d89be949..12186ebb6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,7 +2,7 @@ version: '2'
services:
db:
container_name: scinote_db_development
- image: postgres:9.4
+ image: postgres:9.6
volumes:
- scinote_development_postgres:/var/lib/postgresql/data