cas-puppeteer-scenarios
Skill11k starsChanged yesterdayApache-2.0View on GitHub
Write, fix and review the end-to-end puppeteer scenarios under ci/tests/puppeteer: run.sh behavior, init/bootstrap scripts, script.json properties and variations, storage-backend scenarios (JPA, Redis, Mongo, DynamoDB, LDAP, REST, localstack), cas.js helpers, actuator coverage and the CI retry rules. Use when touching ci/tests/puppeteer or ci/tests/**/run-*.sh, or cross-checking a change against its scenarios.
- Reads credentials
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 12
Prefer polling the service's own port over a container health check, and dump docker compose logs on the failure path: an unhealthy …
- Very specific: line 122
Read JPA tables by looking their names up (information_schema), not by entity name: CAS's physical naming strategy turns …
- Very specific: line 188
Every CAS MongoDb module pulls in Spring Boot's MongoDb data auto-configuration, whose gridFsTemplate needs exactly one injectable …
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CAS puppeteer scenarios
Puppeteer scenario init scripts
ci/tests/puppeteer/run.shruns a scenario'sinitScriptentries witheval "source ${script}", so they execute in the runner's own shell. Anexiton the success path therefore terminates the whole scenario run, which looks like the scenario dying silently right after the init script's last line of output. Let a successful init script fall off the end, and reserveexit 1for the failure path, which is what that exit is there for.ci/tests/ldap/run-ad-server.shstill carries anexit 0early return for the already-running case and has the same hazard.- Init scripts are sourced, so
set -e,set -uorpipefailin one would stay on for the rest ofrun.sh; 17 scenarios source such a script, and a leakedset -emade a failing test exitrun.shat its first attempt with code 1 (whole-script retry) instead of 5 after its in-process attempts.run.shturns those options off after each init script, so do not rely on them persisting. - Prefer polling the service's own port over a container health check, and dump
docker compose logson the failure path: an unhealthy container tells you nothing, while the service's logs say why it would not start. - In CI,
run.shlaunches the Gradle build (bootWar, orbootJarfor starter scenarios; native keepsbuild+nativeCompile) in the background and runs npm install, ESLint, bootstrap and init scripts while it builds, waiting on the build process only before launching the CAS instance that needs it. Init and bootstrap scripts must therefore never depend on the built artifact, and a fixedsleepin them now runs while the build competes for CPU, so poll instead.PUPPETEER_BUILD_OVERLAP=falserestores the sequential order.PUPPETEER_BUILD_CTRis the build timeout in minutes, measured from launch. run.shlints the scenario before running it and stops on any ESLint error. Page globals used insidepage.evaluateorpage.waitForFunctioncallbacks ($,ace, Palantir classes) must be named in the script's/* global ... */header, orno-undeffails the job; runnpx eslint scenarios/<name>/script.jsfromci/tests/puppeteerbefore handing a scenario off.- A container answering HTTP is not a container ready for setup calls. Apache Syncope's Tomcat answers
/syncope/several seconds before its content loader populates the empty Master domain, and admin REST calls made in that window fail withAuthorizationDeniedException(seen once the build overlapped the init scripts and slowed startup).ci/tests/syncope/run-syncope-server.shtherefore waits forStarted SyncopeCoreApplicationin the container log; readiness waits in other init scripts should likewise key off the application's own started signal, bounded, withexit 1on timeout. - Scenario matrix jobs in
functional-tests.ymlrestore the Gradle User Home withcache-read-only: trueand do not setcache: 'gradle'onsetup-java; saving from every one of the ~560 jobs cost ~12 s each and churned the Actions cache, and the two actions caching the same directory conflict. - The matrix jobs cache the Node.js install under
${{ runner.tool_cache }}/node/<NODE_VERSION_REQUIRED>with a key that is that exact version, restored beforesetup-nodeand saved only from the default branch on a miss. A version change inNODE_CURRENTor a scenario'srequirements.nodejsis a new key, sosetup-nodedownloads the instructed version; keep those values exact versions (a range would never match the cached directory) and do not addcheck-latest. - The matrix jobs start
ci/tests/puppeteer/prefetch-docker-images.sh <scenario>right after checkout (innative-tests.yml, afterConfigure CIand only when the scenario's native run is enabled, since init scripts run only in the--nrstep and the pulls then finish during the native build): it finds the images the scenario's bootstrap, init and ready scripts need (theirdocker run/pull/buildcommands,*_IMAGEand*COMPOSE_FILEassignments, compose files, DockerfileFROMlines, and theci/testsscripts they call) and pulls them in the background while the job sets up, logging to$RUNNER_TEMP/docker-prefetch.log(printed by theDocker Prefetch Summarystep). Images a script builds itself, compose services withbuild:and run.sh's owncas-<scenario>images are skipped, and so are scenarios that are disabled or miss aconditions.envvariable (run.sh skips those too). It is best-effort: an image it misses or is still pulling is pulled by the init script as before, so new init scripts need no changes, but keep image references literal or in a plain*_IMAGE=assignment so they are found.--listprints what it would pull. Images are not stored in the Actions cache: it is already at its limit, and restoring a tarball is a download too. initScriptentries run once per instance, right before that instance starts;bootstrapScriptruns once for the scenario. A multi-instance scenario whose nodes share a backend (Pulsar, MySQL, MongoDB, Kafka, Redis) must start it frombootstrapScript: as an init script it is restarted under the first node before the second one starts, which wipes its state and leaves node 1's connections to reconnect (or not) to a fresh server. The symptom is node 1 silently missing everything node 2 publishes.- Instances whose resolved dependencies are identical share one build:
run.shbuilds only the first instance of each dependency set and copies its artifact to the others. Only instance-specificdependenciescause another build. Instances still start one after another, because several multi-instance scenarios need instance 1 up before instance 2 starts (Spring Boot Admin client registration, passive service-registry replication, cas2cas delegation). run.shexit codes carry meaning for theRun Testsretry (retry_on_exit_code: 1): 1 is a setup failure worth another attempt (init scripts, containers, npm), 2 a failed build, 3 a build that exceededPUPPETEER_BUILD_CTR, 4 a CAS instance that exited or did not answer its health check withinPUPPETEER_STARTUP_TIMEOUTseconds (300 in CI, unlimited locally; a single probe may use the whole remaining budget, since some login pages take ~30 s to render, e.g.thymeleaf-templates-restbefore its template server is up), and 5 a scenario script that still failed after its in-process attempts (3 in CI, against the running server). 2-5 fail at once; a whole-script retry cannot fix them and repeats the build and startup. Keep new failure paths on 1 only if a rerun can help.tests.yml,functional-tests.ymlandnative-tests.ymlretry failures the same way: a booleanretryFailedScenariosdispatch input gates a step that runsci/tests/select-failed-jobs.sh <list.json>, which narrows the listed categories/scenarios in place to the jobs that failed, timed out or were cancelled in the latest attempt of the workflow's previous completed run, and writes a step summary. Call that script from any other matrix workflow; do not inline the logic. It matches job names, so a matrix job'sname:must stay exactly the category or scenario, and the listing job needspermissions: actions: read. No matching job leaves[], so every matrix job that consumes the list is skipped when it is empty (an empty matrix fails the job). A category retry disables PTS and coverage and skips thecoveragejob, which would otherwise upload a partial report.run.shjoins a scenario'spropertieswith spaces and passes them unquoted, so a value containing a space is cut at the space (display-name=My CAS Onearrives asMy). Use values without spaces inscript.json.ci/tests/keycloak/run-keycloak-server.shpolls thecasrealm's discovery document; Keycloak answers HTTP well before the realm import finishes, and a pac4j OIDC client that fetched its metadata in that window stays broken for every later in-process attempt.run.shinstalls the Chrome build Puppeteer pins (npx puppeteer browsers install chrome) afternpm install, because npm can finish without puppeteer's postinstall having downloaded it, which only shows up later asCould not find Chrome.- A scenario that navigates to
https://localhost:9859/anything/...needsci/tests/httpbin/run-httpbin-server.shamong its init scripts; without it the redirect lands onchrome-error://and the ticket assertion fails. - Webflow executions are client-side by default (
cas.webflow.session.storage=false): theexecutionvalue is the signed and encrypted flow state, so replaying one simply resumes that state, and "reusing a consumed execution" is only meaningful with server-side storage (http-session-ticket-registrycovers it). Server-side keys are numbered per session (e1s1,e2s1, ...), and a new HTTP session numbers frome1again, so opening the login page before the replay hands out the very key being replayed and the "replay" logs in. Replay from a non-CAS page, after deleting the TGC withpage.deleteCookie({name: "TGC", url: "https://localhost:8443/cas/"})(cas.deleteCookies(page)only sees cookies of the page's current URL, which on httpbin excludes the/caspath). A well-formed but altered execution fails decoding withClientFlowExecutionRepositoryException, andFlowExecutionExceptionResolverredirects to a fresh flow (302). A malformed one (nouuid_datashape) raisesBadlyFormattedFlowExecutionKeyException, which the resolver now also restarts; it drops anyexecutionquery parameter from the redirect, which is what avoids the redirect loop that once made it skip this exception.webflow-clientside-sessionsexercises all three cases plus a double submit and the back button. The ticket-granting cookie value embeds theUser-Agentwhencas.tgc.pin-to-sessionis on; a request without one is pinned to a placeholder (DefaultCasCookieValueManager), so the cookie only matches later requests that also send none. It used to throw and answer 500. actuator-endpoint-rolesis the table-driven check of endpoint security: every row is called anonymously (401), ascasviewerwithoutADMIN(403) and ascasadmin(2xx, or "reachable" for operations needing a payload). Users come from itsusers.json(cas.monitor.endpoints.json.location). Add a row there when adding an actuator endpoint whose module needs no external service; rows default toContent-Type: application/json, so give operations that consume form parameters (thecasValidateones)contentType: FORM, or they answer 415.- To prove CAS refuses a tampered SAML2 AuthnRequest, intercept the browser hop from the SP to
/cas/idp/profile/SAML2/*/SSOwithpage.setRequestInterception(true)(seesaml2-idp-login-acs-signed). POST binding: rewriteSAMLRequestinpostDataandrequest.continue({postData}). Redirect binding: inflate withzlib.inflateRawSync, alter, deflate, thenrequest.abort()andcas.gotothe altered URL rather than overridingurlon a redirected request. Assert that the tampering really happened, otherwise a request without the attribute passes for the wrong reason. cas.newPage(browser)returns the browser's first tab when one exists; it opens a new one only withcas.newPage(browser, true). A helper that takes a page, intercepts its requests and closes it must force a new page, or it closes the scenario's main page and later calls fail with "Attempted to use detached Frame" (saml2-idp-login-acs-signed).- Before writing a negative assertion, read the server code for the exact error. Each token-endpoint failure surfaces differently: device code reuse →
access_denied(InvalidOAuth20DeviceTokenException), a mismatched ST service →INVALID_SERVICEand the ticket is consumed. Where the specification names the error (CIBA Core §11:authorization_pending,invalid_grant), the scenario asserts the specification even when the current code differs, and the hand-off says so. fromin actuator and ticket registry queries (ssoSessions?from=,TicketRegistryStreamCriteria) is a 0-based offset in every registry. A scenario that deletes all SSO sessions passesfrom=0(or omits it);from=1leaves the first session alive, which only shows once that session outlives the scenario's other timeouts, such as a remember-me TGT in a browser profile that later browsers share.- In CI,
run.shbounds each script attempt withPUPPETEER_SCRIPT_TIMEOUT(420 seconds; 0 locally, meaning no limit). A script whose steps all finish but whose node process never exits then fails that attempt and is retried, instead of running into the 20 minute job timeout. - AOT training runs extract the built artifact with
-Djarmode=tools extractand launch what it produces. Since Spring Boot 4.2 (gh-51372) extracting a war writescas/cas.jar(root web content moved toMETA-INF/resources/), no longercas/cas.war;run.shlaunchescas/cas.jarand falls back tocas/cas.<projectType>. When a Spring Boot bump fails every AOT scenario at once, diffloader/spring-boot-jarmode-tools/.../ExtractCommand.javabetween the two tags first. ci/tests/aws/run-aws-server.shserves theAmazonWebServicesunit test category, not only scenarios: tests start as soon as it returns, and@EnabledIfListeningOnPort(port = 4566)passes while localstack's services are still starting. Components that create their AWS resources while starting (the log4j2SQSAppendercreates its queue instart(), and log4j drops an appender whose start fails) then fail the test. The script removes any previous container withdocker rm -f, waits until it is gone (a--rmcontainer still being removed makesdocker run --nameconflict), and polls/_localstack/healthuntil every service it enables isavailableorrunning, bounded,exit 1on timeout.- A proxy callback (
pgtUrl) is contacted by CAS itself (ProxyAuthenticationHandlerexpects an acceptable status), so it must be a local endpoint: GitHub stopped answering requests from the CI runners with an acceptable status, which failedticket-validation-casv3-pgtand thecasunit tests usinghttps://www.github.com. Scenarios use httpbin (https://localhost:9859/anything/..., with--cas.http-client.host-name-verifier=nonefor its certificate); unit tests usenew MockWebServer(true)withcas.http-client.host-name-verifier=none, which covers the context's http client only: aCas20ProxyHandlerbuilt in the test (delivers the PGT IOU) needs its own client onCasSSLContext.disabled(), or the IOU callback fails with PKIX path errors. A public URL is fine only where nothing contacts it (a redirect target, a service id, a failure path). - WebAuthn scenarios drive the CDP virtual authenticator (
cas.createWebAuthnVirtualAuthenticator(page, protocol, resident, options)spreadsoptionsinto the authenticator, e.g.isUserVerified).cas.setWebAuthnSignCount(device, n)replaces each credential with the same key and a different sign count, which is howmfa-webauthn-register-logout-loginproves a cloned authenticator (count going back) is refused;WebAuthn.setUserVerifiedtoggles user verification mid-scenario. - The OIDC front-channel logout page (
casPropagateLogoutView) calls eachlogoutUrlwith JSONP, so the front-channel request shows up as a script request in the browser. Capture it withpage.on("request")filtered on the URL and readiss/sidfrom its query; httpbin answers it with JSON, which the page ignores. cas.generateOtp(config, stepOffset)builds the code for a neighbouring time step without waiting; use it to testwindow-size(the next step is accepted with the default window, refused withwindow-size=1) and replay (USED_OTPSrecords the codes already sent).parseOtpAuthenticationUrlreadsdigitsandperiodfrom the URI.- A token sent by a client may be a JWT (
jwtAccessToken,jwtRefreshToken). Every lookup of a client-supplied token in the ticket registry must first go throughOAuth20JwtAccessTokenEncoder.toDecodableCipher(...).decode(token), which returns non-JWT values unchanged; revocation and the refresh grant skipped this.OAuth20RefreshTokenAuthenticator(public clients presenting a JWT refresh token) still does, because it has noJwtBuilder. .gitignoredrops everyci/tests/puppeteer/**/*.jwks: client key sets a scenario ships must use another extension (client-public.json), or the scenario passes locally and fails in CI with a missing file.- The REST multifactor trigger (
cas.authn.mfa.triggers.rest) sends a GET with a JSON body unlessmethodis set; a mock endpoint registered withapp.postnever sees it. Setcas.authn.mfa.triggers.rest.method=POST. WebAuthn.getCredentialsomitsrpIdfor non-resident credentials, andWebAuthn.addCredentialrefuses a credential without one;cas.setWebAuthnSignCountpasses the relying party (defaultlocalhost).- Navigating to the page already shown plus a
#fragmentdoes not reload it: to see data imported through an actuator,cas.gotothe page without the fragment, then click the tab. - The OIDC per-service "signing" key set (
fetchJsonWebKeySetForSigning,InternalJwtAccessTokenCipherExecutor) also verifies what the application signs (request objects viaDefaultOAuth20RequestParameterResolver), so public-only keys must stay in it; only keys markeduse=encmay be dropped. - CAS shell validation: the
quitcommand deliberately returns EXECUTION_ERROR and Spring Shell then prints a CommandExecutionException trace;.github/workflows/validation.ymlonly counts exception lines before the quit message. - SPNEGO scenarios get a real KDC from
ci/tests/kerberos/run-kerberos-server.sh(port 10088, not 88: macOS runs its own KDC on 88). Chrome sends Negotiate tokens only with--auth-server-allowlist=localhost; it readsKRB5_CONFIGandKRB5CCNAMEfrom the environment given tocas.newBrowser(a missing cache makes it show the 401 body, which is the login form in mixed mode). CAS must get the port fromkerberos-conf: blankcas.authn.spnego.system.kerberos-kdcandkerberos-realm, because Java ignores krb5.conf once both system properties are set and they cannot carry a port. jcifs-ext logs the service principal in withjcifs-service-passwordonly whenjavax.security.auth.useSubjectCredsOnlyis true (cas.authn.spnego.system.use-subject-creds-only=true). With the CAS default (false) it skips its JAAS login and the JDK looks for a keytab instead, which fails with "Cannot find key of appropriate type to decrypt AP-REQ".
Storage backend scenarios (service registry, SAML2 metadata, MFA devices, consent)
- One scenario covers several storage backends of a feature through
variationsinscript.json:run.shruns the scenario once per variation, adding its properties and exportingSCENARIO_VARIATION. The build carries every backend module, and each variation switches the others off withCasFeatureModule.<Feature>.<module>.enabled=false(Google Authenticatorjpa/redis/mongo/ldap/dynamodb, trusted devicesjdbc/redis/rest/dynamodb, consentldap/mongo/redis/rest/dynamodb, WebAuthnjpa/mongo/redis/ldap/dynamodb/rest); the init scripts start every backend. Examples:mfa-webauthn-register-logout-login,mfa-gauth-login-encrypted,mfa-gauth-login-trusted-devices-storage(formerly-jdbc),attribute-consent-storage,aup-login-storage,authn-throttle-storage(formerlyauthn-throttle-with-jdbc; each throttle reads its own store, so the audit module it needs stays on: JDBC and MongoDb throttles read the audit trail),passwordless-login-storage(formerlypasswordless-login-jpa; the mongo variation also keeps the account in MongoDb),http-session-storage(formerlyhttp-session-mongodb). Storage modules that share a bean name without a module toggle cannot ship together; WebAuthn's had none until its storage auto-configurations gotmodule = "...". - Each such scenario also reads the backend directly with
cas.dockerExec(container, args)(containers:mariadb,postgres-server,mongodb-server,redis_server_master,ldap-server,localstack), so a variation that silently fell back to the in-memory default fails. LDAP-backed repositories that keep records in an attribute of the user's entry (Google Authenticator, consent:description,search-filter=cn={0}) need the entry first:ci/tests/ldap/add-ldap-entries.sh <ldif>waits forldap-serverand adds it. The 389ds schema has no CAS attributes, so point the attribute setting at a standard one the entry's object classes allow (description,carLicense,postalCodeoninetOrgPerson). - CAS Redis templates write values LZ4-compressed and JDK-serialized, so
redis-cli GETshows nothing readable; checkEXISTSor--scanfor keys instead. - Storage tests that extend
BaseAcceptableUsagePolicyRepositoryTestsonly check that a submitted decision is read back whenhasLiveUpdates()is true; the MongoDb and Redis repositories stored decisions without ever reading them, and their tests did not notice. Use a random principal there, since the backend outlives the test. cas.doDelete(url)swallows failures unless a failure handler is given; assert a status withcas.doRequest(url, "DELETE", headers, status).- Read JPA tables by looking their names up (
information_schema), not by entity name: CAS's physical naming strategy turnsJpaMultifactorAuthenticationTrustRecordintojpa_multifactor_authentication_trust_record, and aLIKE '%WebAuthn%'also matches thewebauthn_sequencetable. MariaDBLIKEignores case. - The
mfa-gauth-login-trusted-devices-storageoutage check needs the storage to report itself unavailable when its backend is down (MultifactorAuthenticationTrustStorage.isAvailable): the webflow then skips trusted devices and asks for the token. JPA checks a connection; Redis pings. - Service registry scenarios call
cas.verifyServiceRegistry(backend)(incas.js), passinginsert(definition)(write straight into the backend, bypassing CAS) andcontains(id). It checks load (scheduled reload picks up the external definition), save through theregisteredServicesactuator, rename, and delete, each confirmed in the backend and after a reload. Such a scenario needscas.service-registry.core.init-from-json=false, because with it on the embedded JSON registry joins the chain and answers in place of the backend, plus a 2 scas.service-registry.scheduleand httpbin.service-registry-ldap(389dsldap-server,ou=services, object classaccount) andservice-registry-aws-s3(localstack,docker exec localstack awslocal s3api) are the examples. - A property value with a space (
cn=Directory Manager) goes throughenvironmentVariablesand${VAR}inscript.json, aspassword-sync-ldapdoes. - localstack accepts bucket names real S3 rejects, so a scenario cannot catch an invalid bucket name; assert names in a unit
test.
AmazonS3ServiceRegistry.determineBucketNamekeeps[a-z0-9.-], collapses.., and caps the name at 63 characters with-<id>last. Object metadata is built only from non-null values (Map.ofrejects null). - SAML2 metadata source scenarios (
saml2-idp-login-metadata-redis,saml2-idp-login-metadata-git, and the oldersaml2-idp-login-sp-metadata-mongodb) keep IdP metadata in the backend, fetch it withcurlinready.shbefore starting SimpleSAMLphp, and manage SP metadata throughsamlIdPRegisteredServiceMetadata/managers/<ResolverSimpleClassName>; itsDELETEconsumes form-encoded requests and answers 204. The Git scenario uses a local bare remote under/tmp/cas-saml-metadata-git(push-changes=true, or the IdP metadata is only committed locally) and commits SP metadata to it by hand, the way the docs describe. GitSamlRegisteredServiceMetadataResolverreadssp-metadata/<id>-<name>.xml(written by CAS) and<name>.xml(by hand): the id prefix counts only when numeric, and a hand-committed file's id is derived from its name, so it is stable across loads.- DynamoDB variations and scenarios read DynamoDB Local (
dynamodb-server, port 8000, no CLI inside) over HTTP withcas.dynamoDbScan(table)andcas.dynamoDbRequest(operation, payload)incas.js, which sign requests with SigV4. DynamoDB Local keeps one database per access key and region, so give CAS explicitcredential-access-key=AKIAIPPIGGUNIO74C63Z, the matching secret key andregion=us-east-1(notlocal-instance=true, which takes credentials from the environment), plusdrop-tables-on-startup=true. Items come back in the wire format (item.principal.S). For an outage (docker pause dynamodb-server) setconnection-timeout,socket-timeoutandclient-execution-timeoutto about1000, or each SDK call retries for many seconds. - REST backends in scenarios use an in-script Express server (
aup-login-rest,attribute-consent-storage'srestvariation,audit-rest, ports 5611-5619; 5619 is the REST multifactor trigger ofmfa-provider-selection-trigger-groovy). A REST configuration source must answer before CAS boots, soconfiguration-properties-reststartsconfig-server.jsfrominit.shand stops it inexit.sh. - Service ids in scenario JSON are regular expressions:
^https://localhost:9859/anything/1also matches/anything/10, so give new services distinct path names. A scenario that setscas.service-registry.json.locationhas no catch-all service, which is what lets it assert that a URL is refused. - The login page is a webflow response, which sets its own cache headers; check per-service security headers on
/cas/p3/serviceValidate?service=...instead (service-security-headers). - A ticket-granting ticket's last used time changes only when a ticket is issued from it, not when the login page is shown; time-based SSO participation and throttled timeouts are measured from that.
- Values encrypted with a service public key use RSA with PKCS#1 v1.5 padding, which Node no longer decrypts: decrypt with
RSA_NO_PADDINGand drop everything up to the first zero byte after the second byte. - localstack scenarios (
configuration-properties-aws,simple-mfa-login-aws-ses,logging-config-sqs,audit-aws-firehose) seed and read AWS resources withdocker exec localstack awslocal ...; SES mail is read fromhttp://localhost:4566/_aws/ses. template-accessibilityruns axe-core (@axe-core/puppeteer) and fails on serious or critical WCAG 2.1 A/AA violations only. Element ids must be unique per page: a duplicate id referenced byaria-describedbyorlabel foris a critical violation (the Google Authenticator page with multiple-device registration on still has two#token).layout.htmlfades the page content in (.fade-in, 0.7 s, opacity and blur), so axe run right after a navigation reportscolor-contraston everything in the card; wait fordocument.getAnimations()that end (skip infinite ones, such as the notification bell) before analyzing.- The puppeteer webapp runs without
--add-opens java.base/java.util, but Gradle tests open it. Code that reflects on private JDK fields (Kryo serializers in the Memcached registry) passes unit tests and fails in scenarios and plainjava -jardeployments. - Servlet filters that extract the service treat
UnauthorizedServiceExceptionas an unauthorized service (status 403,RequestDispatcher.ERROR_EXCEPTION, continue the chain), and MVC interceptors ignore it, leaving the refusal to the login flow. Thrown from either, its@ResponseStatussends a 403 whose error dispatch fails the same way: empty body. - Spring Cloud closes the bootstrap context once a refresh is done, so a
PropertySourceLocatorthat closes its client indestroy()leaves the located property source with a closed client. The AWS configuration sources take aSupplierof clients and open and close one per refresh or change. - CAS reads a plain number in a
@DurationCapablesetting as seconds (Beans.newDuration), sosocket-timeout=1000means 1000 seconds; write durations asPT1S. - Every CAS MongoDb module pulls in Spring Boot's MongoDb data auto-configuration, whose
gridFsTemplateneeds exactly one injectableMongoTemplate. A module with zero (onlyMongoOperationsbeans) or twoMongoTemplatebeans breaks startup unless its templates are@Bean(defaultCandidate = false)and injected by@Qualifier; Boot then creates its own template. Every CAS MongoDb template is a non-default candidate except the ticket registry's (@Primary); keep it that way for new ones.mfa-gauth-trusted-devices-mongobuilds two MongoDb modules together. - The Cassandra and Memcached ticket registries encrypt by default with random keys, so multi-node scenarios set shared
cas.ticket.registry.<registry>.crypto.encryption.key/signing.key. Cassandra in CI uses client TLS: setcas.http-client.host-name-verifier=noneandssl-protocols=TLSv1.2, and read it withdocker exec cassandra cqlsh --ssl -e "<query>"(unquoted table names are lowercase: the TGT table iscas.ticketgrantingticketstable). Memcached:statsovernc; the ticket registry also needsmemcached-spy(ormemcached-aws-elasticache) independencies. - CAS Redis values are a 4-byte big-endian length followed by an LZ4 block of the Java-serialized object; a fixture such as
redis-authentication-login/accounts.jsonmust be regenerated when the serialized class changes. - The Kafka console consumer can exit non-zero on
--timeout-ms; read its output withspawnSync. The RADIUS test server takescasuser/Mellon, secrettesting123, PAP; both RADIUS modules needclient.inet-addressset. - WebAuthn LDAP keeps registrations in an attribute of the user's entry (
descriptionon 389ds). The OIDC MongoDb keystore needs bothcas.authn.oidc.jwks.mongo.hostand.collection; client JWKS go to<collection>-ClientJwksStore. - The Google Authenticator JSON repository writes used scratch codes back to its file, so a scenario with variations or
retries runs out of codes. Copy the account file to
${TMPDIR}from an init script (runs per variation) and pointcas.authn.mfa.gauth.json.locationat the copy, asmfa-gauth-login-trusted-devices-storagedoes; unpause paused containers in afinally. - Scenarios that change a backend must put it back at the start, because
run.shretries a failed script against the same CAS instance and containers (configuration-properties-awsdoes this). Keep property values free of spaces (run.shcuts them), e.g.mail.text=Username:${username}. - A SAML2 metadata module with
SAMLIdentityProviderMetadataon replaces the file-system IdP metadata beans even when its own IdP metadata is off; scenarios that keep IdP metadata on disk switch it off withCasFeatureModule.SAMLIdentityProviderMetadata.<module>.enabled=false.
Actuator endpoint coverage in scenarios
samlIdPRegisteredServiceMetadata: every operation insaml2-idp-login-metadata-redis(and-git);oidcVcStatus:oidc-verifiable-credentials, reading the published status list back (2 bits per entry, LSB first, zlib, base64url);redisTicketsCache:redis-ticket-registry-loadbalanced(the cache is per node; another node's delete evicts it);qrDevices:qrauthn-login, which needscas.authn.qr.json.location(without it every device is authorized and nothing is stored);passwordless:passwordless-delegated-login;duoPing,duoAccountStatus,duoAdmin:mfa-duo-universal-login(runs only where the Duo secrets exist) and, without providers,actuator-endpoint-roles.gcpLogshas no scenario: it calls the real Cloud Logging API, no emulator exists, and addinggcp-loggingto a build pulls in Spring Cloud GCP's Stackdriver trace auto-configuration, which stops CAS from starting without Google credentials (exit code 4 inactuator-endpoint-roles).qrauthn-loginlogs in over a STOMP callback;connectAndLoginreturns a promise that settles when that callback's assertions finish, and the browser is closed only after it, or the assertions hit a detached frame.
Google Authenticator credential repositories (all backends)
- One puppeteer scenario can cover several storage backends: put every backend module in
dependencies, list the init scripts comma-separated ininitScript, and give each backend avariationsentry that turns the others off with--CasFeatureModule.<Feature>.<module>.enabled=false(each backend auto-configuration carries@ConditionalOnFeatureEnabled).SCENARIO_VARIATIONholds the variation name.mfa-gauth-login-encrypted-storesdoes this for GAuth on JPA and Redis, with encryption on.
Trending
Quiet since August.about the whole repository
Who's talking about apereo/cas
- Stack Overflow“What is the relation between CAS vs SAML?”Stack Overflow · Apr 28, 2017 · 23 votes
- strapi/strapi“Add support for login via cas - github.com/apereo/cas”GitHub · Sep 26, 2020 · 8 reactions
- Stack Overflow“Opensaml error receiving correct unmarshaller”Stack Overflow · Apr 10, 2017 · 6 votes
- Stack Overflow“How to enable file-based authentication in CAS 5”Stack Overflow · Apr 14, 2017 · 5 votes
- google/error-prone“ErrorProne 2.37: StringConcatToTextBlock causes java.util.NoSuchElementException”GitHub · Mar 19, 2025 · 3 reactions
The biggest 5 of 27 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 27
Talked about on Stack Overflow, GitHub, Hacker News and the web (27 times).
On Stack Overflow · 14
- Apr 28, 2017What is the relation between CAS vs SAML?23 votes
- Apr 10, 2017Opensaml error receiving correct unmarshaller6 votes
- Apr 14, 2017How to enable file-based authentication in CAS 55 votes
- Aug 2, 2021How to create a SAML IDP that authenticates with an API from scratch2 votes
- Feb 3, 2020CAS 6.1 - State Parameter null with Pac4J2 votes
9 more on Stack Overflow
- Jul 4, 2017CAS Spnego - KrbException: Checksum failed2 votes · 3 answers
- Dec 11, 2023What happened "org.apereo.cas.web.security" in 7.0.0-RC9 of org.apereo.cas?1 vote
- Jul 23, 2020Cas - configure Principal transformation1 vote · 1 answer
- Nov 25, 2019CAS Disable Service X-Frame-Options not work1 vote
- Apr 24, 2019inject model data into spring webflow in cas1 vote
- Mar 4, 2019Prevent CAS authentication if X.509 user isn't registered1 vote
- Nov 10, 2021CAS Apereo 6.1.x importing PasswordEncoderUtils0 votes · 1 answer
- Aug 2, 2021How to SSO to a directory server from a python application0 votes
- Nov 7, 2016using CAS with with custom login page0 votes
On GitHub · 9
- Sep 26, 2020Add support for login via cas - github.com/apereo/cas — strapi/strapi8 reactions · 21 comments
- Mar 19, 2025ErrorProne 2.37: StringConcatToTextBlock causes java.util.NoSuchElementException — google/error-prone3 reactions · 3 comments
- Mar 26, 2020Renovate crash: JavaScript heap out of memory (many Gradle dependencies) — renovatebot/renovate1 reaction · 8 comments
- Aug 10, 2026Add Apereo CAS project to regression testing project lists — checkstyle/test-configssays it tested it0 reactions · 1 comment
- Jul 17, 2026[Argus] CVE-2026-59099: Apereo CAS 7.3.0 < 8.0.0-RC6 - AES-GCM 논스(Nonce) 재사용으로 인한 정보 노출 — LEEKIYOON-SEC/Argus-DEV0 reactions
4 more on GitHub
- Dec 5, 2024Extract Attr within moved file — pouryafard75/RM-ASTDiff0 reactions · 14 comments
- Jan 18, 2023Authentication/Authorization: We need a API-friendly strategy — wolpert/svarm0 reactions · 1 comment
- Apr 19, 2020COMPARAISON KEYCLOAK / CAS — pegasus-io/belenios-containers0 reactions
- Mar 20, 2018apereo/cas OAuth endpoints need @Controller annotations — easy-software-ufal/annotations_repos0 reactions
On Hacker News · 3
- Feb 20, 2017Enterprise Single Sign on for all3 points
- Aug 8, 2023The Central Authentication Service Project2 points
- Oct 16, 2016Apereo CAS: Single Sign-On for the Web1 point
On the web · 1
- Apr 11, 2025apereo/cas - Where software is built — google.com
In this repository30 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- cas-actuatorsSkill
- cas-attribute-consentSkill
- cas-build-ciSkill
- cas-docs-siteSkill
- cas-google-authenticatorSkill
- cas-groovy-scriptingSkill
- cas-handoffSkill
- cas-heimdall-authzenSkill
- cas-import-prSkill
- cas-interrupt-notificationsSkill
- cas-ldapSkill
- cas-mcp-serverSkill
- cas-oauth-oidc-authenticationSkill
- cas-oauth-oidcSkill
- cas-oid4vcSkill
- cas-passwordless-webauthnSkill
- cas-protocolSkill
- cas-samlSkill
- cas-sandbox-verificationSkill
- cas-service-registriesSkill
- cas-session-storesSkill
- cas-startup-performanceSkill
- cas-storage-backendsSkill
- cas-testingSkill
- cas-ticket-registriesSkill
- cas-uiSkill
- cas-web-securitySkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

