Redesign the models

This commit is contained in:
augier 2015-09-13 14:44:22 -07:00 committed by theworldbright
parent 9140c8244b
commit 031679762a
28 changed files with 181 additions and 147 deletions

View file

@ -17,7 +17,7 @@ class OpenidConnect::AuthorizationsController < ApplicationController
private private
def request_authorization_consent_form def request_authorization_consent_form
endpoint = OpenidConnect::Authorization::EndpointStartPoint.new(current_user) endpoint = OpenidConnect::Endpoints::EndpointStartPoint.new(current_user)
handle_startpoint_response(endpoint) handle_startpoint_response(endpoint)
end end
@ -35,7 +35,7 @@ class OpenidConnect::AuthorizationsController < ApplicationController
end end
def process_authorization_consent(approvedString) def process_authorization_consent(approvedString)
endpoint = OpenidConnect::Authorization::EndpointConfirmationPoint.new(current_user, to_boolean(approvedString)) endpoint = OpenidConnect::Endpoints::EndpointConfirmationPoint.new(current_user, to_boolean(approvedString))
restore_request_parameters(endpoint) restore_request_parameters(endpoint)
handle_confirmation_endpoint_response(endpoint) handle_confirmation_endpoint_response(endpoint)
end end
@ -56,7 +56,7 @@ class OpenidConnect::AuthorizationsController < ApplicationController
req.update_param("redirect_uri", session[:redirect_uri]) req.update_param("redirect_uri", session[:redirect_uri])
req.update_param("response_type", session[:response_type]) req.update_param("response_type", session[:response_type])
endpoint.scopes, endpoint.request_object = endpoint.scopes, endpoint.request_object =
session[:scopes].map {|scope| Scope.find_by_name(scope) }, session[:request_object] session[:scopes].map {|scope| OpenidConnect::Scope.find_by_name(scope) }, session[:request_object]
end end
def to_boolean(str) def to_boolean(str)

View file

@ -9,7 +9,7 @@ class OpenidConnect::ClientsController < ApplicationController
def create def create
registrar = OpenIDConnect::Client::Registrar.new(request.url, params) registrar = OpenIDConnect::Client::Registrar.new(request.url, params)
client = OAuthApplication.register! registrar client = OpenidConnect::OAuthApplication.register! registrar
render json: client render json: client
end end

View file

