Compare commits

...

17 Commits

Author SHA1 Message Date
Emelia Smith
45fe1bd1df
Merge a2a34fbadd into 94bceb8683 2025-07-11 14:03:35 +00:00
Echo
94bceb8683
Expose enabled features to the frontend (#35348)
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 13:15:22 +00:00
Claire
88b0f3a172
Simplify DatabaseViewRecord.refresh (#35252) 2025-07-11 08:36:05 +00:00
github-actions[bot]
b69b5ba775
New Crowdin Translations (automated) (#35344)
Co-authored-by: GitHub Actions <noreply@github.com>
2025-07-11 08:14:39 +00:00
Emelia Smith
a2a34fbadd
Migrate Web::PushSubscription and SessionActivation records to the new access token after refresh 2025-04-23 20:58:17 +02:00
Emelia Smith
677e0b8a37
Add 'none' to token_endpoint_auth_methods_supported to indicate public client support 2025-04-23 20:58:17 +02:00
Emelia Smith
81b79fefb7
WIP: refresh tokens 2025-04-23 20:58:17 +02:00
Emelia Smith
9e0eb99747
Change /oauth/token request specs to use client_secret_basic authentication 2025-04-23 20:58:17 +02:00
Emelia Smith
fcd238cb4b
Fix issues with null appearing for user owned access tokens 2025-04-23 20:58:16 +02:00
Emelia Smith
463d5dd4d5
Try to fix the usage of doorkeeper configuration 2025-04-23 20:57:58 +02:00
Emelia Smith
1af6ae19b9
Add offline_access scope 2025-04-23 20:56:51 +02:00
Emelia Smith
7898619d74
Prevent only using offline_access scope 2025-04-23 20:56:51 +02:00
Emelia Smith
2250aead46
WIP 2025-04-23 20:56:51 +02:00
Emelia Smith
47e4f8478f
Enable expiry of OAuth Access Tokens granted to public clients 2025-04-23 20:56:50 +02:00
Emelia Smith
fad8f7b148
Only return client_secret for confidential clients 2025-04-23 20:56:50 +02:00
Emelia Smith
b21e7d8fdb
Add support for public clients to OAuth Application creation - parameter name TBD 2025-04-23 20:56:50 +02:00
Emelia Smith
5c6ad1a0e5
Ensure only confidential clients can use Client Credentials grant flow 2025-04-23 20:56:50 +02:00
26 changed files with 283 additions and 51 deletions

View File

@ -2,6 +2,9 @@
class Api::V1::AppsController < Api::BaseController
skip_before_action :require_authenticated_user!
before_action :validate_token_endpoint_auth_method!
TOKEN_ENDPOINT_AUTH_METHODS = %w(none client_secret_basic client_secret_post).freeze
def create
@app = Doorkeeper::Application.create!(application_options)
@ -16,14 +19,25 @@ class Api::V1::AppsController < Api::BaseController
redirect_uri: app_params[:redirect_uris],
scopes: app_scopes_or_default,
website: app_params[:website],
confidential: !app_public?,
}
end
def validate_token_endpoint_auth_method!
return unless app_params.include? :token_endpoint_auth_method
bad_request unless TOKEN_ENDPOINT_AUTH_METHODS.include? app_params[:token_endpoint_auth_method]
end
def app_public?
app_params[:token_endpoint_auth_method] == 'none'
end
def app_scopes_or_default
app_params[:scopes] || Doorkeeper.configuration.default_scopes
end
def app_params
params.permit(:client_name, :scopes, :website, :redirect_uris, redirect_uris: [])
params.permit(:client_name, :scopes, :website, :token_endpoint_auth_method, :redirect_uris, redirect_uris: [])
end
end

View File

@ -21,7 +21,13 @@ class OAuth::AuthorizationsController < Doorkeeper::AuthorizationsController
end
def render_success
if skip_authorization? || (matching_token? && !truthy_param?('force_login'))
# FIXME: Find a better way to apply this validation: if the scopes only
# includes offline_access, then it's not valid, since offline_access doesn't
# actually give access to resources:
if pre_auth.scopes.all?('offline_access')
error = Doorkeeper::OAuth::InvalidRequestResponse.new(reason: :offline_access_only, missing_param: nil)
render :error, locals: { error_response: error }, status: 400
elsif skip_authorization? || (matching_token? && !truthy_param?('force_login'))
redirect_or_render authorize_response
elsif Doorkeeper.configuration.api_only
render json: pre_auth

View File

@ -1,6 +1,5 @@
// @ts-check
/**
* @typedef {[code: string, name: string, localName: string]} InitialStateLanguage
*/
@ -64,6 +63,7 @@
* @property {boolean=} critical_updates_pending
* @property {InitialStateMeta} meta
* @property {Role?} role
* @property {string[]} features
*/
const element = document.getElementById('initial-state');
@ -140,4 +140,12 @@ export function getAccessToken() {
return getMeta('access_token');
}
/**
* @param {string} feature
* @returns {boolean}
*/
export function isFeatureEnabled(feature) {
return initialState?.features?.includes(feature) || false;
}
export default initialState;

View File

@ -219,6 +219,9 @@
"confirmations.delete_list.confirm": "Elimina",
"confirmations.delete_list.message": "Segur que vols suprimir permanentment aquesta llista?",
"confirmations.delete_list.title": "Eliminar la llista?",
"confirmations.discard_draft.confirm": "Descarta i continua",
"confirmations.discard_draft.edit.cancel": "Continua l'edició",
"confirmations.discard_draft.post.cancel": "Reprendre l'esborrany",
"confirmations.discard_edit_media.confirm": "Descarta",
"confirmations.discard_edit_media.message": "Tens canvis no desats en la descripció del contingut o en la previsualització, els vols descartar?",
"confirmations.follow_to_list.confirm": "Seguir i afegir a una llista",
@ -792,6 +795,7 @@
"report_notification.categories.violation": "Violació de norma",
"report_notification.categories.violation_sentence": "violació de normes",
"report_notification.open": "Obre l'informe",
"search.clear": "Esborra la cerca",
"search.no_recent_searches": "No hi ha cerques recents",
"search.placeholder": "Cerca",
"search.quick_action.account_search": "Perfils coincidint amb {x}",

View File

@ -572,7 +572,7 @@
"navigation_bar.mutes": "Skjulte brugere",
"navigation_bar.opened_in_classic_interface": "Indlæg, konti og visse andre sider åbnes som standard i den klassiske webgrænseflade.",
"navigation_bar.preferences": "Præferencer",
"navigation_bar.privacy_and_reach": "Fortrolighed og udbredelse",
"navigation_bar.privacy_and_reach": "Fortrolighed og rækkevidde",
"navigation_bar.search": "Søg",
"navigation_bar.search_trends": "Søg/Trender",
"navigation_panel.collapse_followed_tags": "Sammenfold menuen Fulgte hashtags",

View File

@ -14,6 +14,8 @@ class ScopeTransformer < Parslet::Transform
# # override for profile scope which is read only
@access = %w(read) if @term == 'profile'
# Override offline_access since it doesn't imply read or write access:
@access = %w(offline) if @term == 'offline_access'
end
def key

View File

@ -10,12 +10,6 @@ module DatabaseViewRecord
concurrently: true,
cascade: false
)
rescue ActiveRecord::StatementInvalid
Scenic.database.refresh_materialized_view(
table_name,
concurrently: false,
cascade: false
)
end
end

View File

@ -65,12 +65,22 @@ class SessionActivation < ApplicationRecord
end
def access_token_attributes
app = Doorkeeper::Application.find_by(superapp: true)
scopes = Doorkeeper::OAuth::Scopes.from_array(DEFAULT_SCOPES)
context = Doorkeeper::OAuth::Authorization::Token.build_context(
app,
Doorkeeper::OAuth::AUTHORIZATION_CODE,
scopes,
user_id
)
{
application_id: Doorkeeper::Application.find_by(superapp: true)&.id,
resource_owner_id: user_id,
scopes: DEFAULT_SCOPES.join(' '),
expires_in: Doorkeeper.configuration.access_token_expires_in,
use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?,
application_id: context.client.id,
resource_owner_id: context.resource_owner,
scopes: context.scopes,
expires_in: Doorkeeper::OAuth::Authorization::Token.access_token_expires_in(Doorkeeper.config, context),
use_refresh_token: Doorkeeper::OAuth::Authorization::Token.refresh_token_enabled?(Doorkeeper.config, context),
}
end
end

View File

@ -314,10 +314,12 @@ 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|
t.scopes = app.scopes
t.expires_in = Doorkeeper.configuration.access_token_expires_in
t.use_refresh_token = Doorkeeper.configuration.refresh_token_enabled?
context = Doorkeeper::OAuth::Authorization::Token.build_context(app, Doorkeeper::OAuth::AUTHORIZATION_CODE, app.scopes, app.owner.id)
Doorkeeper::AccessToken.find_or_create_by(application_id: context.client.id, resource_owner_id: context.resource_owner) do |t|
t.scopes = context.scopes
t.expires_in = Doorkeeper::OAuth::Authorization::Token.access_token_expires_in(Doorkeeper.config, context)
t.use_refresh_token = Doorkeeper::OAuth::Authorization::Token.refresh_token_enabled?(Doorkeeper.config, context)
end
end

View File

@ -61,7 +61,7 @@ class OAuthMetadataPresenter < ActiveModelSerializers::Model
end
def token_endpoint_auth_methods_supported
%w(client_secret_basic client_secret_post)
%w(none client_secret_basic client_secret_post)
end
def code_challenge_methods_supported

View File

@ -5,7 +5,7 @@ class InitialStateSerializer < ActiveModel::Serializer
attributes :meta, :compose, :accounts,
:media_attachments, :settings,
:languages
:languages, :features
attribute :critical_updates_pending, if: -> { object&.role&.can?(:view_devops) && SoftwareUpdate.check_enabled? }
@ -85,6 +85,10 @@ class InitialStateSerializer < ActiveModel::Serializer
LanguagesHelper::SUPPORTED_LOCALES.map { |(key, value)| [key, value[0], value[1]] }
end
def features
Mastodon::Feature.enabled_features
end
private
def default_meta_store

View File

@ -8,7 +8,7 @@ class REST::CredentialApplicationSerializer < REST::ApplicationSerializer
end
def client_secret
object.secret
object.secret if object.confidential?
end
# Added for future forwards compatibility when we may decide to expire OAuth

View File

@ -27,12 +27,14 @@ class AppSignUpService < BaseService
end
def create_access_token!
context = Doorkeeper::OAuth::Authorization::Token.build_context(@app, Doorkeeper::OAuth::AUTHORIZATION_CODE, @app.scopes, @user.id)
@access_token = Doorkeeper::AccessToken.create!(
application: @app,
resource_owner_id: @user.id,
scopes: @app.scopes,
expires_in: Doorkeeper.configuration.access_token_expires_in,
use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?
application: context.client,
resource_owner_id: context.resource_owner,
scopes: context.scopes,
expires_in: Doorkeeper::OAuth::Authorization::Token.access_token_expires_in(Doorkeeper.config, context),
use_refresh_token: Doorkeeper::OAuth::Authorization::Token.refresh_token_enabled?(Doorkeeper.config, context)
)
end

View File

@ -31,10 +31,30 @@ Doorkeeper.configure do
# If you want to disable expiration, set this to nil.
access_token_expires_in nil
# Assign a custom TTL for implicit grants.
# custom_access_token_expires_in do |oauth_client|
# oauth_client.application.additional_settings.implicit_oauth_expiration
# end
# context.grant_type to compare with Doorkeeper::OAUTH grant type constants
# context.client for client (Doorkeeper::Application)
# context.scopes for scopes
custom_access_token_expires_in do |context|
# If the client is confidential (all clients pre 4.3) and it hasn't
# requested offline_access, then we don't want to expire access tokens.
# Applications created by users are also considered confidential.
if context.client.confidential? && !context.scopes.exists?('offline_access')
nil
else
15.minutes.to_i
end
end
use_refresh_token do |context|
context.scopes.exists?('offline_access')
end
after_successful_strategy_response do |request, _response|
if request.is_a? Doorkeeper::OAuth::RefreshTokenRequest
Web::PushSubscription.where(access_token_id: request.refresh_token.id).update!(access_token_id: request.access_token.id)
SessionActivation.where(access_token_id: request.refresh_token.id).update!(access_token_id: request.access_token.id)
end
end
# Use a custom class for generating the access token.
# https://github.com/doorkeeper-gem/doorkeeper#custom-access-token-generator
@ -71,6 +91,7 @@ Doorkeeper.configure do
# https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
default_scopes :read
optional_scopes :profile,
:offline_access,
:write,
:'write:accounts',
:'write:blocks',
@ -120,7 +141,9 @@ Doorkeeper.configure do
# 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 the wiki for more information on customization
# client_credentials :from_basic, :from_params
#
# This is the default value:
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
@ -165,7 +188,17 @@ Doorkeeper.configure do
# http://tools.ietf.org/html/rfc6819#section-4.4.3
#
grant_flows %w(authorization_code client_credentials)
grant_flows %w(authorization_code client_credentials refresh_token)
# If the client is not a confidential client, it should not be able to use the
# client_credentials grant flow, since it cannot keep a secret.
allow_grant_flow_for_client do |grant_flow, client|
if grant_flow == Doorkeeper::OAuth::CLIENT_CREDENTIALS
client.confidential?
else
true
end
end
# Under some circumstances you might want to have applications auto-approved,
# so that the user skips the authorization step.

View File

@ -578,6 +578,11 @@ ca:
all: Totes
limited: Limitades
title: Moderació
moderation_notes:
create: Afegeix una nota de moderació
created_msg: S'ha creat la nota de moderació d'instància.
destroyed_msg: S'ha esborrat la nota de moderació d'instància.
title: Notes de moderació
private_comment: Comentari privat
public_comment: Comentari públic
purge: Purga
@ -1339,6 +1344,10 @@ ca:
basic_information: Informació bàsica
hint_html: "<strong>Personalitza el que la gent veu en el teu perfil públic i a prop dels teus tuts..</strong> És més probable que altres persones et segueixin i interaccionin amb tu quan tens emplenat el teu perfil i amb la teva imatge."
other: Altres
emoji_styles:
auto: Automàtic
native: Nadiu
twemoji: Twemoji
errors:
'400': La sol·licitud que vas emetre no era vàlida o no era correcta.
'403': No tens permís per a veure aquesta pàgina.

View File

@ -653,7 +653,7 @@ da:
mark_as_sensitive_description_html: Medierne i det anmeldte indlæg markeres som sensitive, og en advarsel (strike) registreres mhp. eskalering ved evt. fremtidige overtrædelser fra samme konto.
other_description_html: Se flere muligheder for at kontrollere kontoens adfærd og tilpasse kommunikationen til den anmeldte konto.
resolve_description_html: Ingen foranstaltninger træffes mod den anmeldte konto, ingen advarsel (strike) registreres og anmeldelsen lukkes.
silence_description_html: Kontoen vil kun være synlig for følgerene eller dem, som manuelt slå den op, hvilket markant begrænser dens udbredelse. Kan altid omgøres. Lukker alle indrapporteringer af kontoen.
silence_description_html: Kontoen vil kun være synlig for dem, der allerede følger den eller manuelt slår den op, hvilket alvorligt begrænser dens rækkevidde. Kan altid omgøres. Lukker alle indrapporteringer af denne konto.
suspend_description_html: Kontoen inkl. alt indhold utilgængeliggøres og interaktion umuliggøres, og den slettes på et tidspunkt. Kan omgøres inden for 30 dage. Lukker alle indrapporteringer af kontoen.
actions_description_html: Afgør, hvilke foranstaltning, der skal træffes for at løse denne anmeldelse. Ved en straffende foranstaltning mod den anmeldte konto, fremsendes en e-mailnotifikation, undtagen når kategorien <strong>Spam</strong> er valgt.
actions_description_remote_html: Fastslå en nødvendig handling mhp. at løse denne anmeldelse. Dette vil kun påvirke <strong>din</strong> servers kommunikation med, og indholdshåndtering for, fjernkontoen.
@ -1266,8 +1266,8 @@ da:
user_privacy_agreement_html: Jeg accepterer <a href="%{privacy_policy_path}" target="_blank">fortrolighedspolitikken</a>
author_attribution:
example_title: Eksempeltekst
hint_html: Skriver du nyheder eller blogartikler uden for Mastodon? Styr, hvordan man bliver krediteret, når disse deles på Mastodon.
instructions: 'Sørg for, at denne kode er i artikelens HTML:'
hint_html: Skriver du nyheder eller blogartikler uden for Mastodon? Styr, hvordan du bliver krediteret, når de bliver delt på Mastodon.
instructions: 'Sørg for, at denne kode er i din artikels HTML:'
more_from_html: Flere fra %{name}
s_blog: "%{name}s blog"
then_instructions: Tilføj dernæst publikationsdomænenavnet i feltet nedenfor.
@ -1718,11 +1718,11 @@ da:
hint_html: "<strong>Tilpas hvordan din profil og dine indlæg kan findes.</strong> En række funktioner i Mastodon kan hjælpe dig med at nå ud til et bredere publikum, hvis du aktiverer dem. Tjek indstillingerne herunder for at sikre, at de passer til dit brugsscenarie."
privacy: Privatliv
privacy_hint_html: Styr, hvor meget der ønskes synliggjort til gavn for andre. Folk finder interessante profiler og apps ved at tjekke andres følgere ud, samt se hvilke apps de sender fra, men dine præferencer ønskes muligvis ikke synliggjort.
reach: Udbredelse
reach: Rækkevidde
reach_hint_html: Indstil om du vil blive opdaget og fulgt af nye mennesker. Ønsker du, at dine indlæg skal vises på Udforsk-siden? Ønsker du, at andre skal se dig i deres følg-anbefalinger? Ønsker du at acceptere alle nye følgere automatisk, eller vil du have detaljeret kontrol over hver og en?
search: Søg
search: Søgning
search_hint_html: Indstil hvordan du vil findes. Ønsker du, at folk skal finde dig gennem hvad du har skrevet offentligt? Vil du have folk udenfor Mastodon til at finde din profil, når de søger på nettet? Vær opmærksom på, at det ikke kan garanteres at dine offentlige indlæg er udelukket fra alle søgemaskiner.
title: Fortrolighed og udbredelse
title: Fortrolighed og rækkevidde
privacy_policy:
title: Privatlivspolitik
reactions:
@ -1923,7 +1923,7 @@ da:
'7889238': 3 måneder
min_age_label: Alderstærskel
min_favs: Behold indlæg favoritmarkeret mindst
min_favs_hint: Sletter ingen dine egne indlæg, som har modtaget minimum dette antal favoritmarkeringer. Lad stå tomt for at slette indlæg uanset antal favoritmarkeringer
min_favs_hint: Sletter ingen af dine egne indlæg, som har modtaget minimum dette antal favoritmarkeringer. Lad stå tom for at slette indlæg uanset antal favoritmarkeringer
min_reblogs: Behold indlæg fremhævet mindst
min_reblogs_hint: Sletter ingen af dine egne indlæg, som er fremhævet flere end dette antal gange. Lad stå tom for at slette indlæg uanset antallet af fremhævelser
stream_entries:
@ -2095,7 +2095,7 @@ da:
verification:
extra_instructions_html: <strong>Tip:</strong> Linket på din hjemmeside kan være usynligt. Den vigtige del er <code>rel="me"</code> , som forhindrer impersonation på websteder med brugergenereret indhold. Du kan endda bruge et <code>link</code> tag i overskriften på siden i stedet for <code>a</code>, men HTML skal være tilgængelig uden at udføre JavaScript.
here_is_how: Sådan gør du
hint_html: "<strong>Bekræftelse af din identitet på Mastodon er for alle.</strong> Baseret på åbne webstandarder, nu og for evigt gratis. Alt du behøver er en personlig hjemmeside, som folk genkende dig ved. Når du linker til denne hjemmeside fra din profil, vi vil kontrollere, at hjemmesiden linker tilbage til din profil og vise en visuel indikator på det."
hint_html: "<strong>Verificering af din identitet på Mastodon er for alle.</strong> Baseret på åbne webstandarder, nu og for altid gratis. Alt, hvad du behøver, er en personlig hjemmeside, som folk kender dig fra. Når du linker til denne hjemmeside fra din profil, kontrollerer vi, at hjemmesiden linker tilbage til din profil, og viser en visuel indikator på den."
instructions_html: Kopier og indsæt koden nedenfor i HTML på din hjemmeside. Tilføj derefter adressen på din hjemmeside i et af de ekstra felter på din profil på fanen "Redigér profil" og gem ændringer.
verification: Bekræftelse
verified_links: Dine bekræftede links

View File

@ -89,6 +89,7 @@ en:
invalid_request:
missing_param: 'Missing required parameter: %{value}.'
request_not_authorized: Request need to be authorized. Required parameter for authorizing request is missing or invalid.
offline_access_only: The offline_access scope can only be used with other scopes.
unknown: The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.
invalid_resource_owner: The provided resource owner credentials are not valid, or resource owner cannot be found
invalid_scope: The requested scope is invalid, unknown, or malformed.
@ -118,6 +119,7 @@ en:
read: Read-only access
read/write: Read and write access
write: Write-only access
offline: Access for an extended period of time
title:
accounts: Accounts
admin/accounts: Administration of accounts
@ -138,6 +140,7 @@ en:
notifications: Notifications
profile: Your Mastodon profile
push: Push notifications
offline_access: Offline access
reports: Reports
search: Search
statuses: Posts

View File

@ -1349,6 +1349,10 @@ hu:
basic_information: Általános információk
hint_html: "<strong>Tedd egyedivé, mi látnak mások a profilodon és a bejegyzéseid mellett.</strong> Mások nagyobb eséllyel követnek vissza és lépnek veled kapcsolatba, ha van kitöltött profilod és profilképed."
other: Egyéb
emoji_styles:
auto: Automatikus
native: Natív
twemoji: Twemoji
errors:
'400': A küldött kérés érvénytelen vagy hibás volt.
'403': Nincs jogosultságod az oldal megtekintéséhez.

View File

@ -61,6 +61,7 @@ ca:
setting_display_media_default: Amaga el contingut gràfic marcat com a sensible
setting_display_media_hide_all: Oculta sempre tot el contingut multimèdia
setting_display_media_show_all: Mostra sempre el contingut gràfic
setting_emoji_style: Com mostrar els emojis. "Automàtic" provarà de fer servir els emojis nadius, però revertirà a twemojis en els navegadors antics.
setting_system_scrollbars_ui: S'aplica només als navegadors d'escriptori basats en Safari i Chrome
setting_use_blurhash: Els degradats es basen en els colors de les imatges ocultes, però n'enfosqueixen els detalls
setting_use_pending_items: Amaga les actualitzacions de la línia de temps després de fer un clic, en lloc de desplaçar-les automàticament
@ -240,6 +241,7 @@ ca:
setting_display_media_default: Per defecte
setting_display_media_hide_all: Amaga-ho tot
setting_display_media_show_all: Mostra-ho tot
setting_emoji_style: Estil d'emojis
setting_expand_spoilers: Desplega sempre els tuts marcats amb advertències de contingut
setting_hide_network: Amaga la teva xarxa
setting_missing_alt_text_modal: Mostra un diàleg de confirmació abans de publicar contingut sense text alternatiu

View File

@ -61,6 +61,7 @@ hu:
setting_display_media_default: Kényes tartalomnak jelölt média elrejtése
setting_display_media_hide_all: Média elrejtése mindig
setting_display_media_show_all: Média megjelenítése mindig
setting_emoji_style: Az emodzsik megjelenítési módja. Az „Automatikus” megpróbálja a natív emodzsikat használni, de az örökölt böngészők esetén a Twemojira vált vissza.
setting_system_scrollbars_ui: Csak Chrome és Safari alapú asztali böngészőkre vonatkozik
setting_use_blurhash: A kihomályosítás az eredeti képből történik, de minden részletet elrejt
setting_use_pending_items: Idővonal frissítése csak kattintásra automatikus görgetés helyett
@ -241,6 +242,7 @@ hu:
setting_display_media_default: Alapértelmezés
setting_display_media_hide_all: Mindent elrejt
setting_display_media_show_all: Mindent mutat
setting_emoji_style: Emodzsistílus
setting_expand_spoilers: Tartalmi figyelmeztetéssel ellátott bejegyzések automatikus kinyitása
setting_hide_network: Hálózatod elrejtése
setting_missing_alt_text_modal: Megerősítési párbeszédablak megjelenítése a helyettesítő szöveg nélküli média közzététele előtt

View File

@ -34,12 +34,19 @@ RSpec.describe OAuth::AuthorizationsController do
context 'when app is already authorized' do
before do
context = Doorkeeper::OAuth::Authorization::Token.build_context(
app,
Doorkeeper::OAuth::AUTHORIZATION_CODE,
app.scopes,
user.id
)
Doorkeeper::AccessToken.find_or_create_for(
application: app,
resource_owner: user.id,
scopes: app.scopes,
expires_in: Doorkeeper.configuration.access_token_expires_in,
use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?
application: context.client,
resource_owner: context.resource_owner,
scopes: context.scopes,
expires_in: Doorkeeper::OAuth::Authorization::Token.access_token_expires_in(Doorkeeper.config, context),
use_refresh_token: Doorkeeper::OAuth::Authorization::Token.refresh_token_enabled?(Doorkeeper.config, context)
)
end

View File

@ -3,5 +3,5 @@
Fabricator(:application, from: Doorkeeper::Application) do
name 'Example'
website 'http://example.com'
redirect_uri 'http://example.com/callback'
redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
end

View File

@ -23,6 +23,12 @@ RSpec.describe ScopeTransformer do
it_behaves_like 'a scope', nil, 'profile', 'read'
end
context 'with scope "offline_access"' do
let(:input) { 'offline_access' }
it_behaves_like 'a scope', nil, 'offline_access', 'offline'
end
context 'with scope "read"' do
let(:input) { 'read' }

View File

@ -36,6 +36,7 @@ RSpec.describe 'Apps' do
expect(app).to be_present
expect(app.scopes.to_s).to eq scopes
expect(app.redirect_uris).to eq redirect_uris
expect(app.confidential).to be true
expect(response.parsed_body).to match(
a_hash_including(
@ -55,6 +56,76 @@ RSpec.describe 'Apps' do
end
end
context 'without being a confidential application' do
let(:client_name) { 'Test confidential app' }
let(:params) do
{
client_name: client_name,
redirect_uris: redirect_uris,
scopes: scopes,
website: website,
token_endpoint_auth_method: 'none',
}
end
it 'creates an public OAuth app', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(response.content_type)
.to start_with('application/json')
app = Doorkeeper::Application.find_by(name: client_name)
expect(app).to be_present
expect(app.scopes.to_s).to eq scopes
expect(app.redirect_uris).to eq redirect_uris
expect(app.confidential).to be false
expect(response.parsed_body).to match(
a_hash_including(
id: app.id.to_s,
client_id: app.uid,
client_secret: nil,
client_secret_expires_at: 0,
name: client_name,
website: website,
scopes: ['read', 'write'],
redirect_uris: redirect_uris,
# Deprecated properties as of 4.3:
redirect_uri: redirect_uri,
vapid_key: Rails.configuration.x.vapid_public_key
)
)
end
end
context 'when token_endpoint_auth_method is unknown' do
let(:client_name) { 'Test unknown auth app' }
let(:params) do
{
client_name: client_name,
redirect_uris: redirect_uris,
scopes: scopes,
website: website,
# Not yet supported:
token_endpoint_auth_method: 'private_key_jwt',
}
end
it 'does not create an OAuth app', :aggregate_failures do
subject
expect(response).to have_http_status(400)
expect(response.content_type)
.to start_with('application/json')
app = Doorkeeper::Application.find_by(name: client_name)
expect(app).to_not be_present
end
end
context 'without scopes being supplied' do
let(:scopes) { nil }

View File

@ -5,17 +5,19 @@ require 'rails_helper'
RSpec.describe 'Managing OAuth Tokens' do
describe 'POST /oauth/token' do
subject do
post '/oauth/token', params: params
post '/oauth/token', params: params, headers: {
# This is using the OAuth client_secret_basic client authentication method
Authorization: ActionController::HttpAuthentication::Basic.encode_credentials(application.uid, application.secret),
}
end
let(:application) do
Fabricate(:application, scopes: 'read write follow', redirect_uri: 'urn:ietf:wg:oauth:2.0:oob')
end
let(:params) do
{
grant_type: grant_type,
client_id: application.uid,
client_secret: application.secret,
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
code: code,
scope: scope,
@ -103,6 +105,53 @@ RSpec.describe 'Managing OAuth Tokens' do
end
end
end
context "with grant_type 'refresh_token'" do
let(:grant_type) { 'refresh_token' }
let!(:user) { Fabricate(:user) }
let!(:application) { Fabricate(:application, scopes: 'read offline_access') }
let!(:access_token) do
Fabricate(
:accessible_access_token,
resource_owner_id: user.id,
application: application,
# Even though the `application` uses the `offline_access` scope, the
# generation of a refresh token only happens when the model is created
# with `use_refresh_token: true`.
#
# This is normally determined from the request to create the access
# token, but here we are just creating the access token model, so we
# need to force the `access_token` to have `use_refresh_token: true`
use_refresh_token: true
)
end
let!(:web_push_subscription) { Fabricate(:web_push_subscription, user: user, access_token: access_token) }
let(:params) do
{
grant_type: grant_type,
refresh_token: access_token.refresh_token,
}
end
it 'updates the Web::PushSubscription when refreshed' do
expect { subject }
.to change { access_token.reload.revoked_at }.from(nil).to(be_present)
expect(response).to have_http_status(200)
new_token = Doorkeeper::AccessToken.by_token(response.parsed_body[:access_token])
expect(web_push_subscription.reload.access_token_id).to eq(new_token.id)
# Assert that there are definitely no subscriptions left for the
# previous access token:
expect(Web::PushSubscription.where(access_token: access_token.id).count)
.to eq(0)
end
end
end
describe 'POST /oauth/revoke' do

View File

@ -25,7 +25,7 @@ RSpec.describe 'The /.well-known/oauth-authorization-server request' do
scopes_supported: Doorkeeper.configuration.scopes.map(&:to_s),
response_types_supported: Doorkeeper.configuration.authorization_response_types,
response_modes_supported: Doorkeeper.configuration.authorization_response_flows.flat_map(&:response_mode_matches).uniq,
token_endpoint_auth_methods_supported: %w(client_secret_basic client_secret_post),
token_endpoint_auth_methods_supported: %w(none client_secret_basic client_secret_post),
grant_types_supported: grant_types_supported,
code_challenge_methods_supported: Doorkeeper.configuration.pkce_code_challenge_methods_supported,
# non-standard extension: