Commit Graph

47 Commits

Author SHA1 Message Date
Zakaria 39664957fe fix(athena): run and list queries in their workgroup
StartQueryExecution ignored the workgroup: results always went to the default
bucket, even when the workgroup has a result location (or enforces it), and an
unknown workgroup was accepted. ListQueryExecutions ignored the WorkGroup
filter and returned every query.

Now, as in AWS, a query without a result location uses its workgroup's, an
enforcing workgroup's location always wins, an unknown workgroup is an
InvalidRequestException, and ListQueryExecutions returns the named workgroup's
queries (the primary workgroup's when none is named), newest first.
2026-10-25 17:54:43 -04:00
Zakaria 00b3af3813 Merge pull request 'fix(acm): keep the Export option on RequestCertificate' (#12) from fix/acm-request-export-option into main
Reviewed-on: #12
2026-10-25 17:21:08 +00:00
Zakaria d8d7dd021b fix(acm): keep the Export option on RequestCertificate
RequestCertificate always stored Export as DISABLED, so an exportable public
certificate could never be exported. Read Export and the transparency logging
preference the way UpdateCertificateOptions does, rejecting values other than
ENABLED and DISABLED.
2026-10-25 12:30:11 -04:00
Zakaria eaf5ca6ade Merge pull request 'docs: rebrand the README as TheCloud's AWS runtime' (#11) from docs/readme-thecloud into main
Reviewed-on: #11
2026-10-25 14:29:43 +00:00
Zakaria cc64729599 docs: rebrand the README as TheCloud's AWS runtime
Use TheCloud's logo and name, describe the repository as the fork of Floci
behind TheCloud with its own changes, and replace instructions for upstream's
published artifacts (Floci CLI, Docker Hub tags and release train) with
building and running this repository. Drop the upstream badges, comparison
table, community, sponsor, star-history and contributor sections, keep literal
identifiers (FLOCI_* settings, upstream package names), and credit Floci under
License.
2026-10-25 10:11:34 -04:00
Zakaria 9f17f72cc3 Merge pull request 'fix(apigatewayv2): verify tokens from Floci's own Cognito pools in-process' (#10) from fix/apigatewayv2-local-cognito-issuer into main
Reviewed-on: #10
2026-10-25 13:49:41 +00:00
Zakaria d8d94a773d fix(apigatewayv2): verify tokens from Floci's own Cognito pools in-process
A JWT authorizer on one of Floci's own user pools could never pass: the pool's
issuer is Floci's base URL (plain HTTP on localhost or a Compose service name),
and the verifier only fetches discovery and JWKS documents over HTTPS or from a
private IP literal. When the configured issuer is exactly one of this
emulator's pool issuers, read the pool's signing keys directly; they are the
keys it publishes at its JWKS URL. Every other issuer keeps the strict fetch.
2026-10-25 08:57:05 -04:00
Zakaria 957579c8b7 Merge pull request 'fix(cognito): honour PreventUserExistenceErrors=ENABLED on app clients' (#9) from fix/cognito-prevent-user-existence-errors into main
Reviewed-on: #9
2026-10-25 12:27:48 +00:00
Zakaria d9a1a36dc8 fix(cognito): honour PreventUserExistenceErrors=ENABLED on app clients
The setting was stored but never applied, so an unknown username always got
UserNotFoundException and revealed which accounts exist. With ENABLED, answer
as real Cognito does: password sign-in fails with NotAuthorizedException
"Incorrect username or password", ForgotPassword and ResendConfirmationCode
report a simulated delivery, and ConfirmForgotPassword and ConfirmSignUp fail
with CodeMismatchException. LEGACY and unset keep UserNotFoundException.
SRP and USER_AUTH flows are unchanged.
2026-10-25 08:26:34 -04:00
Zakaria 5d4b010710 Merge pull request 'fix(ses): sign in to the SMTP relay with the password, not as an OAuth token' (#8) from fix/ses-smtp-password-auth into main
Reviewed-on: #8
2026-10-25 11:57:20 +00:00
Zakaria 2486869b6b fix(ses): sign in to the SMTP relay with the password, not as an OAuth token
Vert.x mail prefers XOAUTH2 whenever the server offers it, so against Gmail the
app password was sent as an OAuth token and refused (535 5.7.8). Limit the
relay to the password-based SASL methods.
2026-10-25 07:52:52 -04:00
Zakaria 327173c8f6 Merge pull request 'chore(docker): read optional SES SMTP relay settings from ~/.config/thecloud/ses-smtp.env' (#7) from feat/ses-smtp-relay-env into main
Reviewed-on: #7
2026-10-25 11:46:41 +00:00
Zakaria dcb007f8b8 chore(docker): read optional SES SMTP relay settings from ~/.config/thecloud/ses-smtp.env 2026-10-25 07:44:42 -04:00
Zakaria 607ac0803b Merge pull request 'fix(cloudwatch): retain metric-math alarm query definitions (#4594)' (#6) from feat/cloudwatch-upstream-fixes into main
Reviewed-on: #6
2026-10-25 03:08:55 +00:00
Zakaria a9df81a294 chore: add the upstream helpers the composite alarm change builds on
Composite alarms from upstream (#6408) use SmithyValidationErrors for AWS's
validation messages, and their tests use the CfnStackWaits, PartitionCleanup
and ThreadStates test helpers and ReplacementCleanup.owedPhysicalIds. These
are copied from upstream as they were at that commit; none brings in other
behaviour.
2026-10-24 22:23:29 -04:00
Andre Kurait fa3327bf71 feat(cloudwatch): support composite alarm lifecycle (#6408)
(cherry picked from commit 922e36a472341861e97d1b5639656f3c4a3978d8)
2026-10-24 22:23:29 -04:00
Stuart Lang 83aafc0325 fix(cloudwatch): return empty CompositeAlarms and LogAlarms from DescribeAlarms (#6363)
* fix(cloudwatch): return empty CompositeAlarms and LogAlarms from DescribeAlarms

AWS sends every alarm collection even when it is empty. Floci only sent
MetricAlarms, so the AWS CLI's text output printed a leading None line
for each missing paginator result key.

Closes #6362

* test(cloudwatch): check empty alarm collections in sdk-test-python instead of bats

Drop the new AWS CLI bats file and assert the empty CompositeAlarms and
LogAlarms lists in the existing describe_alarms test, as boto3 leaves an
absent list out of the response.

(cherry picked from commit 42f5019231b446c727d6c94def9d1445263de2d7)
2026-10-24 22:19:09 -04:00
Mohith 24314f4432 fix(cloudwatch): reject PutMetricData datums without a MetricName (#6072)
* fix(cloudwatch): reject PutMetricData datums without a MetricName

MetricName is the one required member of MetricDatum. The JSON handler
stored a nameless datum under an empty name, and the Query handler
treated a member without MetricName as the end of the list, silently
dropping it and every later datum while still storing the earlier ones.

Both handlers now reject a missing or empty MetricName in top-level
MetricData and in EntityMetricData[].MetricData with MissingParameter
(400), the error PutMetricData declares for a missing required input,
naming the offending member. A whitespace name is still accepted, since
AWS constrains MetricName only to 1 to 255 characters. Every datum is
validated before any is stored, so a rejected request leaves no state
behind. On Query, a member index with no keys at all still ends that
MetricData list.

* fix(cloudwatch): return MissingRequiredParameterException on JSON and CBOR

CloudWatch is awsQueryCompatible. MissingParameter is the awsQueryError
code of the MissingRequiredParameterException shape, so it belongs in the
Query <Code> and the x-amzn-query-error header, while a JSON 1.0 or CBOR
body's __type names the shape. With the code in __type the AWS SDK v2
raised a generic CloudWatchException instead of the modelled one.

A package-private CloudWatchMetricsException keeps the Query code as its
error code and overrides jsonType() with the shape name, the same split
SwfException makes. The shared query-code table in AwsException is left
alone because SQS reports MissingParameter as its JSON type.

(cherry picked from commit b8d87df05413ea8c4f9497fa05f388c19d4124ec)
2026-10-24 22:19:09 -04:00
Zakaria e90978ae94 fix(cloudwatch): name the alarm's region without the partition catalog
The SNS alarm notification from upstream (#5918) looks up the region's display
name in AwsPartitions, which upstream added in a partition rework not taken
here. A table of the commercial regions' names gives the same Region field.
2026-10-24 22:19:09 -04:00
Mohith 21625c5a1b fix(cloudwatch): store datums sent in PutMetricData EntityMetricData (#6070)
The CloudWatch Agent sends PutMetricData with an empty top-level
MetricData and every datum nested under EntityMetricData[].MetricData.
Both the JSON 1.0 handler (also reached by Smithy RPC v2 CBOR) and the
Query handler read only the top-level list, so the agent's metrics were
silently dropped.

Both handlers now append each EntityMetricData entry's datums to the
top-level ones and store them under the request Namespace, with the same
parsing as top-level datums. Entity and StrictEntityValidation are
accepted and ignored. The per-request INFO logs of the raw request and
parsed datums are lowered to a DEBUG count.

Fixes #6066

(cherry picked from commit 03f57303969561b049c3ebba38f1fd9d2b68df4c)
2026-10-24 22:19:09 -04:00
mrjusepelo 2e0a438d70 fix(cloudwatch): publish SNS actions on alarm state transitions (#5918)
(cherry picked from commit f5811c3a0735be634b6122503237330826e0ba4a)
2026-10-24 22:17:17 -04:00
Allen Sanborn ccdf32335d fix(cloudwatch): raise ResourceNotFoundException when a tag operation names a resource that does not exist (#4783)
* fix(cloudwatch): raise ResourceNotFoundException when a tag operation names a resource that does not exist

TagResource, UntagResource and ListTagsForResource answered 200 for any ARN, so
tagging a typo'd or deleted alarm, dashboard or metric stream told the caller the
tag had been applied. AWS raises ResourceNotFoundException. All three operations
now resolve the ARN first and report a 404 when nothing answers to it.

The fix sits in the three services rather than in the handlers, because CloudWatch
Metrics serves these operations over both Query and JSON 1.1 and the two handlers
route to the same service methods. A handler-level check would have left the other
protocol still answering 200.

What the 200 was hiding is worth naming, because "return an error instead of 200"
understates it. The write path resolved the ARN and then did nothing with the miss:
tagResource and untagResource each ended in an ifPresent, so a tag against an
unknown ARN was discarded, and ListTagsForResource answered the empty map. So the
ARN was neither rejected nor recorded. Nothing was stored against a phantom
resource, which is the one mercy here, but a caller that wrote a tag and read back
an empty list had no way to tell "this resource has no tags" from "this resource
does not exist".

That is why ListTagsForResource is included rather than left alone. It is the one
operation whose answer changes for a caller doing a speculative list, and the one
whose leniency made the silent write survive a round trip. No test or caller in the
repository depended on the empty-map answer for an unknown ARN: the only internal
caller is CloudWatchDashboardCfnProvisioner.reconcileTags, and both of its call
sites pass an ARN that putDashboard has just returned.

The error code is ResourceNotFoundException, not the shorter ResourceNotFound that
GetDashboard, DeleteDashboards and SetAlarmState answer with. Those are two separate
shapes in the CloudWatch model, mapped to two exception classes by the SDK, and the
model declares the longer one on exactly these three operations.

CloudWatch Logs needed no change. Its own TagResource, UntagResource and
ListTagsForResource already resolve the log group and raise
ResourceNotFoundException, which the new test pins so the two halves of CloudWatch
tagging cannot diverge in the other direction.

* fix(cloudwatch): resolve a dashboard by either the regionless or the region-ful form of its ARN

Dashboards are a global resource and AWS documents their ARN without a region,
arn:aws:cloudwatch::<account-id>:dashboard/<dashboard-name>, on both the TagResource
and the ListTagsForResource pages. Floci mints a region-ful one. The dashboard
lookup now accepts both shapes deliberately, so a caller holding either resolves.

Accepting both is what stops the preceding commit regressing a caller that does the
documented thing. Until the tag operations raised on a miss, the divergence was
invisible on this path: the documented ARN matched no stored dashboard, and the
answer was a silent 200 with an empty tag map. Turning a miss into
ResourceNotFoundException would have turned that into a hard 404 for a dashboard
that exists.

Only an absent region stands in for the dashboard's own, so an ARN naming some other
region is still a miss and does not resolve to a same-named dashboard here.

What Floci mints is not changed. Clients may already hold the ARNs it has handed
out, so bringing the minted shape into line with AWS alters values already in
circulation and belongs in its own change; it is tracked separately. The tolerant
lookup is all that is needed to keep this path correct, and it is strictly more
permissive than before, so it cannot break a caller that works today.

Alarms and metric streams were checked rather than assumed: both mint region-ful
ARNs and AWS documents both region-ful, so the question is dashboards only.

Also reworded the not-found message on the alarm path to name the ARN that did not
resolve instead of asserting it was an alarm. Both tag handlers route every ARN that
is not a dashboard and not a metric stream to that path, including kinds AWS
considers taggable and Floci does not serve, a Contributor Insights insight-rule ARN
being the one AWS documents. Reporting that no resource matches the ARN is true of
those; "alarm not found" was not. No Contributor Insights support is added here.

* test(cloudwatch): assert a refused tag call left the real resource untagged

The not-found tests tagged the unknown ARN env=prod and then re-tagged the real
resource env=dev before reading it back, so a failed call that had written through
to the real resource would have been overwritten and the assertion would still
pass. Read the real resource's tags first; they must be empty.

* test(cloudwatch): import matchers and use configureAwsContentTypes

Import Matchers.not and java.util.Map in the new tests instead of writing
them inline, register the x-amz-json content types through
RestAssuredJsonUtils.configureAwsContentTypes() in place of registerParser
and the hand-built encoder config, and drop DeleteDashboards from the
requireDashboard Javadoc: only GetDashboard declares ResourceNotFound.

(cherry picked from commit 0c8d1848541392ff91991121d96586db564cb3f8)
2026-10-24 22:17:17 -04:00
Aniket Shukla cbb95ba26c fix(cloudwatch): retain metric-math alarm query definitions (#4594)
* fix(cloudwatch): retain metric-math alarm query definitions

* test(cloudwatch): use shared XML parser for alarm assertions

(cherry picked from commit 1d646c6fea70a8d4bfeea4d0ca6d8fc390ea6170)
2026-10-24 22:17:17 -04:00
Zakaria 90d5dc9427 Merge pull request 'fix(cognito): keep revoked tokens across restarts' (#5) from fix/cognito-revoked-tokens-reload into main
Reviewed-on: #5
2026-10-24 18:52:58 +00:00
Zakaria a59913bb38 fix(cognito): keep revoked tokens across restarts
RevokedTokenInfo serialized its derived isExpired() as "expired", which the
persistent store's ObjectMapper then refused to read back. On restart the file
was moved aside as .corrupt and every revocation was forgotten, so refresh
tokens revoked by sign-out worked again. The derived field is no longer
written, and files that carry it are read with it ignored.
2026-10-24 14:50:02 -04:00
Zakaria 2969939a99 Merge pull request 'feat(cloudfront): serve POST, PUT, PATCH and DELETE viewer requests (#4229)' (#4) from feat/cloudfront-upstream-fixes into main
Reviewed-on: #4
2026-10-24 18:38:31 +00:00
Zakaria 31196a0367 fix(s3): open an object's metadata and body stream together for CloudFront
The streaming change from upstream (#5278) serves S3 origins through
S3Service.openObject, which upstream added in a larger disk-streaming rework
(#4691) not taken here. This adds the same method on top of the existing
openObjectStream.
2026-10-24 13:58:20 -04:00
Abhigya 55a981069c fix(cloudfront): release origin connections when viewers disconnect (#5564)
(cherry picked from commit ee229af97569d5876f756f65ce7956726e2ed696)
2026-10-24 13:53:54 -04:00
Aymen JAMAAOUI 13e91cbb5a fix(cloudfront): reject a function runtime that CloudFront does not support (#5445)
Closes #5425

Adapted: kept only the runtime check; the duplicate-name check that surrounds it
upstream comes from a later CloudFront Functions change not taken here.

(cherry picked from commit 8deac0a4244449470d6bd4bfa8769f8921d1533d)
2026-10-24 13:53:54 -04:00
Priamo Germosen ec4664589b fix(cloudfront): stream origin responses and viewer request bodies (#5278)
A distribution read every origin response whole before answering the
viewer: an S3 origin's object through getObject, a custom origin's body
through the HTTP client into a byte array. A viewer's POST, PUT, PATCH
or DELETE body was likewise read whole before it was forwarded. So an
object or upload of about 2 GiB or more failed, and a smaller one could
run the heap out of memory.

S3 origins now open the object as a stream, the snapshot GetObject
serves, and custom origins return their response before its body is
read; either body is written to the viewer as it arrives, with its
Content-Length, or chunked as the origin sent it. A viewer's body is
streamed to the custom origin with its Content-Length, or chunked when
the viewer sent it chunked or, as HTTP/2 allows, with no length.

An origin response closed before its end, as when the viewer goes away
or a custom error page replaces it, cancels its request instead of
reading the rest of the body to reuse the connection. Since a streamed
response holds its connection for the whole transfer, one origin may
now use the whole connection pool, as CloudFront sets no per-origin
limit.

(cherry picked from commit 1f1d4f94dd8a179972bda124bc3aa47b9704f69b)
2026-10-24 13:53:10 -04:00
jwoa95 40ee0e7822 feat(cloudfront): forward viewer headers, cookies and query strings per origin request policy (#4230)
Custom origins only ever received the origin custom headers: the serving
controller built each origin request from them alone and dropped the
query string, whatever cache policy, origin request policy or legacy
ForwardedValues the matched cache behavior carried.

CloudFrontOriginRequestBuilder now decides what reaches the origin. With
a cache policy it forwards the union of the cache policy's
ParametersInCacheKeyAndForwardedToOrigin and the origin request policy
(every HeaderBehavior, CookieBehavior and QueryStringBehavior value).
Without one it applies legacy ForwardedValues, including the headers
CloudFront forwards by default. It also adds what CloudFront always
sends: Host for the origin unless the viewer Host is forwarded,
User-Agent "Amazon CloudFront", X-Forwarded-For, Via and X-Amz-Cf-Id.
Authorization is kept on the methods CloudFront does not cache, and
CloudFront-Forwarded-Proto, -Viewer-Address and -Viewer-Http-Version are
generated when a policy asks for them. Origin custom headers now replace
every same-named viewer header, and signing parameters are still never
forwarded.

The eight AWS managed origin request policies are served by their AWS
IDs and cannot be updated or deleted. With no origin request policy and
nothing selected by the cache policy, no cookies or query strings are
forwarded, as before.

Closes #4217

(cherry picked from commit f8f1b56ce9e9a1cbd63642ec1c4144fe8f6dd661)
2026-10-24 13:53:10 -04:00
jwoa95 aef858a47c feat(cloudfront): serve POST, PUT, PATCH and DELETE viewer requests (#4229)
The serving controller only declared GET, HEAD and OPTIONS, so a write
method on a distribution host matched the resource class but no method
and fell through to a bare 405, even when the cache behavior allowed all
seven methods.

POST, PUT, PATCH and DELETE are now served under the same AllowedMethods
check (403 "Invalid method." otherwise) and the same trusted key group
enforcement as the read methods. Custom origins receive the method, the
viewer request body, its Content-Type and its Authorization header, with
Content-Length taken from the body; GET and HEAD still reach the origin
without Authorization, as on CloudFront. In-process S3 origins answer
these methods with 403 Access Denied instead of forwarding them, which
is documented.

Closes #4216

(cherry picked from commit 4ca0e085cd9064cf7672cb050049e9564b48ed55)
2026-10-24 13:53:10 -04:00
Zakaria e557be1bc6 Merge pull request 'feat(cognito): show the client's identity providers on the hosted login page' (#3) from feat/cognito-hosted-login-providers into main
Reviewed-on: #3
2026-10-24 16:18:07 +00:00
Zakaria 174cb600d4 feat(cognito): show the client's identity providers on the hosted login page
The hosted login page now offers a "Continue with <provider>" button for each
of the app client's other SupportedIdentityProviders, carrying the same
authorize parameters (state, nonce, PKCE) into the federated flow, as Cognito's
managed login does. A client that lists only external providers gets the
buttons without the password form instead of an error.
2026-10-24 12:14:04 -04:00
Zakaria 2e7ebefca9 Merge pull request 'feat(cognito): hosted login page, PKCE and refresh-token grant' (#2) from feat/cognito-hosted-login into main
Reviewed-on: #2
2026-10-24 15:46:36 +00:00
Zakaria f4bbb6fab4 feat(cognito): hosted login page, PKCE and refresh-token grant
The pool's own users can now sign in through /oauth2/authorize the way apps
expect Cognito to work: without identity_provider (or with COGNITO) the
browser lands on a hosted login page, and a correct password redirects to the
registered callback with a one-time code.

- GET/POST /cognito-idp/login (and /login on custom domains), with a CSRF
  cookie and the same password rules and Pre/Post Authentication triggers as
  USER_PASSWORD_AUTH; wrong passwords, unknown and unconfirmed users get the
  same answer until the password is right
- a one-hour browser session, so the next authorize skips the form unless it
  sends prompt=login, and /logout with LogoutURLs validation
- PKCE S256 on both hosted and federated codes, and nonce in the ID token
- refresh_token grant on /oauth2/token
- codes now fire Pre Token Generation with TokenGeneration_HostedAuth instead
  of skipping the trigger
2026-10-24 11:09:37 -04:00
Zakaria e7fb8bb8d6 Merge pull request 'fix(docker): use Floci's address on the configured docker network' (#1) from fix/prefer-configured-docker-network into main
Reviewed-on: #1
2026-10-24 05:43:01 +00:00
Zakaria a1510d8cd6 fix(docker): use Floci's address on the configured docker network
Spawned containers (Lambda, RDS, EKS, EC2, ...) join services.docker-network,
but the address they were given to reach Floci was its IP on the first
user-defined network it is attached to. Once Floci is also attached to an
emulated VPC network, that address is unreachable and every Lambda times out
with "Failed to get next invocation". Prefer the configured network when
Floci is attached to it.
2026-10-24 01:35:31 -04:00
Zakaria bbe7e19554 small fixes 2026-10-15 10:25:56 -04:00
Zakaria 33c480e7e1 feat: Lightsail + svg animation + ec2 bug fixes 2026-10-11 14:19:49 -04:00
Zakaria 019570d8a4 bug fixes: backend services patches - Implemented and deployed systemd-backed AMIs 2026-10-07 13:22:32 -04:00
Zakaria 7135c81050 feat: Fargate, ECS, ALB,Codebuild, CodePipeline 2026-10-02 09:54:55 -04:00
Zakaria 283fcc7ab8 RDS port issue fixed 2026-09-30 18:38:54 -04:00
Zakaria 23d24288cd backend services patching 2026-09-30 17:46:10 -04:00
Zakaria cede01969e feat: paginate step functions executions 2026-09-27 23:35:21 -04:00
Zakaria 0aef7fb2d8 second first commit 2026-09-22 08:57:00 -04:00
Zakaria e88d7e2ff3 first commit 2026-09-22 08:55:56 -04:00