Replace scopes with constants in Authorization
This commit is contained in:
parent
28fc65ae26
commit
e55a0b0d0b
22 changed files with 57 additions and 104 deletions
|
|
@ -122,7 +122,7 @@ module Api
|
||||||
end
|
end
|
||||||
|
|
||||||
def scopes_as_space_seperated_values
|
def scopes_as_space_seperated_values
|
||||||
@scopes.map(&:name).join(" ")
|
@scopes.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_authorization_consent(approvedString)
|
def process_authorization_consent(approvedString)
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ module Api
|
||||||
authorization_endpoint: new_api_openid_connect_authorization_url,
|
authorization_endpoint: new_api_openid_connect_authorization_url,
|
||||||
token_endpoint: api_openid_connect_access_tokens_url,
|
token_endpoint: api_openid_connect_access_tokens_url,
|
||||||
userinfo_endpoint: api_openid_connect_user_info_url,
|
userinfo_endpoint: api_openid_connect_user_info_url,
|
||||||
jwks_uri: File.join(root_url, "api", "openid_connect", "jwks.json"),
|
jwks_uri: api_openid_connect_url,
|
||||||
scopes_supported: Api::OpenidConnect::Scope.pluck(:name),
|
scopes_supported: %w(openid read write),
|
||||||
response_types_supported: Api::OpenidConnect::OAuthApplication.available_response_types,
|
response_types_supported: Api::OpenidConnect::OAuthApplication.available_response_types,
|
||||||
request_object_signing_alg_values_supported: %i(HS256 HS384 HS512),
|
request_object_signing_alg_values_supported: %i(HS256 HS384 HS512),
|
||||||
subject_types_supported: %w(public pairwise),
|
subject_types_supported: %w(public pairwise),
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ module Api
|
||||||
include Api::OpenidConnect::ProtectedResourceEndpoint
|
include Api::OpenidConnect::ProtectedResourceEndpoint
|
||||||
|
|
||||||
before_action do
|
before_action do
|
||||||
require_access_token Api::OpenidConnect::Scope.find_by(name: "openid")
|
require_access_token ["openid"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ module Api
|
||||||
validates :user, presence: true
|
validates :user, presence: true
|
||||||
validates :o_auth_application, presence: true
|
validates :o_auth_application, presence: true
|
||||||
validates :user, uniqueness: {scope: :o_auth_application}
|
validates :user, uniqueness: {scope: :o_auth_application}
|
||||||
|
validate :validate_scope_names
|
||||||
|
serialize :scopes, JSON
|
||||||
|
|
||||||
has_many :authorization_scopes
|
|
||||||
has_many :scopes, through: :authorization_scopes
|
|
||||||
has_many :o_auth_access_tokens, dependent: :destroy
|
has_many :o_auth_access_tokens, dependent: :destroy
|
||||||
has_many :id_tokens, dependent: :destroy
|
has_many :id_tokens, dependent: :destroy
|
||||||
|
|
||||||
|
|
@ -21,21 +21,28 @@ module Api
|
||||||
self.refresh_token = SecureRandom.hex(32)
|
self.refresh_token = SecureRandom.hex(32)
|
||||||
end
|
end
|
||||||
|
|
||||||
def accessible?(required_scopes=nil)
|
def validate_scope_names
|
||||||
Array(required_scopes).all? do |required_scope|
|
return unless scopes
|
||||||
scopes.include? required_scope
|
scopes.each do |scope|
|
||||||
|
errors.add(:scope, "is not a valid scope name") unless %w(openid read write).include? scope
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def accessible?(required_scopes=nil)
|
||||||
|
Array(required_scopes).all? { |required_scope|
|
||||||
|
scopes.include? required_scope
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def create_code
|
def create_code
|
||||||
self.code = SecureRandom.hex(32)
|
SecureRandom.hex(32).tap do |code|
|
||||||
save
|
self.code = code
|
||||||
code
|
save
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_access_token
|
def create_access_token
|
||||||
o_auth_access_tokens.create!.bearer_token
|
o_auth_access_tokens.create!.bearer_token
|
||||||
# TODO: Add support for request object
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_id_token
|
def create_id_token
|
||||||
|
|
@ -53,9 +60,12 @@ module Api
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.use_code(code)
|
def self.use_code(code)
|
||||||
auth = find_by(code: code)
|
return unless code
|
||||||
auth.code = nil if auth # Remove auth code if found so it can't be reused
|
find_by(code: code).tap do |auth|
|
||||||
auth
|
return unless auth
|
||||||
|
auth.code = nil
|
||||||
|
auth.save
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ class UserApplicationsPresenter
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_applications
|
def user_applications
|
||||||
|
# TODO: Fix and add tests
|
||||||
@applications ||= @current_user.o_auth_applications.each_with_object([]) do |app, array|
|
@applications ||= @current_user.o_auth_applications.each_with_object([]) do |app, array|
|
||||||
array << app_as_json(app)
|
array << app_as_json(app)
|
||||||
end
|
end
|
||||||
|
|
@ -29,9 +30,8 @@ class UserApplicationsPresenter
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_scopes(application)
|
def find_scopes(application)
|
||||||
scopes = Api::OpenidConnect::Authorization.find_by_client_id_and_user(
|
Api::OpenidConnect::Authorization.find_by_client_id_and_user(
|
||||||
application.client_id, @current_user).scopes
|
application.client_id, @current_user).scopes
|
||||||
scopes.each_with_object([]) {|scope, array| array << scope.name }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_id(application)
|
def find_id(application)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
= t(".with_id_token")
|
= t(".with_id_token")
|
||||||
%ul
|
%ul
|
||||||
- @scopes.each do |scope|
|
- @scopes.each do |scope|
|
||||||
%li= scope.name
|
%li= scope
|
||||||
- if @request_object
|
- if @request_object
|
||||||
%li= t(".requested_objects")
|
%li= t(".requested_objects")
|
||||||
%ul
|
%ul
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ class CreateAuthorizations < ActiveRecord::Migration
|
||||||
t.string :code
|
t.string :code
|
||||||
t.string :redirect_uri
|
t.string :redirect_uri
|
||||||
t.string :nonce
|
t.string :nonce
|
||||||
|
t.string :scopes
|
||||||
|
|
||||||
t.timestamps null: false
|
t.timestamps null: false
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
class CreateScope < ActiveRecord::Migration
|
|
||||||
def change
|
|
||||||
create_table :scopes do |t|
|
|
||||||
t.primary_key :name, :string
|
|
||||||
|
|
||||||
t.timestamps null: false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
class CreateAuthorizationScopesJoinTable < ActiveRecord::Migration
|
|
||||||
def change
|
|
||||||
create_table :authorization_scopes, id: false do |t|
|
|
||||||
t.belongs_to :authorization, index: true
|
|
||||||
t.belongs_to :scope, index: true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
15
db/schema.rb
15
db/schema.rb
|
|
@ -55,14 +55,6 @@ ActiveRecord::Schema.define(version: 20150801074555) do
|
||||||
add_index "aspects", ["user_id", "contacts_visible"], name: "index_aspects_on_user_id_and_contacts_visible", using: :btree
|
add_index "aspects", ["user_id", "contacts_visible"], name: "index_aspects_on_user_id_and_contacts_visible", using: :btree
|
||||||
add_index "aspects", ["user_id"], name: "index_aspects_on_user_id", using: :btree
|
add_index "aspects", ["user_id"], name: "index_aspects_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "authorization_scopes", id: false, force: :cascade do |t|
|
|
||||||
t.integer "authorization_id", limit: 4
|
|
||||||
t.integer "scope_id", limit: 4
|
|
||||||
end
|
|
||||||
|
|
||||||
add_index "authorization_scopes", ["authorization_id"], name: "index_authorization_scopes_on_authorization_id", using: :btree
|
|
||||||
add_index "authorization_scopes", ["scope_id"], name: "index_authorization_scopes_on_scope_id", using: :btree
|
|
||||||
|
|
||||||
create_table "authorizations", force: :cascade do |t|
|
create_table "authorizations", force: :cascade do |t|
|
||||||
t.integer "user_id", limit: 4
|
t.integer "user_id", limit: 4
|
||||||
t.integer "o_auth_application_id", limit: 4
|
t.integer "o_auth_application_id", limit: 4
|
||||||
|
|
@ -70,6 +62,7 @@ ActiveRecord::Schema.define(version: 20150801074555) do
|
||||||
t.string "code", limit: 255
|
t.string "code", limit: 255
|
||||||
t.string "redirect_uri", limit: 255
|
t.string "redirect_uri", limit: 255
|
||||||
t.string "nonce", limit: 255
|
t.string "nonce", limit: 255
|
||||||
|
t.string "scopes", limit: 255
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
end
|
end
|
||||||
|
|
@ -532,12 +525,6 @@ ActiveRecord::Schema.define(version: 20150801074555) do
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "scopes", force: :cascade do |t|
|
|
||||||
t.string "name", limit: 255
|
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.datetime "updated_at", null: false
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table "services", force: :cascade do |t|
|
create_table "services", force: :cascade do |t|
|
||||||
t.string "type", limit: 127, null: false
|
t.string "type", limit: 127, null: false
|
||||||
t.integer "user_id", limit: 4, null: false
|
t.integer "user_id", limit: 4, null: false
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
Api::OpenidConnect::Scope.find_or_create_by!(name: "openid")
|
|
||||||
Api::OpenidConnect::Scope.find_or_create_by!(name: "read")
|
|
||||||
Api::OpenidConnect::Scope.find_or_create_by!(name: "write")
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
Feature: Access protected resources using auth code flow
|
Feature: Access protected resources using auth code flow
|
||||||
Background:
|
Background:
|
||||||
Given a user with username "kent"
|
Given a user with username "kent"
|
||||||
And all scopes exist
|
|
||||||
|
|
||||||
Scenario: Invalid client id to auth endpoint
|
Scenario: Invalid client id to auth endpoint
|
||||||
When I register a new client
|
When I register a new client
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
Feature: Access protected resources using implicit flow
|
Feature: Access protected resources using implicit flow
|
||||||
Background:
|
Background:
|
||||||
Given a user with username "kent"
|
Given a user with username "kent"
|
||||||
And all scopes exist
|
|
||||||
|
|
||||||
Scenario: Invalid client id to auth endpoint
|
Scenario: Invalid client id to auth endpoint
|
||||||
When I register a new client
|
When I register a new client
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
Given(/^all scopes exist$/) do
|
|
||||||
Api::OpenidConnect::Scope.find_or_create_by(name: "openid")
|
|
||||||
Api::OpenidConnect::Scope.find_or_create_by(name: "read")
|
|
||||||
end
|
|
||||||
|
|
||||||
Given /^a client with a provided picture exists for user "([^\"]*)"$/ do |email|
|
Given /^a client with a provided picture exists for user "([^\"]*)"$/ do |email|
|
||||||
app = FactoryGirl.create(:o_auth_application_with_image)
|
app = FactoryGirl.create(:o_auth_application_with_image)
|
||||||
user = User.find_by(email: email)
|
user = User.find_by(email: email)
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,10 @@ module Api
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_scopes(req)
|
def build_scopes(req)
|
||||||
@scopes = req.scope.map {|scope_name|
|
@scopes = req.scope.map {|scope|
|
||||||
OpenidConnect::Scope.where(name: scope_name).first.tap do |scope|
|
scope.tap do |scope_name|
|
||||||
req.invalid_scope! "Unknown scope: #{scope}" unless scope
|
# TODO: Use enum
|
||||||
|
req.invalid_scope! "Unknown scope: #{scope_name}" unless %w(openid read write).include? scope_name
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,23 @@ module Api
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Add support for request object
|
private
|
||||||
|
|
||||||
def approved!(req, res)
|
def approved!(req, res)
|
||||||
auth = OpenidConnect::Authorization.find_or_create_by(
|
auth = find_or_build_auth(req)
|
||||||
o_auth_application: @o_auth_application, user: @user, redirect_uri: @redirect_uri)
|
|
||||||
auth.nonce = req.nonce
|
|
||||||
auth.scopes << @scopes unless auth.scopes == @scopes
|
|
||||||
handle_approved_response_type(auth, req, res)
|
handle_approved_response_type(auth, req, res)
|
||||||
res.approve!
|
res.approve!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_or_build_auth(req)
|
||||||
|
OpenidConnect::Authorization.find_or_create_by!(
|
||||||
|
o_auth_application: @o_auth_application, user: @user, redirect_uri: @redirect_uri).tap do |auth|
|
||||||
|
auth.nonce = req.nonce
|
||||||
|
auth.scopes = @scopes
|
||||||
|
auth.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def handle_approved_response_type(auth, req, res)
|
def handle_approved_response_type(auth, req, res)
|
||||||
response_types = Array(req.response_type)
|
response_types = Array(req.response_type)
|
||||||
handle_approved_auth_code(auth, res, response_types)
|
handle_approved_auth_code(auth, res, response_types)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ module Api
|
||||||
module ProtectedResourceEndpoint
|
module ProtectedResourceEndpoint
|
||||||
attr_reader :current_token
|
attr_reader :current_token
|
||||||
|
|
||||||
def require_access_token(*required_scopes)
|
def require_access_token(required_scopes)
|
||||||
@current_token = request.env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN]
|
@current_token = request.env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN]
|
||||||
raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new("Unauthorized user") unless
|
raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new("Unauthorized user") unless
|
||||||
@current_token && @current_token.authorization
|
@current_token && @current_token.authorization
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ module Api
|
||||||
auth = Api::OpenidConnect::Authorization.with_redirect_uri(req.redirect_uri).use_code(req.code)
|
auth = Api::OpenidConnect::Authorization.with_redirect_uri(req.redirect_uri).use_code(req.code)
|
||||||
req.invalid_grant! if auth.blank?
|
req.invalid_grant! if auth.blank?
|
||||||
res.access_token = auth.create_access_token
|
res.access_token = auth.create_access_token
|
||||||
if auth.accessible?(Api::OpenidConnect::Scope.find_by!(name: "openid"))
|
if auth.accessible? "openid"
|
||||||
id_token = auth.create_id_token
|
id_token = auth.create_id_token
|
||||||
res.id_token = id_token.to_jwt(access_token: res.access_token)
|
res.id_token = id_token.to_jwt(access_token: res.access_token)
|
||||||
end
|
end
|
||||||
|
|
@ -32,16 +32,6 @@ module Api
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_auth_and_access_token(auth, req, res)
|
|
||||||
scope_list = req.scope.map { |scope_name|
|
|
||||||
OpenidConnect::Scope.find_by(name: scope_name).tap do |scope|
|
|
||||||
req.invalid_scope! "Unknown scope: #{scope}" unless scope
|
|
||||||
end
|
|
||||||
} # TODO: Check client scope permissions
|
|
||||||
auth.scopes << scope_list
|
|
||||||
res.access_token = auth.create_access_token
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_refresh_flow(req, res)
|
def handle_refresh_flow(req, res)
|
||||||
# Handle as if scope request was omitted even if provided.
|
# Handle as if scope request was omitted even if provided.
|
||||||
# See https://tools.ietf.org/html/rfc6749#section-6 for handling
|
# See https://tools.ietf.org/html/rfc6749#section-6 for handling
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ describe Api::OpenidConnect::AuthorizationsController, type: :controller do
|
||||||
context "when already authorized" do
|
context "when already authorized" do
|
||||||
let!(:auth) {
|
let!(:auth) {
|
||||||
Api::OpenidConnect::Authorization.find_or_create_by(o_auth_application: client, user: alice,
|
Api::OpenidConnect::Authorization.find_or_create_by(o_auth_application: client, user: alice,
|
||||||
redirect_uri: "http://localhost:3000/")
|
redirect_uri: "http://localhost:3000/", scopes: ["openid"])
|
||||||
}
|
}
|
||||||
|
|
||||||
context "when valid parameters are passed" do
|
context "when valid parameters are passed" do
|
||||||
|
|
|
||||||
|
|
@ -336,32 +336,19 @@ FactoryGirl.define do
|
||||||
factory :auth_with_read, class: Api::OpenidConnect::Authorization do
|
factory :auth_with_read, class: Api::OpenidConnect::Authorization do
|
||||||
o_auth_application
|
o_auth_application
|
||||||
user
|
user
|
||||||
|
scopes ["openid","read"]
|
||||||
after(:create) do |auth_with_read|
|
|
||||||
auth_with_read.scopes << [Api::OpenidConnect::Scope.find_or_create_by(name: "openid"),
|
|
||||||
Api::OpenidConnect::Scope.find_or_create_by(name: "read")]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :auth_with_read_and_ppid, class: Api::OpenidConnect::Authorization do
|
factory :auth_with_read_and_ppid, class: Api::OpenidConnect::Authorization do
|
||||||
association :o_auth_application, factory: :o_auth_application_with_ppid
|
association :o_auth_application, factory: :o_auth_application_with_ppid
|
||||||
user
|
user
|
||||||
|
scopes ["openid","read"]
|
||||||
after(:create) do |auth_with_read|
|
|
||||||
auth_with_read.scopes << [Api::OpenidConnect::Scope.find_or_create_by(name: "openid"),
|
|
||||||
Api::OpenidConnect::Scope.find_or_create_by(name: "read")]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :auth_with_read_and_write, class: Api::OpenidConnect::Authorization do
|
factory :auth_with_read_and_write, class: Api::OpenidConnect::Authorization do
|
||||||
o_auth_application
|
o_auth_application
|
||||||
user
|
user
|
||||||
|
scopes ["openid","read","write"]
|
||||||
after(:create) do |auth_with_read|
|
|
||||||
auth_with_read.scopes << [Api::OpenidConnect::Scope.find_or_create_by(name: "openid"),
|
|
||||||
Api::OpenidConnect::Scope.find_or_create_by(name: "read"),
|
|
||||||
Api::OpenidConnect::Scope.find_or_create_by(name: "write")]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Factories for the DiasporaFederation-gem
|
# Factories for the DiasporaFederation-gem
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
require "spec_helper"
|
require "spec_helper"
|
||||||
describe Api::OpenidConnect::TokenEndpoint, type: :request do
|
describe Api::OpenidConnect::TokenEndpoint, type: :request do
|
||||||
let!(:client) { FactoryGirl.create(:o_auth_application_with_ppid) }
|
let!(:client) { FactoryGirl.create(:o_auth_application_with_ppid) }
|
||||||
let!(:auth) do
|
let!(:auth) {
|
||||||
auth = Api::OpenidConnect::Authorization.find_or_create_by(
|
Api::OpenidConnect::Authorization.find_or_create_by(
|
||||||
o_auth_application: client, user: bob, redirect_uri: "http://localhost:3000/")
|
o_auth_application: client, user: bob, redirect_uri: "http://localhost:3000/", scopes: ["openid"])
|
||||||
auth.scopes << [Api::OpenidConnect::Scope.find_by!(name: "openid")]
|
}
|
||||||
auth
|
|
||||||
end
|
|
||||||
let!(:code) { auth.create_code }
|
let!(:code) { auth.create_code }
|
||||||
|
|
||||||
describe "the authorization code grant type" do
|
describe "the authorization code grant type" do
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,6 @@ RSpec.configure do |config|
|
||||||
$process_queue = false
|
$process_queue = false
|
||||||
allow_any_instance_of(Postzord::Dispatcher::Public).to receive(:deliver_to_remote)
|
allow_any_instance_of(Postzord::Dispatcher::Public).to receive(:deliver_to_remote)
|
||||||
allow_any_instance_of(Postzord::Dispatcher::Private).to receive(:deliver_to_remote)
|
allow_any_instance_of(Postzord::Dispatcher::Private).to receive(:deliver_to_remote)
|
||||||
load "#{Rails.root}/db/seeds.rb"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
config.expect_with :rspec do |expect_config|
|
config.expect_with :rspec do |expect_config|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue