ShyneBeauty is an internal Flask operations app. Only the latest code on main is supported.
- Admin authentication is handled by
shyne_app.routes.login()againstshyne_app.models.AdminUseron the auth bind (AUTH_BIND_KEY = "auth"). - Passwords are hashed with Werkzeug using
PASSWORD_HASH_METHOD = "pbkdf2:sha256:1000000"inshyne_app/config.py. - Password policy is enforced by
shyne_app.config.password_policy_errors():- minimum 12 characters
- must not contain email-address fragments
- must not match common or demo fallback credentials
- must differ from the current temporary password when applicable
- Account lockout is enforced in
shyne_app.models.AdminUser.register_failed_login()usingFAILED_LOGIN_THRESHOLD = 5andACCOUNT_LOCK_DURATION = timedelta(minutes=15)fromshyne_app/config.py. - IP-based login throttling is also enforced through
shyne_app.models.AdminLoginThrottle.
- Flask-Login session handling is initialized in
shyne_app/extensions.pyand configured inshyne_app/app.py. - Cookie settings in
shyne_app/app.py:SESSION_COOKIE_HTTPONLY = TrueSESSION_COOKIE_SAMESITE = "Lax"SESSION_COOKIE_SECUREdefaults toFalseindemo-devandTrueinlive-prodREMEMBER_COOKIE_HTTPONLY = TrueREMEMBER_COOKIE_SAMESITE = "Lax"REMEMBER_COOKIE_SECURE = SESSION_COOKIE_SECURE
- Revoked or incompatible sessions are cleared in
shyne_app.auth.invalidate_revoked_authenticated_session()andshyne_app.auth.revoke_authenticated_session(). - Forced first-login password rotation is enforced by
shyne_app.auth.enforce_password_change()andAdminUser.must_change_password. - HTTPS upgrades are enforced by
shyne_app.auth.enforce_https()whenAPP_RUNTIMEislive-prodandTRUST_PROXY_HEADERSis enabled.
- Global CSRF protection is enabled with
flask_wtf.csrf.CSRFProtectinshyne_app/extensions.pyviacsrf = CSRFProtect()andcsrf.init_app(flask_app). shyne_app/app.pysetsWTF_CSRF_ENABLED = True.- The login form includes a CSRF token via
templates/_form_helpers.htmlandtemplates/login.html:_form_helpers.htmldefinescsrf_input()login.htmlcalls{{ csrf_input() }}
- Authenticated POST forms such as
templates/change_password.html,templates/users.html,templates/account_settings.html,templates/mfa_challenge.html, andtemplates/mfa_enroll.htmlalso use the same helper.
- Request throttling is implemented in-memory in
shyne_app/rate_limit.py. - Enforcement happens in
shyne_app.auth.enforce_rate_limits()before route execution. - Sensitive POST paths covered by
_SENSITIVE_POST_PATHS:/change-password/account/settings/login/users/invite
- Additional sensitive POST patterns covered by
_SENSITIVE_POST_PATH_PATTERNS:/users/<id>/temporary-password/users/<id>/resend-invite
/admintraffic is separately rate-limited by prefix match incheck_rate_limit().- Current limiter characteristics:
- 30 requests per minute for sensitive POST buckets
- 60 requests per minute for
/adminbuckets - per-process memory storage using
_RateLimiter
- Role-to-permission mapping is defined in
shyne_app.config.ROLE_PERMISSION_MAP. - Permission checks are evaluated by
shyne_app.access.has_permission(). - Route-level enforcement is performed by
shyne_app.access.require_permission(). - Technical console access at
/admin/is gated byPERMISSION_ADMIN_CONSOLE_ACCESSinshyne_app/admin.py. - Business user management routes are gated by
PERMISSION_USERS_MANAGEinshyne_app/routes.py. - Persistent access and lifecycle audit rows are stored in the auth database:
shyne_app.models.AdminAccessEvent- table name:
admin_access_events
AdminAccessEventis used for permission denials and user lifecycle actions such as invite creation, invite resend, invite cancellation, activation, account creation, temporary-password reset, password change, role change, and status change.- Persistent login audit rows are stored in
shyne_app.models.AdminLoginEvent:- table name:
admin_login_events - recorded columns include
email,ip,success,failure_reason,user_agent, andcreated_at
- table name:
- Login events are written from
shyne_app.routes._record_login_event()for successful logins, bad credentials, account lockouts, IP throttle lockouts, and MFA-pending logins.
- Baseline headers are defined in
shyne_app.config.SECURITY_HEADERSand applied inshyne_app.auth.add_security_headers(). - Current baseline includes:
Content-Security-PolicyReferrer-Policy: same-originX-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000; includeSubDomainsis added only whenAPP_RUNTIMEislive-prod.- Authenticated and selected auth endpoints are marked
Cache-Control: no-storeandPragma: no-cacheviaNO_STORE_ENDPOINTSandadd_security_headers().
- Optional TOTP MFA is implemented with
pyotpinshyne_app.models.AdminUserand the/mfa/challengeand/mfa/enrollflows inshyne_app/routes.py. - Elevated roles are defined by
shyne_app.access.MFA_REQUIRED_ROLES:SuperadminDev Admin
- Users in those roles are nudged to enroll when
user_should_be_nudged_to_enroll_mfa()returns true, which currently means:- the role is in
MFA_REQUIRED_ROLES - MFA is not enabled
mfa_enroll_dismissed_atis stillNULL
- the role is in
- The enrollment page explicitly allows deferral. The
Skip for nowaction intemplates/mfa_enroll.htmlsetsAdminUser.mfa_enroll_dismissed_atinshyne_app.routes.mfa_enroll(). - MFA is encouraged for elevated access but is not mandatory in v1.
- Flask-Admin CRUD and export actions in
shyne_app/admin.pydo not emitAdminAccessEventrows for every action. - Schema evolution is still handled with ad hoc compatibility helpers such as
ensure_admin_user_access_columns(),ensure_customer_source_column(), andensure_runtime_auth_schema_compatibility()inshyne_app/models.py. There is no migration framework such as Alembic yet. - Some permission keys are defined but not currently used to gate any route:
shipping.viewshipping.editreports.viewusers.view
shyne_app/rate_limit.pyuses in-memory per-process buckets. Multi-worker or multi-instance production deployments should move to a shared backend such as Redis.
- Set a strong
SECRET_KEYoutside git.shyne_app/app.pyexpects it to be present and recommends generating one withpython -c "import secrets; print(secrets.token_hex(32))". - Use a unique
SECRET_KEYper environment. - Run production with
APP_RUNTIME=live-prod. - Set
SESSION_COOKIE_SECURE=truein production and serve the app only over HTTPS. - If the app is behind a TLS-terminating proxy, set
TRUST_PROXY_HEADERS=truesoshyne_app.auth.enforce_https()can trustX-Forwarded-Proto. - Confirm
ENABLE_DEV_TEST_ADMINis never enabled in production.shyne_app/app.pyforces it off inlive-prod, but operators should still verify the environment. - Back up both active database files together:
- default
demo-dev:instance/shynebeauty_demo.dbandinstance/shynebeauty_demo_auth.db - default
live-prod:instance/shynebeauty_live.dbandinstance/shynebeauty_live_auth.db - if
DATABASE_URLorAUTH_DATABASE_URLoverrides are used, back up those paths instead
- default
- Review
admin_login_eventsandadmin_access_eventson a fixed cadence for failed logins, permission denials, and unexpected account lifecycle changes.