162 Commits
Author SHA1 Message Date
andreandClaude Opus 5 aec7b47820 Replace Andmore ID SSO with local login
fitme3070.pt delegated all authentication to hub.andmore.pt: views/auth/*
were bounce stubs, and because this domain is outside the .andmore.pt cookie
scope the real handoff was a one-time HTTPS ticket redeem. Bring identity back
in-house — passwords live in users.password_hash, which the SSO migration
never dropped.

Removed: config/sso.php, IDENTITY_MODE, every SSO_* constant and env key,
identity_is_federated() and the cookie/ticket paths in the auth views and the
public-site bootstrap. ANDMORE_ID_URL stays — it is still the hub /notify
endpoint for outbound email/SMS, which the reset mails ride on, and has
nothing to do with authentication now.

Added, ported from the fotografia.andmore.pt sibling rather than invented:

  - auth_login() with transparent password_needs_rehash upgrades and the
    last_login trail; auth_register_client(); auth_set_password(); the
    single-use 2h reset tokens.
  - Real login / register / forgot_password / reset_password pages on a new
    anonymous shell (includes/auth_header.php). includes/header.php renders
    the backoffice sidebar and a logout button, which is wrong for a visitor
    who is not signed in yet.
  - safe_return_path(), so a crafted ?return_to= cannot bounce a freshly
    authenticated user off-site.
  - Per-IP throttling on login, register and reset-request. Successes count
    too, so a guessed password cannot be parlayed into stuffing the rest of
    the user list from the same IP.
  - views/user/set_password.php: admin override for anyone whose email is
    undeliverable. Prefers mailing an invite link so the backoffice never
    handles the password. A plain admin cannot act on a sysadmin, and nobody
    can use it on themselves — self-service goes through profile.php, which
    demands the current password so a hijacked session cannot lock the owner
    out.

Accounts provisioned during the SSO era have password_hash NULL. They get in
via the reset email, or by registering again with the same address:
auth_register_client() claims a passwordless row, but only a plain `client`
one, so knowing a staff address does not let anyone self-assign the role.

users.andmore_id is deliberately left in place. No code reads it any more, but
it records which central identity each row was linked to.

Deployment: apply database/migration_20260913_local_auth.sql (password_resets
+ auth_attempts) in the SAME sysadmin session that pulls this commit — the
forgot-password flow needs that table, and after the pull a login requires a
local password.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 21:00:57 +01:00
andreandClaude Opus 4.8 dc8a36e126 style: align hero image to top with object-position
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-12 21:50:26 +01:00
andre e6d4eabcc8 attempt to commit manually 2026-06-16 13:27:20 +01:00
andreandClaude Opus 4.8 3d460e50d1 Retire hub-content API endpoint via delete_ rename
Rename api/hub-content.php -> api/delete_hub-content.php so the old
content-pull path returns 404, disabling the hub social-post endpoint
without removing the implementation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 12:19:24 +01:00
andreandClaude Opus 4.8 03102cf1a9 Fix horário calendar save: surface real error, validate sala
The save AJAX endpoint swallowed DB exceptions behind a generic
"Erro ao guardar" with no logging, making failures undiagnosable.
Now log the real exception (same sink as the global handler) and
echo the detail back in dev.

Also validate `local` against the allowed sala set instead of only
its length, so an out-of-enum value returns a clean 422 instead of
a 500 (horario.local is a strict ENUM).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 12:16:11 +01:00
Platform Update d5ea2b963c Bump shared/platform-tools to 603ff84 2026-06-13 12:31:44 +01:00
Platform Update a9edb51a3c Bump shared/platform-tools to eaea122 2026-06-13 12:18:18 +01:00
andreandClaude Fable 5 026a3a2d32 Add remote-ops agent stub for the hub fleet control plane
One-line require of the platform-tools submodule agent. Needs HUB_OPS_SECRET
in .env and "Operações remotas" enabled on id.andmore.pt to respond.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:15:56 +01:00
andreandClaude Opus 4.8 7885b0b6ac SSO: redeem ticket over HTTPS instead of the central DB
This install is on external hosting and can't reach the central DB's private
LAN address (192.168.1.3), so the direct-DB burn always failed with
ECONNREFUSED. Redeem via the hub's api/sso_redeem.php over HTTPS (HMAC with
HUB_CONTENT_SECRET, ticket folded into the signature) and provision the
returned user into the local DB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 23:55:13 +01:00
andreandClaude Opus 4.8 9ad0c2b763 SSO: redeem one-time ticket on return from the hub
fitme3070.pt is off the .andmore.pt cookie scope, so am_sso never arrives
and login never provisioned the user. Redeem the ?sso_ticket=… the hub now
appends to the return URL instead.

- sso_redeem_ticket(): atomic single-use burn of sso_tickets in the central
  DB, bound to PLATFORM_SLUG, then provision the local row + FIT_SESS session.
- Extract _sso_provision_local() shared with the cookie path.
- views/auth/login.php redeems the ticket before the cookie fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 22:17:40 +01:00
andre 3e3382df57 idk 2026-06-12 21:20:23 +01:00
andreandClaude Opus 4.8 e68d357177 Initial import of fitme3070.pt platform
Plain-PHP fleet platform with id.andmore.pt SSO. Wires shared/platform-tools
as a submodule (platform_update, backups, git/migration tools) like the other
platforms.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:44:58 +01:00