Compare commits

...

4 Commits

Author SHA1 Message Date
Matt Jankowski
f46dbef477
Merge 4393bd395a into 3b52dca405 2025-07-11 17:06:11 +00:00
Claire
3b52dca405
Fix quote attributes missing from Mastodon's context (#35354)
Some checks are pending
Check i18n / check-i18n (push) Waiting to run
Chromatic / Run Chromatic (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Check formatting / lint (push) Waiting to run
JavaScript Linting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
JavaScript Testing / test (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (.ruby-version) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.2) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
2025-07-11 16:35:06 +00:00
Echo
853a0c466e
Make bio hashtags open the local page instead of the remote instance (#35349) 2025-07-11 15:18:34 +00:00
Matt Jankowski
4393bd395a Convert to doorkeeper ORM mixin style for OAuth models 2025-07-03 18:28:24 -04:00
44 changed files with 224 additions and 174 deletions

View File

@ -4,7 +4,7 @@ class Api::V1::AppsController < Api::BaseController
skip_before_action :require_authenticated_user!
def create
@app = Doorkeeper::Application.create!(application_options)
@app = OAuth::Application.create!(application_options)
render json: @app, serializer: REST::CredentialApplicationSerializer
end

View File

@ -17,7 +17,7 @@ class OAuth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
def destroy
Web::PushSubscription.unsubscribe_for(params[:id], current_resource_owner)
Doorkeeper::Application.find_by(id: params[:id])&.close_streaming_sessions(current_resource_owner)
OAuth::Application.find_by(id: params[:id])&.close_streaming_sessions(current_resource_owner)
super
end

View File

@ -10,7 +10,7 @@ class Settings::ApplicationsController < Settings::BaseController
def show; end
def new
@application = Doorkeeper::Application.new(
@application = OAuth::Application.new(
redirect_uri: Doorkeeper.configuration.native_redirect_uri,
scopes: 'profile'
)
@ -59,6 +59,6 @@ class Settings::ApplicationsController < Settings::BaseController
end
def application_params
params.expect(doorkeeper_application: [:name, :redirect_uri, :website, scopes: []])
params.expect(oauth_application: [:name, :redirect_uri, :website, scopes: []])
end
end

View File

@ -26,6 +26,12 @@ module ContextHelper
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
attribution_domains: { 'toot' => 'http://joinmastodon.org/ns#', 'attributionDomains' => { '@id' => 'toot:attributionDomains', '@type' => '@id' } },
quote_requests: { 'QuoteRequest' => 'https://w3id.org/fep/044f#QuoteRequest' },
quotes: {
'quote' => 'https://w3id.org/fep/044f#quote',
'quoteUri' => 'http://fedibird.com/ns#quoteUri',
'_misskey_quote' => 'https://misskey-hub.net/ns#_misskey_quote',
'quoteAuthorization' => 'https://w3id.org/fep/044f#quoteAuthorization',
},
interaction_policies: {
'gts' => 'https://gotosocial.org/ns#',
'interactionPolicy' => { '@id' => 'gts:interactionPolicy', '@type' => '@id' },

View File

@ -1,12 +1,30 @@
import { useCallback } from 'react';
import { useLinks } from 'mastodon/hooks/useLinks';
export const AccountBio: React.FC<{
interface AccountBioProps {
note: string;
className: string;
}> = ({ note, className }) => {
const handleClick = useLinks();
dropdownAccountId?: string;
}
if (note.length === 0 || note === '<p></p>') {
export const AccountBio: React.FC<AccountBioProps> = ({
note,
className,
dropdownAccountId,
}) => {
const handleClick = useLinks(!!dropdownAccountId);
const handleNodeChange = useCallback(
(node: HTMLDivElement | null) => {
if (!dropdownAccountId || !node || node.childNodes.length === 0) {
return;
}
addDropdownToHashtags(node, dropdownAccountId);
},
[dropdownAccountId],
);
if (note.length === 0) {
return null;
}
@ -15,6 +33,28 @@ export const AccountBio: React.FC<{
className={`${className} translate`}
dangerouslySetInnerHTML={{ __html: note }}
onClickCapture={handleClick}
ref={handleNodeChange}
/>
);
};
function addDropdownToHashtags(node: HTMLElement | null, accountId: string) {
if (!node) {
return;
}
for (const childNode of node.childNodes) {
if (!(childNode instanceof HTMLElement)) {
continue;
}
if (
childNode instanceof HTMLAnchorElement &&
(childNode.classList.contains('hashtag') ||
childNode.innerText.startsWith('#')) &&
!childNode.dataset.menuHashtag
) {
childNode.dataset.menuHashtag = accountId;
} else if (childNode.childNodes.length > 0) {
addDropdownToHashtags(childNode, accountId);
}
}
}

View File

@ -6,6 +6,7 @@ import classNames from 'classnames';
import { Helmet } from 'react-helmet';
import { NavLink } from 'react-router-dom';
import { AccountBio } from '@/mastodon/components/account_bio';
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
@ -773,7 +774,6 @@ export const AccountHeader: React.FC<{
);
}
const content = { __html: account.note_emojified };
const displayNameHtml = { __html: account.display_name_html };
const fields = account.fields;
const isLocal = !account.acct.includes('@');
@ -897,12 +897,11 @@ export const AccountHeader: React.FC<{
<AccountNote accountId={accountId} />
)}
{account.note.length > 0 && account.note !== '<p></p>' && (
<div
className='account__header__content translate'
dangerouslySetInnerHTML={content}
<AccountBio
note={account.note_emojified}
dropdownAccountId={accountId}
className='account__header__content'
/>
)}
<div className='account__header__fields'>
<dl>

View File

@ -8,13 +8,14 @@ import { openURL } from 'mastodon/actions/search';
import { useAppDispatch } from 'mastodon/store';
const isMentionClick = (element: HTMLAnchorElement) =>
element.classList.contains('mention');
element.classList.contains('mention') &&
!element.classList.contains('hashtag');
const isHashtagClick = (element: HTMLAnchorElement) =>
element.textContent?.[0] === '#' ||
element.previousSibling?.textContent?.endsWith('#');
export const useLinks = () => {
export const useLinks = (skipHashtags?: boolean) => {
const history = useHistory();
const dispatch = useAppDispatch();
@ -61,12 +62,12 @@ export const useLinks = () => {
if (isMentionClick(target)) {
e.preventDefault();
void handleMentionClick(target);
} else if (isHashtagClick(target)) {
} else if (isHashtagClick(target) && !skipHashtags) {
e.preventDefault();
handleHashtagClick(target);
}
},
[handleMentionClick, handleHashtagClick],
[skipHashtags, handleMentionClick, handleHashtagClick],
);
return handleClick;

View File

@ -126,6 +126,9 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) {
? accountJSON.username
: accountJSON.display_name;
const accountNote =
accountJSON.note && accountJSON.note !== '<p></p>' ? accountJSON.note : '';
return AccountFactory({
...accountJSON,
moved: moved?.id,
@ -142,8 +145,8 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) {
escapeTextContentForBrowser(displayName),
emojiMap,
),
note_emojified: emojify(accountJSON.note, emojiMap),
note_plain: unescapeHTML(accountJSON.note),
note_emojified: emojify(accountNote, emojiMap),
note_plain: unescapeHTML(accountNote),
url:
accountJSON.url.startsWith('http://') ||
accountJSON.url.startsWith('https://')

View File

@ -1,10 +0,0 @@
# frozen_string_literal: true
module AccessGrantExtension
extend ActiveSupport::Concern
included do
scope :expired, -> { where.not(expires_in: nil).where('created_at + MAKE_INTERVAL(secs => expires_in) < NOW()') }
scope :revoked, -> { where.not(revoked_at: nil).where(revoked_at: ...Time.now.utc) }
end
end

View File

@ -1,29 +0,0 @@
# frozen_string_literal: true
module AccessTokenExtension
extend ActiveSupport::Concern
included do
include Redisable
has_many :web_push_subscriptions, class_name: 'Web::PushSubscription', inverse_of: :access_token
after_commit :push_to_streaming_api
scope :expired, -> { where.not(expires_in: nil).where('created_at + MAKE_INTERVAL(secs => expires_in) < NOW()') }
scope :not_revoked, -> { where(revoked_at: nil) }
scope :revoked, -> { where.not(revoked_at: nil).where(revoked_at: ...Time.now.utc) }
end
def revoke(clock = Time)
update(revoked_at: clock.now.utc)
end
def update_last_used(request, clock = Time)
update(last_used_at: clock.now.utc, last_used_ip: request.remote_ip)
end
def push_to_streaming_api
redis.publish("timeline:access_token:#{id}", Oj.dump(event: :kill)) if revoked? || destroyed?
end
end

View File

@ -1,49 +0,0 @@
# frozen_string_literal: true
module ApplicationExtension
extend ActiveSupport::Concern
APP_NAME_LIMIT = 60
APP_REDIRECT_URI_LIMIT = 2_000
APP_WEBSITE_LIMIT = 2_000
included do
include Redisable
has_many :created_users, class_name: 'User', foreign_key: 'created_by_application_id', inverse_of: :created_by_application
validates :name, length: { maximum: APP_NAME_LIMIT }
validates :redirect_uri, length: { maximum: APP_REDIRECT_URI_LIMIT }
validates :website, url: true, length: { maximum: APP_WEBSITE_LIMIT }, if: :website?
# The relationship used between Applications and AccessTokens is using
# dependent: delete_all, which means the ActiveRecord callback in
# AccessTokenExtension is not run, so instead we manually announce to
# streaming that these tokens are being deleted.
before_destroy :close_streaming_sessions, prepend: true
end
def confirmation_redirect_uri
redirect_uri.lines.first.strip
end
def redirect_uris
# Doorkeeper stores the redirect_uri value as a newline delimeted list in
# the database:
redirect_uri.split
end
def close_streaming_sessions(resource_owner = nil)
# TODO: #28793 Combine into a single topic
payload = Oj.dump(event: :kill)
scope = access_tokens
scope = scope.where(resource_owner_id: resource_owner.id) unless resource_owner.nil?
scope.in_batches do |tokens|
redis.pipelined do |pipeline|
tokens.ids.each do |id|
pipeline.publish("timeline:access_token:#{id}", payload)
end
end
end
end
end

View File

@ -9,12 +9,12 @@ class Vacuum::AccessTokensVacuum
private
def vacuum_revoked_access_tokens!
Doorkeeper::AccessToken.expired.in_batches.delete_all
Doorkeeper::AccessToken.revoked.in_batches.delete_all
OAuth::AccessToken.expired.in_batches.delete_all
OAuth::AccessToken.revoked.in_batches.delete_all
end
def vacuum_revoked_access_grants!
Doorkeeper::AccessGrant.expired.in_batches.delete_all
Doorkeeper::AccessGrant.revoked.in_batches.delete_all
OAuth::AccessGrant.expired.in_batches.delete_all
OAuth::AccessGrant.revoked.in_batches.delete_all
end
end

7
app/models/oauth.rb Normal file
View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
module OAuth
def self.table_name_prefix
'oauth_'
end
end

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
class OAuth::AccessGrant < ApplicationRecord
include ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessGrant
scope :expired, -> { where.not(expires_in: nil).where('created_at + MAKE_INTERVAL(secs => expires_in) < NOW()') }
scope :revoked, -> { where.not(revoked_at: nil).where(revoked_at: ...Time.now.utc) }
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
class OAuth::AccessToken < ApplicationRecord
include ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken
include Redisable
has_many :web_push_subscriptions, class_name: 'Web::PushSubscription', inverse_of: :access_token, dependent: nil
scope :expired, -> { where.not(expires_in: nil).where('created_at + MAKE_INTERVAL(secs => expires_in) < NOW()') }
scope :not_revoked, -> { where(revoked_at: nil) }
scope :revoked, -> { where.not(revoked_at: nil).where(revoked_at: ...Time.now.utc) }
after_commit :push_to_streaming_api
def revoke(clock = Time)
update(revoked_at: clock.now.utc)
end
def update_last_used(request, clock = Time)
update(last_used_at: clock.now.utc, last_used_ip: request.remote_ip)
end
private
def push_to_streaming_api
redis.publish("timeline:access_token:#{id}", Oj.dump(event: :kill)) if revoked? || destroyed?
end
end

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
class OAuth::Application < ApplicationRecord
include ::Doorkeeper::Orm::ActiveRecord::Mixins::Application
APP_NAME_LIMIT = 60
APP_REDIRECT_URI_LIMIT = 2_000
APP_WEBSITE_LIMIT = 2_000
include Redisable
has_many :created_users, class_name: 'User', foreign_key: :created_by_application_id, inverse_of: :created_by_application, dependent: nil
validates :name, length: { maximum: APP_NAME_LIMIT }
validates :redirect_uri, length: { maximum: APP_REDIRECT_URI_LIMIT }
validates :website, url: true, length: { maximum: APP_WEBSITE_LIMIT }, if: :website?
before_destroy :close_streaming_sessions, prepend: true
def confirmation_redirect_uri
redirect_uri.lines.first.strip
end
def redirect_uris
# The redirect_uri value is stored as a newline delimited list
redirect_uri.split
end
# The association between `Application` and `AccessToken` uses a setting of
# `dependent: delete_all` which means the callbacks in `AccessToken` are not
# run. Instead, announce to streaming that these tokens are being deleted.
def close_streaming_sessions(resource_owner = nil)
payload = Oj.dump(event: :kill)
scope = access_tokens
scope = scope.where(resource_owner_id: resource_owner.id) unless resource_owner.nil?
scope.in_batches do |tokens|
redis.pipelined do |pipeline|
tokens.ids.each do |id|
pipeline.publish("timeline:access_token:#{id}", payload)
end
end
end
end
end

View File

@ -32,7 +32,7 @@ class Report < ApplicationRecord
rate_limit by: :account, family: :reports
belongs_to :account
belongs_to :application, class_name: 'Doorkeeper::Application', optional: true
belongs_to :application, class_name: 'OAuth::Application', optional: true
with_options class_name: 'Account' do
belongs_to :target_account

View File

@ -19,7 +19,7 @@ class SessionActivation < ApplicationRecord
include BrowserDetection
belongs_to :user, inverse_of: :session_activations
belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', dependent: :destroy, optional: true
belongs_to :access_token, class_name: 'OAuth::AccessToken', dependent: :destroy, optional: true
belongs_to :web_push_subscription, class_name: 'Web::PushSubscription', dependent: :destroy, optional: true
delegate :token,
@ -61,12 +61,12 @@ class SessionActivation < ApplicationRecord
private
def assign_access_token
self.access_token = Doorkeeper::AccessToken.create!(access_token_attributes)
self.access_token = OAuth::AccessToken.create!(access_token_attributes)
end
def access_token_attributes
{
application_id: Doorkeeper::Application.find_by(superapp: true)&.id,
application_id: OAuth::Application.find_by(superapp: true)&.id,
resource_owner_id: user_id,
scopes: DEFAULT_SCOPES.join(' '),
expires_in: Doorkeeper.configuration.access_token_expires_in,

View File

@ -64,7 +64,7 @@ class Status < ApplicationRecord
update_index('statuses', :proper)
update_index('public_statuses', :proper)
belongs_to :application, class_name: 'Doorkeeper::Application', optional: true
belongs_to :application, class_name: 'OAuth::Application', optional: true
belongs_to :account, inverse_of: :statuses
belongs_to :in_reply_to_account, class_name: 'Account', optional: true

View File

@ -83,11 +83,11 @@ class User < ApplicationRecord
belongs_to :account, inverse_of: :user
belongs_to :invite, counter_cache: :uses, optional: true
belongs_to :created_by_application, class_name: 'Doorkeeper::Application', optional: true
belongs_to :created_by_application, class_name: 'OAuth::Application', optional: true
belongs_to :role, class_name: 'UserRole', optional: true
accepts_nested_attributes_for :account
has_many :applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: nil
has_many :applications, class_name: 'OAuth::Application', as: :owner, dependent: nil
has_many :backups, inverse_of: :user, dependent: nil
has_many :invites, inverse_of: :user, dependent: nil
has_many :markers, inverse_of: :user, dependent: :destroy
@ -303,7 +303,7 @@ class User < ApplicationRecord
end
def applications_last_used
Doorkeeper::AccessToken
OAuth::AccessToken
.where(resource_owner_id: id)
.where.not(last_used_at: nil)
.group(:application_id)
@ -314,7 +314,7 @@ class User < ApplicationRecord
def token_for_app(app)
return nil if app.nil? || app.owner != self
Doorkeeper::AccessToken.find_or_create_by(application_id: app.id, resource_owner_id: id) do |t|
OAuth::AccessToken.find_or_create_by(application_id: app.id, resource_owner_id: id) do |t|
t.scopes = app.scopes
t.expires_in = Doorkeeper.configuration.access_token_expires_in
t.use_refresh_token = Doorkeeper.configuration.refresh_token_enabled?
@ -368,9 +368,9 @@ class User < ApplicationRecord
end
def revoke_access!
Doorkeeper::AccessGrant.by_resource_owner(self).touch_all(:revoked_at)
OAuth::AccessGrant.by_resource_owner(self).touch_all(:revoked_at)
Doorkeeper::AccessToken.by_resource_owner(self).in_batches do |batch|
OAuth::AccessToken.by_resource_owner(self).in_batches do |batch|
batch.touch_all(:revoked_at)
Web::PushSubscription.where(access_token_id: batch).delete_all

View File

@ -18,7 +18,7 @@
class Web::PushSubscription < ApplicationRecord
belongs_to :user
belongs_to :access_token, class_name: 'Doorkeeper::AccessToken'
belongs_to :access_token, class_name: 'OAuth::AccessToken'
has_one :session_activation, foreign_key: 'web_push_subscription_id', inverse_of: :web_push_subscription, dependent: nil
@ -42,7 +42,7 @@ class Web::PushSubscription < ApplicationRecord
class << self
def unsubscribe_for(application_id, resource_owner)
access_token_ids = Doorkeeper::AccessToken.where(application_id: application_id, resource_owner_id: resource_owner.id).not_revoked.pluck(:id)
access_token_ids = OAuth::AccessToken.where(application_id: application_id, resource_owner_id: resource_owner.id).not_revoked.pluck(:id)
where(access_token_id: access_token_ids).delete_all
end
end

View File

@ -27,7 +27,7 @@ class AppSignUpService < BaseService
end
def create_access_token!
@access_token = Doorkeeper::AccessToken.create!(
@access_token = OAuth::AccessToken.create!(
application: @app,
resource_owner_id: @user.id,
scopes: @app.scopes,

View File

@ -26,7 +26,7 @@ class PostStatusService < BaseService
# @option [String] :scheduled_at
# @option [Hash] :poll Optional poll to attach
# @option [Enumerable] :media_ids Optional array of media IDs to attach
# @option [Doorkeeper::Application] :application
# @option [OAuth::Application] :application
# @option [String] :idempotency Optional idempotency key
# @option [Boolean] :with_rate_limit
# @option [Enumerable] :allowed_mentions Optional array of expected mentioned account IDs, raises `UnexpectedMentionsError` if unexpected accounts end up in mentions

View File

@ -21,7 +21,7 @@ class PublishScheduledStatusWorker
def options_with_objects(options)
options.tap do |options_hash|
options_hash[:application] = Doorkeeper::Application.find(options_hash.delete(:application_id)) if options[:application_id]
options_hash[:application] = OAuth::Application.find(options_hash.delete(:application_id)) if options[:application_id]
options_hash[:thread] = Status.find(options_hash.delete(:in_reply_to_id)) if options_hash[:in_reply_to_id]
end
end

View File

@ -20,7 +20,7 @@ class Scheduler::IpCleanupScheduler
SessionActivation.where(updated_at: ...IP_RETENTION_PERIOD.ago).in_batches.update_all(ip: nil)
User.where(current_sign_in_at: ...IP_RETENTION_PERIOD.ago).in_batches.update_all(sign_up_ip: nil)
LoginActivity.where(created_at: ...IP_RETENTION_PERIOD.ago).in_batches.destroy_all
Doorkeeper::AccessToken.where(last_used_at: ...IP_RETENTION_PERIOD.ago).in_batches.update_all(last_used_ip: nil)
OAuth::AccessToken.where(last_used_at: ...IP_RETENTION_PERIOD.ago).in_batches.update_all(last_used_ip: nil)
end
def clean_expired_ip_blocks!

View File

@ -117,9 +117,6 @@ module Mastodon
end
config.to_prepare do
Doorkeeper::Application.include ApplicationExtension
Doorkeeper::AccessGrant.include AccessGrantExtension
Doorkeeper::AccessToken.include AccessTokenExtension
Devise::FailureApp.include AbstractController::Callbacks
Devise::FailureApp.include Localized
end

View File

@ -4,6 +4,10 @@ Doorkeeper.configure do
# Change the ORM that doorkeeper will use (needs plugins)
orm :active_record
access_grant_class 'OAuth::AccessGrant'
access_token_class 'OAuth::AccessToken'
application_class 'OAuth::Application'
# This block will be called to check whether the resource owner is authenticated or not.
resource_owner_authenticator do
current_user || redirect_to(new_user_session_url)

View File

@ -1,3 +1,3 @@
# frozen_string_literal: true
Doorkeeper::Application.create_with(name: 'Web', redirect_uri: Doorkeeper.configuration.native_redirect_uri, scopes: 'read write follow push').find_or_create_by(superapp: true)
OAuth::Application.create_with(name: 'Web', redirect_uri: Doorkeeper.configuration.native_redirect_uri, scopes: 'read write follow push').find_or_create_by(superapp: true)

View File

@ -134,12 +134,12 @@ namespace :tests do
exit(1)
end
unless Doorkeeper::Application.find(2)[:scopes] == 'write:accounts profile'
unless OAuth::Application.find(2)[:scopes] == 'write:accounts profile'
puts 'Application OAuth scopes not rewritten as expected'
exit(1)
end
unless Doorkeeper::Application.find(2).access_tokens.first[:scopes] == 'write:accounts profile'
unless OAuth::Application.find(2).access_tokens.first[:scopes] == 'write:accounts profile'
puts 'OAuth access token scopes not rewritten as expected'
exit(1)
end

View File

@ -5,7 +5,7 @@ require 'rails_helper'
RSpec.describe OAuth::AuthorizationsController do
render_views
let(:app) { Doorkeeper::Application.create!(name: 'test', redirect_uri: 'http://localhost/', scopes: 'read') }
let(:app) { OAuth::Application.create!(name: 'test', redirect_uri: 'http://localhost/', scopes: 'read') }
describe 'GET #new' do
subject do
@ -34,7 +34,7 @@ RSpec.describe OAuth::AuthorizationsController do
context 'when app is already authorized' do
before do
Doorkeeper::AccessToken.find_or_create_for(
OAuth::AccessToken.find_or_create_for(
application: app,
resource_owner: user.id,
scopes: app.scopes,

View File

@ -55,7 +55,7 @@ RSpec.describe OAuth::AuthorizedApplicationsController do
it 'revokes access tokens for the application and removes subscriptions and sends kill payload to streaming' do
post :destroy, params: { id: application.id }
expect(Doorkeeper::AccessToken.where(application: application).first.revoked_at)
expect(OAuth::AccessToken.where(application: application).first.revoked_at)
.to_not be_nil
expect(Web::PushSubscription.where(user: user).count)
.to eq(0)

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
Fabricator :access_grant, from: 'Doorkeeper::AccessGrant' do
Fabricator :access_grant, from: OAuth::AccessGrant do
application
resource_owner_id { Fabricate(:user).id }
expires_in 3_600

View File

@ -1,3 +1,3 @@
# frozen_string_literal: true
Fabricator :access_token, from: 'Doorkeeper::AccessToken'
Fabricator :access_token, from: OAuth::AccessToken

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
Fabricator(:application, from: Doorkeeper::Application) do
Fabricator(:application, from: OAuth::Application) do
name 'Example'
website 'http://example.com'
redirect_uri 'http://example.com/callback'

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe Doorkeeper::AccessGrant do
RSpec.describe OAuth::AccessGrant do
describe 'Validations' do
subject { Fabricate :access_grant }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe Doorkeeper::AccessToken do
RSpec.describe OAuth::AccessToken do
describe 'Associations' do
it { is_expected.to have_many(:web_push_subscriptions).class_name('Web::PushSubscription').inverse_of(:access_token) }
end

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe Doorkeeper::Application do
RSpec.describe OAuth::Application do
describe 'Associations' do
it { is_expected.to have_many(:created_users).class_name('User').inverse_of(:created_by_application).with_foreign_key(:created_by_application_id) }
end

View File

@ -439,7 +439,7 @@ RSpec.describe User do
it 'creates and returns a persisted token' do
expect { user.token_for_app(app) }
.to change(Doorkeeper::AccessToken.where(resource_owner_id: user.id, application: app), :count).by(1)
.to change(OAuth::AccessToken.where(resource_owner_id: user.id, application: app), :count).by(1)
end
end
@ -449,7 +449,7 @@ RSpec.describe User do
it 'returns a persisted token' do
expect(user.token_for_app(app))
.to be_a(Doorkeeper::AccessToken)
.to be_a(OAuth::AccessToken)
.and eq(token)
end
end
@ -531,7 +531,7 @@ RSpec.describe User do
end
def remove_active_user_tokens
change { Doorkeeper::AccessToken.active_for(user).count }.to(0)
change { OAuth::AccessToken.active_for(user).count }.to(0)
end
def remove_user_web_subscriptions

View File

@ -31,7 +31,7 @@ RSpec.describe 'Apps' do
expect(response.content_type)
.to start_with('application/json')
app = Doorkeeper::Application.find_by(name: client_name)
app = OAuth::Application.find_by(name: client_name)
expect(app).to be_present
expect(app.scopes.to_s).to eq scopes
@ -64,7 +64,7 @@ RSpec.describe 'Apps' do
expect(response).to have_http_status(200)
expect(response.content_type)
.to start_with('application/json')
expect(Doorkeeper::Application.find_by(name: client_name)).to be_present
expect(OAuth::Application.find_by(name: client_name)).to be_present
expect(response.parsed_body)
.to include(
@ -84,7 +84,7 @@ RSpec.describe 'Apps' do
expect(response.content_type)
.to start_with('application/json')
app = Doorkeeper::Application.find_by(name: client_name)
app = OAuth::Application.find_by(name: client_name)
expect(app).to be_present
expect(app.scopes.to_s).to eq 'read'
@ -117,12 +117,12 @@ RSpec.describe 'Apps' do
expect(response).to have_http_status(200)
expect(response.content_type)
.to start_with('application/json')
expect(Doorkeeper::Application.find_by(name: client_name).scopes.to_s).to eq 'read'
expect(OAuth::Application.find_by(name: client_name).scopes.to_s).to eq 'read'
end
end
context 'with a too-long name' do
let(:client_name) { 'a' * Doorkeeper::Application::APP_NAME_LIMIT * 2 }
let(:client_name) { 'a' * OAuth::Application::APP_NAME_LIMIT * 2 }
it 'returns http unprocessable entity' do
subject
@ -134,7 +134,7 @@ RSpec.describe 'Apps' do
end
context 'with a too-long website' do
let(:website) { "https://foo.bar/#{'a' * Doorkeeper::Application::APP_WEBSITE_LIMIT * 2}" }
let(:website) { "https://foo.bar/#{'a' * OAuth::Application::APP_WEBSITE_LIMIT * 2}" }
it 'returns http unprocessable entity' do
subject
@ -146,7 +146,7 @@ RSpec.describe 'Apps' do
end
context 'with a too-long redirect_uri' do
let(:redirect_uris) { "https://app.example/#{'a' * Doorkeeper::Application::APP_REDIRECT_URI_LIMIT * 2}" }
let(:redirect_uris) { "https://app.example/#{'a' * OAuth::Application::APP_REDIRECT_URI_LIMIT * 2}" }
it 'returns http unprocessable entity' do
subject
@ -180,7 +180,7 @@ RSpec.describe 'Apps' do
expect(response.content_type)
.to start_with('application/json')
app = Doorkeeper::Application.find_by(name: client_name)
app = OAuth::Application.find_by(name: client_name)
expect(app).to be_present
expect(app.redirect_uri).to eq redirect_uris
@ -204,7 +204,7 @@ RSpec.describe 'Apps' do
expect(response.content_type)
.to start_with('application/json')
app = Doorkeeper::Application.find_by(name: client_name)
app = OAuth::Application.find_by(name: client_name)
expect(app).to be_present
expect(app.redirect_uri).to eq redirect_uris.join "\n"
@ -276,7 +276,7 @@ RSpec.describe 'Apps' do
expect(response.content_type)
.to start_with('application/json')
app = Doorkeeper::Application.find_by(name: client_name)
app = OAuth::Application.find_by(name: client_name)
expect(app).to be_present
expect(app.website).to eq website

View File

@ -169,7 +169,7 @@ RSpec.describe 'Caching behavior' do
let(:alice) { Account.find_by(username: 'alice') }
let(:user) { User.find_by(email: 'user@host.example') }
let(:token) { Doorkeeper::AccessToken.find_by(resource_owner_id: user.id) }
let(:token) { OAuth::AccessToken.find_by(resource_owner_id: user.id) }
before_all do
alice = Fabricate(:account, username: 'alice')

View File

@ -25,7 +25,7 @@ RSpec.describe 'Settings / Exports' do
let(:params) do
{
doorkeeper_application: {
oauth_application: {
name: 'My New App',
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
website: 'http://google.com',
@ -36,13 +36,13 @@ RSpec.describe 'Settings / Exports' do
it 'supports passing scope values as string' do
expect { subject }
.to change(Doorkeeper::Application, :count).by(1)
.to change(OAuth::Application, :count).by(1)
expect(response)
.to redirect_to(settings_applications_path)
end
it 'gracefully handles invalid nested params' do
post settings_applications_path(doorkeeper_application: 'invalid')
post settings_applications_path(oauth_application: 'invalid')
expect(response)
.to have_http_status(400)
@ -53,7 +53,7 @@ RSpec.describe 'Settings / Exports' do
let(:application) { Fabricate :application, owner: user }
it 'gracefully handles invalid nested params' do
put settings_application_path(application.id, doorkeeper_application: 'invalid')
put settings_application_path(application.id, oauth_application: 'invalid')
expect(response)
.to have_http_status(400)

View File

@ -68,7 +68,7 @@ RSpec.describe 'Auth Passwords' do
end
def user_token_count
Doorkeeper::AccessToken
OAuth::AccessToken
.active_for(user)
.count
end

View File

@ -7,7 +7,7 @@ RSpec.describe 'Using OAuth from an external app' do
subject { visit "/oauth/authorize?#{params.to_query}" }
let(:client_app) { Doorkeeper::Application.create!(name: 'test', redirect_uri: about_url(host: Rails.application.config.x.local_domain), scopes: 'read') }
let(:client_app) { OAuth::Application.create!(name: 'test', redirect_uri: about_url(host: Rails.application.config.x.local_domain), scopes: 'read') }
let(:params) do
{ client_id: client_app.uid, response_type: 'code', redirect_uri: client_app.redirect_uri, scope: 'read' }
end
@ -264,7 +264,7 @@ RSpec.describe 'Using OAuth from an external app' do
end
def user_has_grant_with_client_app?
Doorkeeper::AccessGrant
OAuth::AccessGrant
.exists?(
application: client_app,
resource_owner_id: user.id

View File

@ -34,7 +34,7 @@ RSpec.describe 'Settings applications page' do
fill_in_form
expect { submit_form }
.to change(Doorkeeper::Application, :count).by(1)
.to change(OAuth::Application, :count).by(1)
expect(page)
.to have_content(I18n.t('doorkeeper.applications.index.title'))
.and have_content('My new app')
@ -47,7 +47,7 @@ RSpec.describe 'Settings applications page' do
visit new_settings_application_path
expect { submit_form }
.to not_change(Doorkeeper::Application, :count)
.to not_change(OAuth::Application, :count)
expect(page)
.to have_content("can't be blank")
end
@ -60,9 +60,9 @@ RSpec.describe 'Settings applications page' do
fill_in I18n.t('activerecord.attributes.doorkeeper/application.redirect_uri'),
with: 'urn:ietf:wg:oauth:2.0:oob'
check 'read', id: :doorkeeper_application_scopes_read
check 'write', id: :doorkeeper_application_scopes_write
check 'follow', id: :doorkeeper_application_scopes_follow
check 'read', id: :oauth_application_scopes_read
check 'write', id: :oauth_application_scopes_write
check 'follow', id: :oauth_application_scopes_follow
end
def submit_form
@ -76,12 +76,12 @@ RSpec.describe 'Settings applications page' do
fill_in form_app_name_label,
with: 'My new app name with a new value'
check 'push', id: :doorkeeper_application_scopes_push
check 'push', id: :oauth_application_scopes_push
submit_form
expect(page)
.to have_content('My new app name with a new value')
.and have_checked_field('push', id: :doorkeeper_application_scopes_push)
.and have_checked_field('push', id: :oauth_application_scopes_push)
end
it 'does not update with wrong values' do
@ -110,7 +110,7 @@ RSpec.describe 'Settings applications page' do
visit settings_applications_path
expect { destroy_application }
.to change(Doorkeeper::Application, :count).by(-1)
.to change(OAuth::Application, :count).by(-1)
expect(page)
.to have_no_content(application.name)
expect(redis_pipeline_stub)