A kódbázis kommentek nélkül marad
Kérésre: minden magyarázó komment kikerült a forrásfájlokból — 89 Ruby, 16 TypeScript, 14 Vue, plusz a CSS/JS/CJS. Nem soralapú kereséssel: a Ruby-t a Ripper tokenizálta, a JS/TS/CSS-t állapotgép járta végig, hogy az URL-ekben, reguláris kifejezésekben és heredocokban álló // és # jelek helyükön maradjanak. Három komment maradt, mert nélkülük nem indul a kód: az entrypoint.sh shebangja, a vite-env.d.ts hármas perjeles referenciája, és a sanitize teszt @vitest-environment direktívája (ez utóbbi a magyarázó része nélkül). Egy helyen kódot is kellett írni: a CommandBlock másolás-hibaágán a komment volt a catch egyetlen tartalma, és üres blokkot az eslint nem enged — a copied jelző visszaállítása került a helyére. A yaml, Dockerfile, Makefile, erb és markdown fájlokat nem érintettem. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,35 +1,16 @@
|
||||
module WarpEngine
|
||||
# Who the caller is, on the read-only side of the API.
|
||||
#
|
||||
# Distinct from UpdateAuthentication, which guards publishing: that one asks "may this
|
||||
# pipeline write to the catalog", this one asks "whose library am I looking at". The
|
||||
# answer is allowed to be nobody — an anonymous caller is a normal, supported caller,
|
||||
# and a catalog with no policy configured never needs one.
|
||||
#
|
||||
# The credential is a bearer token, because that is what a client can carry: it has no
|
||||
# cookie jar and no browser session.
|
||||
|
||||
module SubjectAuthentication
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
private
|
||||
|
||||
# The ApplicationToken behind the request, or nil.
|
||||
def current_access_token
|
||||
return @current_access_token if defined?(@current_access_token)
|
||||
|
||||
@current_access_token = resolve_access_token
|
||||
end
|
||||
|
||||
# Whoever the request is on behalf of — the host's own object, or nil.
|
||||
#
|
||||
# Two ways to be somebody, tried in that order. A bearer token is what a client
|
||||
# carries. A *browser* carries a session instead, and the engine has no idea what a
|
||||
# session is here — so a host that wants its signed-in visitors recognised on these
|
||||
# endpoints supplies a resolver:
|
||||
#
|
||||
# c.subject_resolver = ->(request) { request.env["warden"]&.user }
|
||||
#
|
||||
# Without one, a browser is simply anonymous, which is what it always was.
|
||||
def current_subject
|
||||
return @current_subject if defined?(@current_subject)
|
||||
|
||||
@@ -51,9 +32,6 @@ module WarpEngine
|
||||
record
|
||||
end
|
||||
|
||||
# A resolver that raises must not take the request with it: it runs on every read
|
||||
# endpoint, and a broken one would turn the whole API into 500s rather than into
|
||||
# anonymous requests, which is the honest fallback.
|
||||
def resolve_host_subject
|
||||
resolver = WarpEngine.config.subject_resolver
|
||||
return nil if resolver.nil?
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
module WarpEngine
|
||||
# Token authentication for the publishing (/build/*) endpoints.
|
||||
# The auth source is exclusive: in :database mode the shared secret is not
|
||||
# accepted, in :env mode DB tokens are not.
|
||||
|
||||
module UpdateAuthentication
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -9,8 +7,6 @@ module WarpEngine
|
||||
|
||||
attr_reader :current_application_token
|
||||
|
||||
# The token is accepted from the X-Update-Secret header only — in the URL
|
||||
# it would leak into proxy and access logs.
|
||||
def update_authorized?(required_scope:)
|
||||
token = request.headers["X-Update-Secret"].presence
|
||||
return false if token.blank?
|
||||
@@ -23,7 +19,7 @@ module WarpEngine
|
||||
|
||||
def env_secret_authorized?(token)
|
||||
expected = WarpEngine.config.update_secret
|
||||
# With no secret configured the endpoint stays closed.
|
||||
|
||||
expected.present? && ActiveSupport::SecurityUtils.secure_compare(token, expected)
|
||||
end
|
||||
|
||||
@@ -41,9 +37,6 @@ module WarpEngine
|
||||
true
|
||||
end
|
||||
|
||||
# Ownership enforcement applies only in :database mode (there is a token)
|
||||
# with enforce_software_ownership on. An ownerless software is up for grabs
|
||||
# until the backfill — backfill before enabling the enforcement.
|
||||
def software_ownership_authorized?(name)
|
||||
return true unless WarpEngine.config.enforce_software_ownership
|
||||
|
||||
@@ -56,8 +49,6 @@ module WarpEngine
|
||||
software.owner_type == token.owner_type && software.owner_id == token.owner_id
|
||||
end
|
||||
|
||||
# A first-published (or pre-backfill, ownerless) software gets the
|
||||
# submitting token's owner. Unrestricted (internal) tokens claim nothing.
|
||||
def claim_software_ownership(name)
|
||||
token = current_application_token
|
||||
return if token.nil? || token.unrestricted?
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
module WarpEngine
|
||||
# The device authorization grant, client side (RFC 8628).
|
||||
#
|
||||
# Both actions are unauthenticated, and have to be: the whole point of the flow is
|
||||
# that the caller has no credential yet. What protects it is that a device code is
|
||||
# useless until a signed-in person approves it on the host's own page.
|
||||
|
||||
class Api::Auth::DevicesController < ApiController
|
||||
before_action :ensure_identity_configured
|
||||
|
||||
@@ -34,9 +30,6 @@ module WarpEngine
|
||||
def token
|
||||
state, plain = service.poll(device_code: params[:device_code])
|
||||
|
||||
# The token rides on the one poll that finds the grant newly approved; a client
|
||||
# that loses it starts the flow again. Keeping a plain token around to hand out
|
||||
# twice would mean storing it, which is the thing this design avoids.
|
||||
body = { state: state.to_s }
|
||||
body[:token] = plain if plain.present?
|
||||
render json: body
|
||||
@@ -50,8 +43,6 @@ module WarpEngine
|
||||
@service ||= WarpEngine::DeviceGrantService.new
|
||||
end
|
||||
|
||||
# A deployment with no configured subject class has no sign-in at all, and says so
|
||||
# the same way GET /api/service does — by not offering it.
|
||||
def ensure_identity_configured
|
||||
return if WarpEngine.identity_configured?
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
module WarpEngine
|
||||
# Signing out: a client throws away its own token.
|
||||
#
|
||||
# Revocation is a soft delete on the ApplicationToken, so the record of which device
|
||||
# signed in and when survives it. The person's own list of devices — where somebody
|
||||
# revokes a token for a laptop they no longer have — is the host's page, because it
|
||||
# needs a session and a browser.
|
||||
|
||||
class Api::Auth::TokensController < ApiController
|
||||
resource_description do
|
||||
short "Client tokens"
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
module WarpEngine
|
||||
# What this deployment is and what it can do, in one unauthenticated request.
|
||||
#
|
||||
# This is how a client stops guessing. Before it existed, everything a client knew
|
||||
# about a store was compiled into the client — which endpoints to call, whether
|
||||
# signing in was a thing here, where to send somebody who wanted to buy something.
|
||||
# Every one of those is a property of the *server*, and a client that carries them
|
||||
# can only ever serve the one store it was built for.
|
||||
|
||||
class Api::ServiceController < ApiController
|
||||
resource_description do
|
||||
short "Service descriptor"
|
||||
|
||||
@@ -5,14 +5,8 @@ module WarpEngine
|
||||
formats [ "json" ]
|
||||
end
|
||||
|
||||
# Every response the engine serves names the version that served it, so a client can
|
||||
# branch on the engine's age without a round trip to ask. Set *before* the action,
|
||||
# not after: an error handled by `rescue_from` never reaches an after_action, and a
|
||||
# client needs the version most when something came back wrong.
|
||||
before_action :set_version_header
|
||||
|
||||
# Every read-only endpoint may be called with a bearer token; none of them requires
|
||||
# one. See WarpEngine::SubjectAuthentication.
|
||||
include WarpEngine::SubjectAuthentication
|
||||
|
||||
rescue_from StandardError do |e|
|
||||
@@ -39,12 +33,7 @@ module WarpEngine
|
||||
private
|
||||
|
||||
def set_version_header
|
||||
# The name is spelled out here rather than taken from WarpEngine::VERSION_HEADER on
|
||||
# purpose. A deployed process can end up with these controllers and an older
|
||||
# `lib/` — it happened on the first deploy of this feature — and a controller that
|
||||
# needs a constant from the newer half answers 500 to every request instead of
|
||||
# serving the catalog. A response header is not worth that fragility. The constant
|
||||
# is still the documented name, and a spec holds the two together.
|
||||
|
||||
response.headers["WarpEngine-Version"] = WarpEngine::VERSION
|
||||
end
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
module WarpEngine
|
||||
module Build
|
||||
# Woodpecker configuration-extension endpoint: on every pipeline start the
|
||||
# CI server POSTs the repo's marker file and receives the platform's full
|
||||
# pipeline YAML. GET renders the same thing as a preview.
|
||||
|
||||
class ConfigsController < ApiController
|
||||
resource_description do
|
||||
short "Woodpecker CI pipeline configs"
|
||||
@@ -58,9 +56,6 @@ module WarpEngine
|
||||
)
|
||||
end
|
||||
|
||||
# The first submitted config that parses as a marker (Hash with a `platform`
|
||||
# key). The docs call the key "configuration", the example-config-service
|
||||
# uses "configs" — accept both.
|
||||
def find_marker
|
||||
configs = params[:configuration].presence || params[:configs].presence || []
|
||||
configs.each do |config|
|
||||
|
||||
@@ -9,8 +9,6 @@ module WarpEngine
|
||||
short "Build artifact upload"
|
||||
end
|
||||
|
||||
# Release file naming convention: <name>-<version>.<ext> or
|
||||
# <name>-<version>-<target>.zip — the updater looks for these too.
|
||||
NAME_FORMAT = /\A[A-Za-z0-9._-]+\z/
|
||||
|
||||
api :POST, "/build/upload", "Upload a build artifact into the artifact directory"
|
||||
|
||||
Reference in New Issue
Block a user