@ -1,5 +1,4 @@
module OpenidConnect class OpenidConnect::DiscoveryController < ApplicationController
class DiscoveryController < ApplicationController
def webfinger def webfinger
jrd = { jrd = {
links: [{ links: [{
@ -28,5 +27,4 @@ module OpenidConnect
# TODO: claims_supported: ["sub", "iss", "name", "email"] # TODO: claims_supported: ["sub", "iss", "name", "email"]
) )
end end
end
end end

View file

@ -1,7 +0,0 @@
class Authorization < ActiveRecord::Base
belongs_to :user
belongs_to :o_auth_application
has_many :scopes, through: :authorization_scopes
# TODO: Incomplete class
end

View file

@ -0,0 +1,43 @@
class OpenidConnect::Authorization < ActiveRecord::Base
belongs_to :user
belongs_to :o_auth_application
has_many :scopes, through: :authorization_scopes
has_many :o_auth_access_tokens
before_validation :setup, on: :create
validates :refresh_token, uniqueness: true
validates :user, :o_auth_application, uniqueness: true
# TODO: Incomplete class
def setup
self.refresh_token = nil
end
def self.valid?(token)
OpenidConnect::Authorization.exists? refresh_token: token
end
def create_refresh_token
self.refresh_token = SecureRandom.hex(32)
end
def create_token
o_auth_access_tokens.create!.bearer_token
end
def self.find_by_client_id_and_user(client_id, user)
app = OpenidConnect::OAuthApplication.find_by(client_id: client_id)
find_by(o_auth_application: app, user: user)
end
def self.find_or_create(client_id, user)
auth = find_by_client_id_and_user client_id, user
unless auth
# TODO: Handle creation error
auth = create! user: user, o_auth_application: OpenidConnect::OAuthApplication.find_by(client_id: client_id)
end
auth
end
end

View file

@ -1,7 +1,7 @@
class Token < ActiveRecord::Base class OpenidConnect::OAuthAccessToken < ActiveRecord::Base
belongs_to :user belongs_to :user
belongs_to :authorization
has_many :scopes, through: :scope_tokens has_many :scopes, through: :scope_tokens
has_one :refresh_token
before_validation :setup, on: :create before_validation :setup, on: :create
@ -11,7 +11,6 @@ class Token < ActiveRecord::Base
def setup def setup
self.token = SecureRandom.hex(32) self.token = SecureRandom.hex(32)
self.refresh_token = RefreshToken.create!
self.expires_at = 24.hours.from_now self.expires_at = 24.hours.from_now
end end

View file

@ -1,7 +1,8 @@
class OAuthApplication < ActiveRecord::Base class OpenidConnect::OAuthApplication < ActiveRecord::Base
belongs_to :user belongs_to :user
has_many :authorizations has_many :authorizations
has_many :user, through: :authorizations
validates :client_id, presence: true, uniqueness: true validates :client_id, presence: true, uniqueness: true
validates :client_secret, presence: true validates :client_secret, presence: true
@ -9,6 +10,7 @@ class OAuthApplication < ActiveRecord::Base
serialize :redirect_uris, JSON serialize :redirect_uris, JSON
before_validation :setup, on: :create before_validation :setup, on: :create
def setup def setup
self.client_id = SecureRandom.hex(16) self.client_id = SecureRandom.hex(16)
self.client_secret = SecureRandom.hex(32) self.client_secret = SecureRandom.hex(32)

View file

@ -1,4 +1,4 @@
class Scope < ActiveRecord::Base class OpenidConnect::Scope < ActiveRecord::Base
has_many :tokens, through: :scope_tokens has_many :tokens, through: :scope_tokens
has_many :authorizations, through: :authorization_scopes has_many :authorizations, through: :authorization_scopes

View file

@ -1,4 +1,4 @@
class ScopeToken < ActiveRecord::Base class OpenidConnect::ScopeToken < ActiveRecord::Base
belongs_to :scope belongs_to :scope
belongs_to :token belongs_to :token

View file

@ -1,26 +0,0 @@
class RefreshToken < ActiveRecord::Base
belongs_to :token
before_validation :setup, on: :create
validates :refresh_token, presence: true, uniqueness: true
attr_reader :refresh_token
def setup
self.refresh_token = SecureRandom.hex(32)
# No expipration date for now
end
# Finds the requested refresh token and destroys it if found; returns true if found, false otherwise
def valid?(token)
the_token = RefreshToken.find_by_refresh_token token
if the_token
RefreshToken.destroy_all refresh_token: the_token.refresh_token
Token.destroy_all refresh_token: the_token.refresh_token
true
else
false
end
end
end

View file

@ -76,9 +76,9 @@ class User < ActiveRecord::Base
has_many :reports has_many :reports
has_many :o_auth_applications has_many :authorizations, class_name: 'OpenidConnect::Authorization'
has_many :authorizations has_many :o_auth_applications, through: :authorizations, class_name: 'OpenidConnect::OAuthApplication'
has_many :tokens has_many :o_auth_access_tokens, through: :authorizations, class_name: 'OpenidConnect::OAuthAccessToken'
before_save :guard_unconfirmed_email, before_save :guard_unconfirmed_email,
:save_person! :save_person!
@ -602,15 +602,17 @@ class User < ActiveRecord::Base
end end
end end
def find_authorization_by_client_id(client_id)
OpenidConnect::Authorization.find_by_client_id_and_user client_id, self
end
private private
def clearable_fields def clearable_fields
self.attributes.keys - ["id", "username", "encrypted_password", self.attributes.keys - %w(id username encrypted_password created_at updated_at locked_at
"created_at", "updated_at", "locked_at", serialized_private_key getting_started
"serialized_private_key", "getting_started", disable_mail show_community_spotlight_in_stream
"disable_mail", "show_community_spotlight_in_stream", strip_exif email remove_after export exporting exported_at
"strip_exif", "email", "remove_after", exported_photos_file exporting_photos exported_photos_at)
"export", "exporting", "exported_at",
"exported_photos_file", "exporting_photos", "exported_photos_at"]
end end
end end

View file

@ -109,7 +109,7 @@ module Diaspora
config.action_mailer.asset_host = AppConfig.pod_uri.to_s config.action_mailer.asset_host = AppConfig.pod_uri.to_s
config.middleware.use Rack::OAuth2::Server::Resource::Bearer, "OpenID Connect" do |req| config.middleware.use Rack::OAuth2::Server::Resource::Bearer, "OpenID Connect" do |req|
Token.valid(Time.now.utc).find_by(token: req.access_token) || req.invalid_token! OpenidConnect::OAuthAccessToken.valid(Time.now.utc).find_by(token: req.access_token) || req.invalid_token!
end end
end end
end end

View file

@ -0,0 +1,13 @@
class CreateOAuthAccessTokens < ActiveRecord::Migration
def self.up
create_table :o_auth_access_tokens do |t|
t.belongs_to :user, index: true
t.belongs_to :authorizations
t.belongs_to :endpoints
t.string :token
t.datetime :expires_at
t.timestamps null: false
end
end
end

View file

@ -1,15 +0,0 @@
class CreateTokens < ActiveRecord::Migration
def self.up
create_table :tokens do |t|
t.belongs_to :user, index: true
t.string :token
t.datetime :expires_at
t.timestamps null: false
end
end
def self.down
drop_table :tokens
end
end

View file

@ -3,6 +3,7 @@ class CreateAuthorizations < ActiveRecord::Migration
create_table :authorizations do |t| create_table :authorizations do |t|
t.belongs_to :user, index: true t.belongs_to :user, index: true
t.belongs_to :o_auth_application, index: true t.belongs_to :o_auth_application, index: true
t.string :refresh_token
t.timestamps null: false t.timestamps null: false
end end

View file

@ -1,7 +1,7 @@
class CreateAuthorizationsScopesJoinTable < ActiveRecord::Migration class CreateAuthorizationsScopesJoinTable < ActiveRecord::Migration
def change def change
create_table :authorizations_scopes, id: false do |t| create_table :authorizations_scopes, id: false do |t|
t.belongs_to :authorization, index: true t.belongs_to :endpoints, index: true
t.belongs_to :scope, index: true t.belongs_to :scope, index: true
end end
end end

View file

@ -2,7 +2,7 @@ class CreateScopesTokensJoinTable < ActiveRecord::Migration
def change def change
create_table :scopes_tokens, id: false do |t| create_table :scopes_tokens, id: false do |t|
t.belongs_to :scope, index: true t.belongs_to :scope, index: true
t.belongs_to :token, index: true t.belongs_to :o_auth_access_token, index: true
end end
end end
end end

View file

@ -1,14 +0,0 @@
class RefreshToken < ActiveRecord::Migration
def change
create_table :refresh_token do
t.belongs_to :token
t.string :refresh_token
t.timestamps null: false
end
end
def self.down
drop_table :refresh_token
end
end

View file

@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20151003142048) do ActiveRecord::Schema.define(version: 20150708155747) do
create_table "account_deletions", force: :cascade do |t| create_table "account_deletions", force: :cascade do |t|
t.string "diaspora_handle", limit: 255 t.string "diaspora_handle", limit: 255
@ -55,6 +55,25 @@ ActiveRecord::Schema.define(version: 20151003142048) 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 "authorizations", force: :cascade do |t|
t.integer "user_id", limit: 4
t.integer "o_auth_application_id", limit: 4
t.string "refresh_token", limit: 255
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "authorizations", ["o_auth_application_id"], name: "index_authorizations_on_o_auth_application_id", using: :btree
add_index "authorizations", ["user_id"], name: "index_authorizations_on_user_id", using: :btree
create_table "authorizations_scopes", id: false, force: :cascade do |t|
t.integer "authorization_id", limit: 4
t.integer "scope_id", limit: 4
end
add_index "authorizations_scopes", ["authorization_id"], name: "index_authorizations_scopes_on_authorization_id", using: :btree
add_index "authorizations_scopes", ["scope_id"], name: "index_authorizations_scopes_on_scope_id", using: :btree
create_table "blocks", force: :cascade do |t| create_table "blocks", force: :cascade do |t|
t.integer "user_id", limit: 4 t.integer "user_id", limit: 4
t.integer "person_id", limit: 4 t.integer "person_id", limit: 4
@ -236,6 +255,17 @@ ActiveRecord::Schema.define(version: 20151003142048) do
add_index "notifications", ["target_id"], name: "index_notifications_on_target_id", using: :btree add_index "notifications", ["target_id"], name: "index_notifications_on_target_id", using: :btree
add_index "notifications", ["target_type", "target_id"], name: "index_notifications_on_target_type_and_target_id", length: {"target_type"=>190, "target_id"=>nil}, using: :btree add_index "notifications", ["target_type", "target_id"], name: "index_notifications_on_target_type_and_target_id", length: {"target_type"=>190, "target_id"=>nil}, using: :btree
create_table "o_auth_access_tokens", force: :cascade do |t|
t.integer "user_id", limit: 4
t.integer "authorization_id", limit: 4
t.string "token", limit: 255
t.datetime "expires_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "o_auth_access_tokens", ["user_id"], name: "index_o_auth_access_tokens_on_user_id", using: :btree
create_table "o_auth_applications", force: :cascade do |t| create_table "o_auth_applications", force: :cascade do |t|
t.integer "user_id", limit: 4 t.integer "user_id", limit: 4
t.string "client_id", limit: 255 t.string "client_id", limit: 255
@ -470,6 +500,20 @@ ActiveRecord::Schema.define(version: 20151003142048) 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 "scopes_tokens", id: false, force: :cascade do |t|
t.integer "scope_id", limit: 4
t.integer "o_auth_access_token_id", limit: 4
end
add_index "scopes_tokens", ["o_auth_access_token_id"], name: "index_scopes_tokens_on_o_auth_access_token_id", using: :btree
add_index "scopes_tokens", ["scope_id"], name: "index_scopes_tokens_on_scope_id", using: :btree
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
@ -540,16 +584,6 @@ ActiveRecord::Schema.define(version: 20151003142048) do
add_index "tags", ["name"], name: "index_tags_on_name", unique: true, length: {"name"=>191}, using: :btree add_index "tags", ["name"], name: "index_tags_on_name", unique: true, length: {"name"=>191}, using: :btree
create_table "tokens", force: :cascade do |t|
t.integer "user_id", limit: 4
t.string "token", limit: 255
t.datetime "expires_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "tokens", ["user_id"], name: "index_tokens_on_user_id", using: :btree
create_table "user_preferences", force: :cascade do |t| create_table "user_preferences", force: :cascade do |t|
t.string "email_type", limit: 255 t.string "email_type", limit: 255
t.integer "user_id", limit: 4 t.integer "user_id", limit: 4

View file

@ -47,15 +47,16 @@ class AccountDeleter
#user deletions #user deletions
def normal_ar_user_associates_to_delete def normal_ar_user_associates_to_delete
%i(tag_followings invitations_to_me services aspects user_preferences %i(tag_followings invitations_to_me services aspects user_preferences
notifications blocks authorizations o_auth_applications tokens) notifications blocks authorizations o_auth_applications o_auth_access_tokens)
end end
def special_ar_user_associations def special_ar_user_associations
[:invitations_from_me, :person, :profile, :contacts, :auto_follow_back_aspect] %i(invitations_from_me person profile contacts auto_follow_back_aspect)
end end
def ignored_ar_user_associations def ignored_ar_user_associations
[:followed_tags, :invited_by, :contact_people, :aspect_memberships, :ignored_people, :conversation_visibilities, :conversations, :reports] %i(followed_tags invited_by contact_people aspect_memberships
ignored_people conversation_visibilities conversations reports)
end end
def delete_standard_user_associations def delete_standard_user_associations

View file

@ -1,5 +1,5 @@
module OpenidConnect module OpenidConnect
module Authorization module Endpoints
class Endpoint class Endpoint
attr_accessor :app, :user, :client, :redirect_uri, :response_type, attr_accessor :app, :user, :client, :redirect_uri, :response_type,
:scopes, :_request_, :request_uri, :request_object :scopes, :_request_, :request_uri, :request_object
@ -9,7 +9,8 @@ module OpenidConnect
@user = current_user @user = current_user
@app = Rack::OAuth2::Server::Authorize.new do |req, res| @app = Rack::OAuth2::Server::Authorize.new do |req, res|
build_attributes(req, res) build_attributes(req, res)
if OAuthApplication.available_response_types.include? Array(req.response_type).map(&:to_s).join(" ") if OpenidConnect::OAuthApplication.available_response_types.include?(
Array(req.response_type).map(&:to_s).join(" "))
handle_response_type(req, res) handle_response_type(req, res)
else else
req.unsupported_response_type! req.unsupported_response_type!
@ -29,7 +30,7 @@ module OpenidConnect
private private
def build_client(req) def build_client(req)
@client = OAuthApplication.find_by_client_id(req.client_id) || req.bad_request! @client = OpenidConnect::OAuthApplication.find_by_client_id(req.client_id) || req.bad_request!
end end
def build_redirect_uri(req, res) def build_redirect_uri(req, res)

View file

@ -1,5 +1,5 @@
module OpenidConnect module OpenidConnect
module Authorization module Endpoints
class EndpointConfirmationPoint < Endpoint class EndpointConfirmationPoint < Endpoint
def initialize(current_user, approved=false) def initialize(current_user, approved=false)
super(current_user) super(current_user)

View file

@ -1,5 +1,5 @@
module OpenidConnect module OpenidConnect
module Authorization module Endpoints
class EndpointStartPoint < Endpoint class EndpointStartPoint < Endpoint
def initialize(current_user) def initialize(current_user)
super(current_user) super(current_user)
@ -24,7 +24,7 @@ module OpenidConnect
def build_scopes(req) def build_scopes(req)
@scopes = req.scope.map {|scope| @scopes = req.scope.map {|scope|
Scope.where(name: scope).first.tap do |scope| OpenidConnect::Scope.where(name: scope).first.tap do |scope|
req.invalid_scope! "Unknown scope: #{scope}" unless scope req.invalid_scope! "Unknown scope: #{scope}" unless scope
end end
} }

View file

@ -29,7 +29,8 @@ module OpenidConnect
user = User.find_for_database_authentication(username: req.username) user = User.find_for_database_authentication(username: req.username)
if user if user
if user.valid_password?(req.password) if user.valid_password?(req.password)
res.access_token = token! user auth = OpenidConnect::Authorization.find_or_create(req.client_id, user)
res.access_token = auth.create_token
else else
req.invalid_grant! req.invalid_grant!
end end
@ -39,24 +40,21 @@ module OpenidConnect
end end
def handle_refresh_flow(req, res) def handle_refresh_flow(req, res)
user = OAuthApplication.find_by_client_id(req.client_id).user auth = OpenidConnect::Authorization.find_by_client_id req.client_id
if RefreshToken.valid?(req.refresh_token) if OpenidConnect::Authorization.valid? req.refresh_token
res.access_token = token! user res.access_token = auth.create_token
else else
req.invalid_grant! req.invalid_grant!
end end
end end
def retrieve_client(req) def retrieve_client(req)
OAuthApplication.find_by_client_id req.client_id OpenidConnect::OAuthApplication.find_by client_id: req.client_id
end end
def app_valid?(o_auth_app, req) def app_valid?(o_auth_app, req)
o_auth_app.client_secret == req.client_secret o_auth_app.client_secret == req.client_secret
end end
def token!(user)
user.tokens.create!.bearer_token
end
end end
end end

View file

@ -1,16 +1,16 @@
require "spec_helper" require "spec_helper"
describe OpenidConnect::AuthorizationsController, type: :controller do describe OpenidConnect::AuthorizationsController, type: :controller do
let!(:client) { OAuthApplication.create!(name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/"]) } let!(:client) { OpenidConnect::OAuthApplication.create!(name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/"]) }
let!(:client_with_multiple_redirects) do let!(:client_with_multiple_redirects) do
OAuthApplication.create!( OpenidConnect::OAuthApplication.create!(
name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/", "http://localhost/"]) name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/", "http://localhost/"])
end end
before do before do
sign_in :user, alice sign_in :user, alice
allow(@controller).to receive(:current_user).and_return(alice) allow(@controller).to receive(:current_user).and_return(alice)
Scope.create!(name: "openid") OpenidConnect::Scope.create!(name: "openid")
end end
describe "#new" do describe "#new" do

View file

@ -2,7 +2,11 @@ require "spec_helper"
describe OpenidConnect::ProtectedResourceEndpoint, type: :request do describe OpenidConnect::ProtectedResourceEndpoint, type: :request do
describe "getting the user info" do describe "getting the user info" do
let!(:token) { bob.tokens.create!.bearer_token.to_s } let!(:client) do
OpenidConnect::OAuthApplication.create!(name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/"])
end
let(:auth) { OpenidConnect::Authorization.find_or_create(client.client_id, bob) }
let!(:token) { auth.create_token.to_s }
let(:invalid_token) { SecureRandom.hex(32).to_s } let(:invalid_token) { SecureRandom.hex(32).to_s }
# TODO: Add tests for expired access tokens # TODO: Add tests for expired access tokens

View file

@ -1,7 +1,7 @@
require "spec_helper" require "spec_helper"
describe OpenidConnect::TokenEndpoint, type: :request do describe OpenidConnect::TokenEndpoint, type: :request do
let!(:client) { OAuthApplication.create!(redirect_uris: ["http://localhost"]) } let!(:client) { OpenidConnect::OAuthApplication.create!(redirect_uris: ["http://localhost"]) }
describe "the password grant type" do describe "the password grant type" do
context "when the username field is missing" do context "when the username field is missing" do
it "should return an invalid request error" do it "should return an invalid request error" do