diff --git a/composer.lock b/composer.lock index c2f5c1dd..50f046a4 100644 --- a/composer.lock +++ b/composer.lock @@ -65,26 +65,26 @@ }, { "name": "firebase/php-jwt", - "version": "v7.1.0", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/googleapis/php-jwt.git", - "reference": "b374a5d1a4f1f67fadc2165cdb284645945e2fc0" + "reference": "f502cdbf279cd7532060b041f7c22a05208c0b93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/b374a5d1a4f1f67fadc2165cdb284645945e2fc0", - "reference": "b374a5d1a4f1f67fadc2165cdb284645945e2fc0", + "url": "https://api.github.com/repos/googleapis/php-jwt/zipball/f502cdbf279cd7532060b041f7c22a05208c0b93", + "reference": "f502cdbf279cd7532060b041f7c22a05208c0b93", "shasum": "" }, "require": { "php": "^8.0" }, "require-dev": { - "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/guzzle": "^7.4||^8.0", "phpfastcache/phpfastcache": "^9.2", "phpseclib/phpseclib": "~3.0", - "phpspec/prophecy-phpunit": "^2.0", + "phpspec/prophecy-phpunit": "^2.2", "phpunit/phpunit": "^9.5", "psr/cache": "^2.0||^3.0", "psr/http-client": "^1.0", @@ -96,6 +96,14 @@ "phpseclib/phpseclib": "Support PS256 (RSASSA-PSS) signatures" }, "type": "library", + "extra": { + "component": { + "id": "jwt", + "path": "Jwt", + "entry": "README.md", + "target": "googleapis/php-jwt.git" + } + }, "autoload": { "psr-4": { "Firebase\\JWT\\": "src" @@ -125,9 +133,9 @@ ], "support": { "issues": "https://github.com/googleapis/php-jwt/issues", - "source": "https://github.com/googleapis/php-jwt/tree/v7.1.0" + "source": "https://github.com/googleapis/php-jwt/tree/v7.2.0" }, - "time": "2026-06-11T17:54:14+00:00" + "time": "2026-09-21T19:29:58+00:00" }, { "name": "id4me/id4me-rp", diff --git a/l10n/da.js b/l10n/da.js index d681f14a..5c28e166 100644 --- a/l10n/da.js +++ b/l10n/da.js @@ -1,7 +1,7 @@ OC.L10N.register( "user_oidc", { - "Login with %1s" : "Log på med %1s", + "Login with %1s" : "Log ind med %1s", "Error" : "Fejl", "Access forbidden" : "Adgang forbudt", "ID4Me is disabled" : "ID4Me er deaktiveret", @@ -11,6 +11,7 @@ OC.L10N.register( "The received state does not match the expected value." : "Den modtagne tilstand matcher ikke den forventede værdi.", "Authority not found" : "Autoritet ikke fundet", "Failed to decrypt the ID4ME provider client secret" : "Kunne ikke dekryptere ID4ME leverandør klient hemmeligheden", + "Failed to authenticate" : "Godkendelsen mislykkedes", "The received token is expired." : "Det modtagne token er udløbet.", "The audience does not match ours" : "Publikum matcher ikke vores", "The authorized party does not match ours" : "Den godkendte part matcher ikke vores", @@ -19,16 +20,19 @@ OC.L10N.register( "You must access Nextcloud with HTTPS to use OpenID Connect." : "Du skal tilgå Nextcloud via HTTPS for at anvende OpenID Connect.", "There is no such OpenID Connect provider." : "Der er ingen sådan OpenID Connect udbyder.", "Could not reach the OpenID Connect provider." : "Kunne ikke kontakte OpenID Connect leverandøren", + "The identity provider failed to authenticate the user." : "Identitetsudbyderen kunne ikke godkende brugeren.", + "The received state has expired." : "Den modtagne tilstand er udløbet.", "Failed to decrypt the OIDC provider client secret" : "Kunne ikke dekryptere OIDC leverandør klient hemmeligheden", "Failed to contact the OIDC provider token endpoint" : "Kunne ikke kontakte OIDC leverandør token slutpunktet", "The issuer does not match the one from the discovery endpoint" : "Udstederen matcher ikke den fra opdagelses slutpunktet", "Failed to provision the user" : "Kunne ikke provisionere brugeren", - "You do not have permission to log in to this instance. If you think this is an error, please contact an administrator." : "Du har ikke tilladelse til at logge på denne instans. Hvis du mener at det er en fejl, så kontakt venligst en administrator.", + "You do not have permission to log in to this instance. If you think this is an error, please contact an administrator." : "Du har ikke tilladelse til at logge ind på denne instans. Hvis du mener, at det er en fejl, så kontakt en administrator.", "User conflict" : "Brugerkonfliktconflict", "OpenID Connect" : "OpenID Connect", "OpenID Connect user backend" : "OpenID Connect bruger backend", - "Use an OpenID Connect backend to login to your Nextcloud" : "Anvend en OpenID Connect backend for at logge på din Nextcloud", - "Allows flexible configuration of an OIDC server as Nextcloud login user backend." : "Giver mulighed for fleksibel konfiguration af en OIDC server som Nextcloud log på bruger backend.", + "Use an OpenID Connect backend to login to your Nextcloud" : "Brug en OpenID Connect-backend til at logge ind på din Nextcloud", + "Allows flexible configuration of an OIDC server as Nextcloud login user backend." : "Giver mulighed for fleksibel konfiguration af en OIDC-server som login-backend for brugere i Nextcloud.", + "Are you sure you want to delete the provider \"{providerName}\"?" : "Er du sikker på, at du vil slette udbyderen \"{providerName}\"?", "Could not save ID4me state: {msg}" : "Kunne ikke gemme ID4me tilstand: {msg}", "Could not save storeLoginToken state: {msg}" : "Kunne ikke gemme storeLoginToken tilstand: {msg}", "Could not update the provider:" : "Kunne ikke opdatere udbyderen:", @@ -37,6 +41,7 @@ OC.L10N.register( "Allows users to authenticate via OpenID Connect providers." : "Giver brugerne mulighed for at autentificere via OpenID Connect udbydere.", "Enable ID4me" : "Aktivér ID4me", "Store login tokens" : "Gem login nøgler", + "\"Store login tokens\" is needed if you are using other apps that want to use user_oidc's token exchange or simply get the login token" : "\"Gem login-tokens\" er nødvendig, hvis du bruger andre apps, der vil bruge user_oidc's tokenudveksling eller blot hente login-tokenet", "Registered Providers" : "Registrerede Udbydere", "Register new provider" : "Registrér ny udbyder", "Register a new provider" : "Registrér en ny udbyder", @@ -44,7 +49,7 @@ OC.L10N.register( "No providers registered." : "Ingen udbydere registeret.", "Client ID" : "Klient ID", "Discovery endpoint" : "Discovery endpoint", - "Backchannel Logout URL" : "Backchannel log af URL", + "Backchannel Logout URL" : "Backchannel Logout-URL", "Redirect URI (to be authorized in the provider client configuration)" : "Omdirigér URI (for godkendelse i udbyderens klient konfiguration)", "Update" : "Opdatér", "Remove" : "Fjern", @@ -61,6 +66,7 @@ OC.L10N.register( "Leave empty to keep existing" : "Lad være tom for at beholde eksisterende", "Warning, if the protocol of the URLs in the discovery content is HTTP, the ID token will be delivered through an insecure connection." : "Advarsel, hvis protokollen for URL'er i opdagelsesindholdet er HTTP, så vil ID token blive leveret gennem en usikker forbindelse.", "Custom end session endpoint" : "Brugerdefineret slut session endepunkt", + "Post logout URI" : "URI efter logud", "Scope" : "Anvendelsesområde", "Extra claims" : "Ekstra claims", "Attribute mapping" : "Attribut tilknytning", @@ -70,10 +76,13 @@ OC.L10N.register( "Groups mapping" : "Gruppe tilknytning", "Extra attributes mapping" : "Ekstra attributter tilknytning", "Display name mapping" : "Vis navne tilknytning", + "Birth date mapping" : "Tilknytning af fødselsdato", + "Pronouns mapping" : "Tilknytning af pronominer", "Gender mapping" : "Køn tilknytning", "Email mapping" : "Mail tilknytning", "Phone mapping" : "Telefon tilknytning", "Language mapping" : "Sprog tilknytning", + "Locale mapping" : "Tilknytning af lokalitet", "Role/Title mapping" : "Rolle/titel tilknytning", "Street mapping" : "Gade tilknytning", "Postal code mapping" : "Postnummer tilknytning", @@ -90,17 +99,21 @@ OC.L10N.register( "Authentication and Access Control Settings" : "Indstillinger for godkendelse og adgangskontrol", "Use unique user ID" : "Brug unikt Bruger ID", "By default every user will get a unique user ID that is a hashed value of the provider and user ID. This can be turned off but uniqueness of users accross multiple user backends and providers is no longer preserved then." : "Som standard vil hver bruger få et unikt bruger ID, der er en hashed værdi af udbyderen og bruger ID. Dette kan deaktiveres, men unikheden af brugerne på tværs af flere bruger backends og udbydere vil så ikke længere være bevaret.", + "Keep in mind that disabling \"Unique user IDs\" means that this provider can produce user IDs that are identical to user IDs from other providers (and from other user backends if you are in non-auto-provisioning mode). This means this provider can be potentially used to authenticate as users from other providers/backends. This might be what you want to achieve. In any case, please make sure you trust this provider." : "Vær opmærksom på, at hvis du deaktiverer \"Unikke bruger-id'er\", kan denne udbyder oprette bruger-id'er, der er identiske med bruger-id'er fra andre udbydere (og fra andre brugerbackends, hvis du ikke bruger automatisk oprettelse). Det betyder, at denne udbyder potentielt kan bruges til at godkende sig som brugere fra andre udbydere eller backends. Det kan være det, du vil opnå. Under alle omstændigheder skal du sikre dig, at du har tillid til denne udbyder.", "Use provider identifier as prefix for IDs" : "Brug udbyderens id som præfiks for ID'er", "To keep IDs in plain text, but also preserve uniqueness of them across multiple providers, a prefix with the providers name is added." : "For at holde ID'er i almindelig tekst, men også bevare unikke af dem på tværs af flere udbydere, et præfiks med udbyderens navn tilføjes.", "Use group provisioning." : "Brug gruppeprovisionering", "Group whitelist regex" : "Gruppe hvidlistet regex", "Restrict login for users that are not in any whitelisted group" : "Begræns login for brugere, der ikke er i nogen hvidlistet gruppe", - "Users that are not part of any whitelisted group are not created and can not login" : "Brugere, der ikke er en del af nogen hvidlistet gruppe bliver ikke oprettet og kan ikke logge på", + "Users that are not part of any whitelisted group are not created and can not login" : "Brugere, der ikke er med i en gruppe på tilladelseslisten, bliver ikke oprettet og kan ikke logge ind", "Check Bearer token on API and WebDAV requests" : "Tjek Bearer token på API og WebDAV anmodninger", + "Do you want to allow API calls and WebDAV requests that are authenticated with an OIDC ID token or access token?" : "Vil du tillade API-kald og WebDAV-anmodninger, der er godkendt med et OIDC-id-token eller et adgangstoken?", "Auto provision user when accessing API and WebDAV with Bearer token" : "Automatisk provisionering af bruger når API og WebDAV tilgås med Bearer token", "This automatically provisions the user, when sending API and WebDAV requests with a Bearer token. Auto provisioning and Bearer token check have to be activated for this to work." : "Dette provisionerer automatisk brugeren, når der sendes API og WebDAV anmodninger med en Bearer token. Auto provisionering og Bearer token kontrol skal aktiveres for at dette virker.", - "Send ID token hint on logout" : "Send ID token hint om log af", + "Send ID token hint on logout" : "Send ID-token-hint ved logout", "Should the ID token be included as the id_token_hint GET parameter in the OpenID logout URL? Users are redirected to this URL after logging out of Nextcloud. Enabling this setting exposes the OIDC ID token to the user agent, which may not be necessary depending on the OIDC provider." : "Skal ID token inkluderes som id_token_hint GET-parameteren i OpenID logud URL? Brugere omdirigeres til denne URL når de logger ud. Aktivering af denne indstilling eksponerer OIDC ID token til bruger-agenten, som måske ikke er nødvendig afhængigt af OIDC udbyder.", + "Enrich login ID token with userinfo" : "Berig login-id-tokenet med userinfo", + "Fetch additional information not found in the login ID token from the userinfo endpoint. This setting is overwritten if the global enrich_login_id_token_with_userinfo option is enabled." : "Hent yderligere oplysninger, som ikke findes i login-id-tokenet, fra userinfo-endpointet. Indstillingen overskrives, hvis den globale indstilling enrich_login_id_token_with_userinfo er slået til.", "Only groups matching the whitelist regex will be created, updated and deleted by the group claim. For example: {regex} allows all groups which ID starts with {substr}" : "Kun grupper der matcher hvidlistningens regex vil blive oprettet, opdateret og slettet af gruppe krav. For eksempel: {regex} tillader alle grupper hvis ID starter med {substr}", "This will create and update the users groups depending on the groups claim in the ID token. The Format of the groups claim value should be {sample1}, {sample2} or {sample3}" : "Dette vil oprette og opdatere brugernes grupper afhængigt af gruppernes krav i ID token. Formatet for gruppernes værdi skal være {sample1}, {sample2} eller {sample3}", "Back to %s" : "Tilbage til %s", diff --git a/l10n/da.json b/l10n/da.json index cefec050..4255df88 100644 --- a/l10n/da.json +++ b/l10n/da.json @@ -1,5 +1,5 @@ { "translations": { - "Login with %1s" : "Log på med %1s", + "Login with %1s" : "Log ind med %1s", "Error" : "Fejl", "Access forbidden" : "Adgang forbudt", "ID4Me is disabled" : "ID4Me er deaktiveret", @@ -9,6 +9,7 @@ "The received state does not match the expected value." : "Den modtagne tilstand matcher ikke den forventede værdi.", "Authority not found" : "Autoritet ikke fundet", "Failed to decrypt the ID4ME provider client secret" : "Kunne ikke dekryptere ID4ME leverandør klient hemmeligheden", + "Failed to authenticate" : "Godkendelsen mislykkedes", "The received token is expired." : "Det modtagne token er udløbet.", "The audience does not match ours" : "Publikum matcher ikke vores", "The authorized party does not match ours" : "Den godkendte part matcher ikke vores", @@ -17,16 +18,19 @@ "You must access Nextcloud with HTTPS to use OpenID Connect." : "Du skal tilgå Nextcloud via HTTPS for at anvende OpenID Connect.", "There is no such OpenID Connect provider." : "Der er ingen sådan OpenID Connect udbyder.", "Could not reach the OpenID Connect provider." : "Kunne ikke kontakte OpenID Connect leverandøren", + "The identity provider failed to authenticate the user." : "Identitetsudbyderen kunne ikke godkende brugeren.", + "The received state has expired." : "Den modtagne tilstand er udløbet.", "Failed to decrypt the OIDC provider client secret" : "Kunne ikke dekryptere OIDC leverandør klient hemmeligheden", "Failed to contact the OIDC provider token endpoint" : "Kunne ikke kontakte OIDC leverandør token slutpunktet", "The issuer does not match the one from the discovery endpoint" : "Udstederen matcher ikke den fra opdagelses slutpunktet", "Failed to provision the user" : "Kunne ikke provisionere brugeren", - "You do not have permission to log in to this instance. If you think this is an error, please contact an administrator." : "Du har ikke tilladelse til at logge på denne instans. Hvis du mener at det er en fejl, så kontakt venligst en administrator.", + "You do not have permission to log in to this instance. If you think this is an error, please contact an administrator." : "Du har ikke tilladelse til at logge ind på denne instans. Hvis du mener, at det er en fejl, så kontakt en administrator.", "User conflict" : "Brugerkonfliktconflict", "OpenID Connect" : "OpenID Connect", "OpenID Connect user backend" : "OpenID Connect bruger backend", - "Use an OpenID Connect backend to login to your Nextcloud" : "Anvend en OpenID Connect backend for at logge på din Nextcloud", - "Allows flexible configuration of an OIDC server as Nextcloud login user backend." : "Giver mulighed for fleksibel konfiguration af en OIDC server som Nextcloud log på bruger backend.", + "Use an OpenID Connect backend to login to your Nextcloud" : "Brug en OpenID Connect-backend til at logge ind på din Nextcloud", + "Allows flexible configuration of an OIDC server as Nextcloud login user backend." : "Giver mulighed for fleksibel konfiguration af en OIDC-server som login-backend for brugere i Nextcloud.", + "Are you sure you want to delete the provider \"{providerName}\"?" : "Er du sikker på, at du vil slette udbyderen \"{providerName}\"?", "Could not save ID4me state: {msg}" : "Kunne ikke gemme ID4me tilstand: {msg}", "Could not save storeLoginToken state: {msg}" : "Kunne ikke gemme storeLoginToken tilstand: {msg}", "Could not update the provider:" : "Kunne ikke opdatere udbyderen:", @@ -35,6 +39,7 @@ "Allows users to authenticate via OpenID Connect providers." : "Giver brugerne mulighed for at autentificere via OpenID Connect udbydere.", "Enable ID4me" : "Aktivér ID4me", "Store login tokens" : "Gem login nøgler", + "\"Store login tokens\" is needed if you are using other apps that want to use user_oidc's token exchange or simply get the login token" : "\"Gem login-tokens\" er nødvendig, hvis du bruger andre apps, der vil bruge user_oidc's tokenudveksling eller blot hente login-tokenet", "Registered Providers" : "Registrerede Udbydere", "Register new provider" : "Registrér ny udbyder", "Register a new provider" : "Registrér en ny udbyder", @@ -42,7 +47,7 @@ "No providers registered." : "Ingen udbydere registeret.", "Client ID" : "Klient ID", "Discovery endpoint" : "Discovery endpoint", - "Backchannel Logout URL" : "Backchannel log af URL", + "Backchannel Logout URL" : "Backchannel Logout-URL", "Redirect URI (to be authorized in the provider client configuration)" : "Omdirigér URI (for godkendelse i udbyderens klient konfiguration)", "Update" : "Opdatér", "Remove" : "Fjern", @@ -59,6 +64,7 @@ "Leave empty to keep existing" : "Lad være tom for at beholde eksisterende", "Warning, if the protocol of the URLs in the discovery content is HTTP, the ID token will be delivered through an insecure connection." : "Advarsel, hvis protokollen for URL'er i opdagelsesindholdet er HTTP, så vil ID token blive leveret gennem en usikker forbindelse.", "Custom end session endpoint" : "Brugerdefineret slut session endepunkt", + "Post logout URI" : "URI efter logud", "Scope" : "Anvendelsesområde", "Extra claims" : "Ekstra claims", "Attribute mapping" : "Attribut tilknytning", @@ -68,10 +74,13 @@ "Groups mapping" : "Gruppe tilknytning", "Extra attributes mapping" : "Ekstra attributter tilknytning", "Display name mapping" : "Vis navne tilknytning", + "Birth date mapping" : "Tilknytning af fødselsdato", + "Pronouns mapping" : "Tilknytning af pronominer", "Gender mapping" : "Køn tilknytning", "Email mapping" : "Mail tilknytning", "Phone mapping" : "Telefon tilknytning", "Language mapping" : "Sprog tilknytning", + "Locale mapping" : "Tilknytning af lokalitet", "Role/Title mapping" : "Rolle/titel tilknytning", "Street mapping" : "Gade tilknytning", "Postal code mapping" : "Postnummer tilknytning", @@ -88,17 +97,21 @@ "Authentication and Access Control Settings" : "Indstillinger for godkendelse og adgangskontrol", "Use unique user ID" : "Brug unikt Bruger ID", "By default every user will get a unique user ID that is a hashed value of the provider and user ID. This can be turned off but uniqueness of users accross multiple user backends and providers is no longer preserved then." : "Som standard vil hver bruger få et unikt bruger ID, der er en hashed værdi af udbyderen og bruger ID. Dette kan deaktiveres, men unikheden af brugerne på tværs af flere bruger backends og udbydere vil så ikke længere være bevaret.", + "Keep in mind that disabling \"Unique user IDs\" means that this provider can produce user IDs that are identical to user IDs from other providers (and from other user backends if you are in non-auto-provisioning mode). This means this provider can be potentially used to authenticate as users from other providers/backends. This might be what you want to achieve. In any case, please make sure you trust this provider." : "Vær opmærksom på, at hvis du deaktiverer \"Unikke bruger-id'er\", kan denne udbyder oprette bruger-id'er, der er identiske med bruger-id'er fra andre udbydere (og fra andre brugerbackends, hvis du ikke bruger automatisk oprettelse). Det betyder, at denne udbyder potentielt kan bruges til at godkende sig som brugere fra andre udbydere eller backends. Det kan være det, du vil opnå. Under alle omstændigheder skal du sikre dig, at du har tillid til denne udbyder.", "Use provider identifier as prefix for IDs" : "Brug udbyderens id som præfiks for ID'er", "To keep IDs in plain text, but also preserve uniqueness of them across multiple providers, a prefix with the providers name is added." : "For at holde ID'er i almindelig tekst, men også bevare unikke af dem på tværs af flere udbydere, et præfiks med udbyderens navn tilføjes.", "Use group provisioning." : "Brug gruppeprovisionering", "Group whitelist regex" : "Gruppe hvidlistet regex", "Restrict login for users that are not in any whitelisted group" : "Begræns login for brugere, der ikke er i nogen hvidlistet gruppe", - "Users that are not part of any whitelisted group are not created and can not login" : "Brugere, der ikke er en del af nogen hvidlistet gruppe bliver ikke oprettet og kan ikke logge på", + "Users that are not part of any whitelisted group are not created and can not login" : "Brugere, der ikke er med i en gruppe på tilladelseslisten, bliver ikke oprettet og kan ikke logge ind", "Check Bearer token on API and WebDAV requests" : "Tjek Bearer token på API og WebDAV anmodninger", + "Do you want to allow API calls and WebDAV requests that are authenticated with an OIDC ID token or access token?" : "Vil du tillade API-kald og WebDAV-anmodninger, der er godkendt med et OIDC-id-token eller et adgangstoken?", "Auto provision user when accessing API and WebDAV with Bearer token" : "Automatisk provisionering af bruger når API og WebDAV tilgås med Bearer token", "This automatically provisions the user, when sending API and WebDAV requests with a Bearer token. Auto provisioning and Bearer token check have to be activated for this to work." : "Dette provisionerer automatisk brugeren, når der sendes API og WebDAV anmodninger med en Bearer token. Auto provisionering og Bearer token kontrol skal aktiveres for at dette virker.", - "Send ID token hint on logout" : "Send ID token hint om log af", + "Send ID token hint on logout" : "Send ID-token-hint ved logout", "Should the ID token be included as the id_token_hint GET parameter in the OpenID logout URL? Users are redirected to this URL after logging out of Nextcloud. Enabling this setting exposes the OIDC ID token to the user agent, which may not be necessary depending on the OIDC provider." : "Skal ID token inkluderes som id_token_hint GET-parameteren i OpenID logud URL? Brugere omdirigeres til denne URL når de logger ud. Aktivering af denne indstilling eksponerer OIDC ID token til bruger-agenten, som måske ikke er nødvendig afhængigt af OIDC udbyder.", + "Enrich login ID token with userinfo" : "Berig login-id-tokenet med userinfo", + "Fetch additional information not found in the login ID token from the userinfo endpoint. This setting is overwritten if the global enrich_login_id_token_with_userinfo option is enabled." : "Hent yderligere oplysninger, som ikke findes i login-id-tokenet, fra userinfo-endpointet. Indstillingen overskrives, hvis den globale indstilling enrich_login_id_token_with_userinfo er slået til.", "Only groups matching the whitelist regex will be created, updated and deleted by the group claim. For example: {regex} allows all groups which ID starts with {substr}" : "Kun grupper der matcher hvidlistningens regex vil blive oprettet, opdateret og slettet af gruppe krav. For eksempel: {regex} tillader alle grupper hvis ID starter med {substr}", "This will create and update the users groups depending on the groups claim in the ID token. The Format of the groups claim value should be {sample1}, {sample2} or {sample3}" : "Dette vil oprette og opdatere brugernes grupper afhængigt af gruppernes krav i ID token. Formatet for gruppernes værdi skal være {sample1}, {sample2} eller {sample3}", "Back to %s" : "Tilbage til %s", diff --git a/l10n/sk.js b/l10n/sk.js index be3db699..f237043f 100644 --- a/l10n/sk.js +++ b/l10n/sk.js @@ -38,14 +38,14 @@ OC.L10N.register( "Could not update the provider:" : "Poskytovateľa sa nepodarilo aktualizovať:", "Could not remove provider: {msg}" : "Nepodarilo sa odstrániť poskytovateľa: {msg}", "Could not register provider:" : "Nepodarilo sa zaregistrovať poskytovateľa:", - "Allows users to authenticate via OpenID Connect providers." : "Povoliť užívateľom autentifikáciu cez OpenID Connect poskytovateľov.", + "Allows users to authenticate via OpenID Connect providers." : "Povoliť používateľom autentifikáciu cez OpenID Connect poskytovateľov.", "Enable ID4me" : "Povoliť ID4me", "Store login tokens" : "Uložiť prihlasovacie tokeny", "\"Store login tokens\" is needed if you are using other apps that want to use user_oidc's token exchange or simply get the login token" : "Nastavenie \"Ukladať prihlasovacie tokeny\" je potrebné, ak používate iné aplikácie, ktoré chcú používať výmenu tokenov aplikácie user_oidc alebo jednoducho získať prihlasovací token", "Registered Providers" : "Preferovaní poskytovatelia", "Register new provider" : "Registrovať nového poskytovateľa", "Register a new provider" : "Registrovať nového poskytovateľa", - "Configure your provider to redirect back to {url}" : "Nastavte vášho poskytovateľa pre presmerovanie na {url}", + "Configure your provider to redirect back to {url}" : "Nastavte svojho poskytovateľa tak, aby presmeroval späť na {url}.", "No providers registered." : "Neboli zaregistrovaný žiadny poskytovatelia.", "Client ID" : "Client ID", "Discovery endpoint" : "Koncový bod discovery", diff --git a/l10n/sk.json b/l10n/sk.json index d087a14c..951cfbed 100644 --- a/l10n/sk.json +++ b/l10n/sk.json @@ -36,14 +36,14 @@ "Could not update the provider:" : "Poskytovateľa sa nepodarilo aktualizovať:", "Could not remove provider: {msg}" : "Nepodarilo sa odstrániť poskytovateľa: {msg}", "Could not register provider:" : "Nepodarilo sa zaregistrovať poskytovateľa:", - "Allows users to authenticate via OpenID Connect providers." : "Povoliť užívateľom autentifikáciu cez OpenID Connect poskytovateľov.", + "Allows users to authenticate via OpenID Connect providers." : "Povoliť používateľom autentifikáciu cez OpenID Connect poskytovateľov.", "Enable ID4me" : "Povoliť ID4me", "Store login tokens" : "Uložiť prihlasovacie tokeny", "\"Store login tokens\" is needed if you are using other apps that want to use user_oidc's token exchange or simply get the login token" : "Nastavenie \"Ukladať prihlasovacie tokeny\" je potrebné, ak používate iné aplikácie, ktoré chcú používať výmenu tokenov aplikácie user_oidc alebo jednoducho získať prihlasovací token", "Registered Providers" : "Preferovaní poskytovatelia", "Register new provider" : "Registrovať nového poskytovateľa", "Register a new provider" : "Registrovať nového poskytovateľa", - "Configure your provider to redirect back to {url}" : "Nastavte vášho poskytovateľa pre presmerovanie na {url}", + "Configure your provider to redirect back to {url}" : "Nastavte svojho poskytovateľa tak, aby presmeroval späť na {url}.", "No providers registered." : "Neboli zaregistrovaný žiadny poskytovatelia.", "Client ID" : "Client ID", "Discovery endpoint" : "Koncový bod discovery", diff --git a/lib/Controller/Id4meController.php b/lib/Controller/Id4meController.php index 8a76c8e5..7e448ff0 100644 --- a/lib/Controller/Id4meController.php +++ b/lib/Controller/Id4meController.php @@ -33,7 +33,6 @@ use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; @@ -60,12 +59,11 @@ public function __construct( private IConfig $config, private IL10N $l10n, private ITimeFactory $timeFactory, - private IClientService $clientService, private IURLGenerator $urlGenerator, private UserMapper $userMapper, private IUserSession $userSession, private IUserManager $userManager, - HttpClientHelper $clientHelper, + private HttpClientHelper $clientHelper, private Id4MeMapper $id4MeMapper, private ID4MeService $id4MeService, private LoggerInterface $logger, @@ -222,24 +220,21 @@ public function code(string $state = '', string $code = '', string $scope = '') return $this->buildErrorTemplateResponse($message, Http::STATUS_BAD_REQUEST, [], false); } - $client = $this->clientService->newClient(); - $result = $client->post( + $body = $this->clientHelper->post( $openIdConfig->getTokenEndpoint(), [ - 'headers' => [ - 'Authorization' => 'Basic ' . base64_encode($id4Me->getClientId() . ':' . $id4meClientSecret) - ], - 'body' => [ - 'code' => $code, - 'client_id' => $id4Me->getClientId(), - 'client_secret' => $id4meClientSecret, - 'redirect_uri' => $this->urlGenerator->linkToRouteAbsolute(Application::APP_ID . '.id4me.code'), - 'grant_type' => 'authorization_code', - ], + 'code' => $code, + 'client_id' => $id4Me->getClientId(), + 'client_secret' => $id4meClientSecret, + 'redirect_uri' => $this->urlGenerator->linkToRouteAbsolute(Application::APP_ID . '.id4me.code'), + 'grant_type' => 'authorization_code', + ], + [ + 'Authorization' => 'Basic ' . base64_encode($id4Me->getClientId() . ':' . $id4meClientSecret), ] ); - $data = json_decode($result->getBody(), true); + $data = json_decode($body, true); // documentation about token validation: // https://gitlab.com/ID4me/documentation/blob/master/id4ME%20Relying%20Party%20Implementation%20Guide.pdf diff --git a/lib/Controller/LoginController.php b/lib/Controller/LoginController.php index 6edf9ea0..99970b93 100644 --- a/lib/Controller/LoginController.php +++ b/lib/Controller/LoginController.php @@ -697,7 +697,7 @@ public function code(string $state = '', string $code = '', string $scope = '', $this->eventDispatcher->dispatchTyped(new BeforeUserLoggedInEvent($userId, null, \OCP\Server::get(Backend::class))); $this->userSession->completeLogin($user, ['loginName' => $userId, 'password' => '']); - $this->userSession->createSessionToken($this->request, $userId, $userId); + $this->userSession->createSessionToken($this->request, $userId, $userId, remember: IToken::REMEMBER); $this->userSession->createRememberMeToken($user); // prevent password confirmation diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index ce50f3b6..80084391 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -12,6 +12,7 @@ use OCA\UserOIDC\AppInfo\Application; use OCA\UserOIDC\Db\Provider; use OCA\UserOIDC\Db\ProviderMapper; +use OCA\UserOIDC\Helper\HttpClientHelper; use OCA\UserOIDC\ResponseDefinitions; use OCA\UserOIDC\Service\ID4MeService; use OCA\UserOIDC\Service\ProviderService; @@ -22,7 +23,6 @@ use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; -use OCP\Http\Client\IClientService; use OCP\IAppConfig; use OCP\IRequest; use OCP\Security\ICrypto; @@ -40,7 +40,7 @@ public function __construct( private ID4MeService $id4meService, private ProviderService $providerService, private ICrypto $crypto, - private IClientService $clientService, + private HttpClientHelper $httpClientHelper, private LoggerInterface $logger, ) { parent::__construct(Application::APP_ID, $request); @@ -53,13 +53,10 @@ private function isDiscoveryEndpointValid($url) { ]; try { - $client = $this->clientService->newClient(); - $response = $client->get($url); - $httpCode = $response->getStatusCode(); - $body = $response->getBody(); + $body = $this->httpClientHelper->get($url); // Check if the request was successful - if ($httpCode === Http::STATUS_OK && !empty($body)) { + if (!empty($body)) { $result['isReachable'] = true; $data = json_decode($body, true); diff --git a/lib/Service/OIDCService.php b/lib/Service/OIDCService.php index 3b5bddad..3d94c353 100644 --- a/lib/Service/OIDCService.php +++ b/lib/Service/OIDCService.php @@ -11,6 +11,7 @@ use OCA\UserOIDC\Db\Provider; use OCA\UserOIDC\Helper\HttpClientHelper; +use OCA\UserOIDC\Vendor\Firebase\JWT\JWT; use OCP\Security\ICrypto; use Psr\Log\LoggerInterface; use Throwable; @@ -37,12 +38,31 @@ public function userinfo(Provider $provider, string $accessToken): array { 'Authorization' => 'Bearer ' . $accessToken, ], ]; + try { - return json_decode($this->clientService->get($url, [], $options), true); + $userInfoResponse = $this->clientService->get($url, [], $options); } catch (Throwable $e) { $this->logger->warning('[UserInfo] Failed to fetch user info endpoint', ['exception' => $e]); return []; } + + try { + return json_decode($userInfoResponse, true, 512, JSON_THROW_ON_ERROR); + } catch (Throwable) { + $this->logger->debug('[UserInfo] The response is not JSON'); + } + + JWT::$leeway = 60; + try { + $jwks = $this->discoveryService->obtainJWK($provider, $userInfoResponse); + $payload = JWT::decode($userInfoResponse, $jwks); + $arrayPayload = json_decode(json_encode($payload), true); + $this->logger->debug('[UserInfo] Decoded the JWT response', ['decoded_userinfo_response' => $arrayPayload]); + return $arrayPayload; + } catch (Throwable $e) { + $this->logger->warning('[UserInfo] Failed to decode the response as a JWT', ['exception' => $e]); + return []; + } } public function introspection(Provider $provider, string $accessToken): array { diff --git a/lib/Service/ProvisioningService.php b/lib/Service/ProvisioningService.php index 20ab7f4f..ebe970ff 100644 --- a/lib/Service/ProvisioningService.php +++ b/lib/Service/ProvisioningService.php @@ -620,7 +620,21 @@ public function getSyncGroupsOfToken(int $providerId, object $idTokenPayload): ? $group = $v; } elseif (is_string($v)) { // Handle array of strings, e.g. ["group1", "group2", ...] - $group = (object)['gid' => $v, 'displayName' => $v]; + // Some upstream servers return each group object as a JSON-encoded string, + // e.g. "{\"gid\":\"group1\",\"displayName\":\"Group 1\"}". Decode these so the + // gid and displayName can be read correctly instead of being passed through verbatim. + $decoded = json_decode($v, true); + if (is_array($decoded) && isset($decoded['gid'])) { + if (empty($decoded['gid']) && $decoded['gid'] !== '0' && $decoded['gid'] !== 0) { + continue; + } + $group = (object)[ + 'gid' => $decoded['gid'], + 'displayName' => $decoded['displayName'] ?? $decoded['gid'], + ]; + } else { + $group = (object)['gid' => $v, 'displayName' => $v]; + } } else { continue; } diff --git a/lib/User/Backend.php b/lib/User/Backend.php index 967ff0cf..ebdc3e9f 100644 --- a/lib/User/Backend.php +++ b/lib/User/Backend.php @@ -254,8 +254,11 @@ public function getCurrentUserId(): string { } // get the bearer token from headers + // the auth-scheme is case-insensitive and is separated from the credentials + // by one or more spaces or horizontal tabs (RFC 9110, section 11.1), so this + // guard has to accept exactly what the strip below removes $headerToken = $this->request->getHeader(Application::OIDC_API_REQ_HEADER); - if (!str_starts_with($headerToken, 'bearer ') && !str_starts_with($headerToken, 'Bearer ')) { + if (preg_match('/^bearer\s+/i', $headerToken) !== 1) { $this->logger->debug('No Bearer token'); return ''; } diff --git a/package-lock.json b/package-lock.json index ece1eb10..35203c7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@nextcloud/logger": "^3.0.2", "@nextcloud/password-confirmation": "^6.1.0", "@nextcloud/router": "^3.0.1", - "@nextcloud/vue": "^9.11.0", + "@nextcloud/vue": "^9.13.1", "jstz": "^2.1.1", "vue": "^3.5.17", "vue-material-design-icons": "^5.3.1" @@ -52,9 +52,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "peer": true, @@ -63,22 +63,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -147,15 +147,15 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -381,9 +381,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "peer": true, @@ -408,15 +408,15 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2029,10 +2029,11 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "balanced-match": "^1.0.0", @@ -2116,10 +2117,11 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "balanced-match": "^1.0.0", @@ -2306,15 +2308,15 @@ } }, "node_modules/@jsonjoy.com/fs-core": { - "version": "4.68.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.68.1.tgz", - "integrity": "sha512-V5oZ4Gt9WJKyQef0n9cAd0N9qjSkIBm3E4MYsgNIWBk5aINCDPKxMPo1i29rBxqiT4Ixf1epklqV9VJMKIxwlw==", + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.80.0.tgz", + "integrity": "sha512-qMKWshnyjbyhm+NbzqGE3w5y1mckFFEMj0aDPU2/JBNh1BzIjVy9esq9lBmRnNVmhMEkxwDzLZ8Gqh5G1P5Dag==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.68.1", - "@jsonjoy.com/fs-node-utils": "4.68.1", + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "@jsonjoy.com/fs-node-utils": "4.80.0", "thingies": "^2.5.0" }, "engines": { @@ -2329,16 +2331,16 @@ } }, "node_modules/@jsonjoy.com/fs-fsa": { - "version": "4.68.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.68.1.tgz", - "integrity": "sha512-HCG72UioncuO7Gw09XNVG+S85e3cq2hrUC/mexBrsWsa3mI7eePkkqWie3uVYbtsb64OR9YGQs5SqaufDRYBcg==", + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.80.0.tgz", + "integrity": "sha512-ZBEKl7J6dbkRCfmrFV48Re211A/FHqniTUhsWCsaRteNSOqMgkX3qF1UreVgwB9oSPm81JLAwhIi6Lb/NH/PoA==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@jsonjoy.com/fs-core": "4.68.1", - "@jsonjoy.com/fs-node-builtins": "4.68.1", - "@jsonjoy.com/fs-node-utils": "4.68.1", + "@jsonjoy.com/fs-core": "4.80.0", + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "@jsonjoy.com/fs-node-utils": "4.80.0", "thingies": "^2.5.0" }, "engines": { @@ -2353,19 +2355,19 @@ } }, "node_modules/@jsonjoy.com/fs-node": { - "version": "4.68.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.68.1.tgz", - "integrity": "sha512-R5D9mWtqdURzcOWj1vdXr3APCwX0xchtFT+kmW7fXLNDifWdDrnh26jSID8pdnUfFBxTyfHtFtTL/NWKzIH7kQ==", + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.80.0.tgz", + "integrity": "sha512-yiBlUfkMMRFFGy8CD/h2zd5rPnrM6bjlF+nx2LRAxNV6hOSiuObQIUEE7m7y2VTMisjxCRtzINH94bFGySWL7g==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@jsonjoy.com/fs-core": "4.68.1", - "@jsonjoy.com/fs-node-builtins": "4.68.1", - "@jsonjoy.com/fs-node-utils": "4.68.1", - "@jsonjoy.com/fs-print": "4.68.1", - "@jsonjoy.com/fs-snapshot": "4.68.1", - "glob-to-regex.js": "^1.0.0", + "@jsonjoy.com/fs-core": "4.80.0", + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "@jsonjoy.com/fs-node-utils": "4.80.0", + "@jsonjoy.com/fs-print": "4.80.0", + "@jsonjoy.com/fs-snapshot": "4.80.0", + "glob-to-regex.js": "^1.3.1", "thingies": "^2.5.0" }, "engines": { @@ -2380,9 +2382,9 @@ } }, "node_modules/@jsonjoy.com/fs-node-builtins": { - "version": "4.68.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.68.1.tgz", - "integrity": "sha512-HK1BTksysokNZxNspqDH0yPaqN9YgR/AYIlYiIaU2Ys4BOk5CdybI7r6BgiZuiiPiV8n4sK/kZdice7Znpy2Kw==", + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.80.0.tgz", + "integrity": "sha512-OIOIhqaWiwUySFMny4epIAoviXsyMaQQ63PTc3zH+5HEOvarl5hpUmZIhp2m5+OfyKAh5ylxXnQIgew1OgJDVw==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -2398,16 +2400,16 @@ } }, "node_modules/@jsonjoy.com/fs-node-to-fsa": { - "version": "4.68.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.68.1.tgz", - "integrity": "sha512-lpKmU4X9e/oh8GIuAI7EXaS5QiLNM3KD15CkdhfS6PYmrGvoJqKQcyEfnLgnnaGslh/PFUMYSIZBCf2ejJGw8g==", + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.80.0.tgz", + "integrity": "sha512-ljeaR5xwtX1EyRmB0wvXANUMQZ3mjKcm7z8s2N8D8SLQ04CcAGtUhHFRZQXFb9mhgFyEVMydQGQtSOFS7pcrFQ==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@jsonjoy.com/fs-fsa": "4.68.1", - "@jsonjoy.com/fs-node-builtins": "4.68.1", - "@jsonjoy.com/fs-node-utils": "4.68.1" + "@jsonjoy.com/fs-fsa": "4.80.0", + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "@jsonjoy.com/fs-node-utils": "4.80.0" }, "engines": { "node": ">=10.0" @@ -2421,15 +2423,15 @@ } }, "node_modules/@jsonjoy.com/fs-node-utils": { - "version": "4.68.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.68.1.tgz", - "integrity": "sha512-/GxfW1DWm9SCdkfbvqevLO/P5duobQfmKkHXxdMIDbcZMQeAgooAstIfZhkXpATzq9QbCQsnoWFM/dGHdZfndw==", + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.80.0.tgz", + "integrity": "sha512-xwHeVVi3f+Khg/Fmb8i5qoYVmQ08QznE5TWhQokUmIZjz4KBPzU+zt/+bahBI6G1ghtqajt27lWuAciIxApuzA==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.68.1", - "glob-to-regex.js": "^1.0.1" + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "glob-to-regex.js": "^1.3.1" }, "engines": { "node": ">=10.0" @@ -2443,14 +2445,14 @@ } }, "node_modules/@jsonjoy.com/fs-print": { - "version": "4.68.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.68.1.tgz", - "integrity": "sha512-oGeZOGPYKK9v1CgeVeEDsLomH1lCnslSpqUN5GmPzrmAVGQlsmsdcXNA2O4lV8Y4xkuSuynx2ITBkUHJVaTbow==", + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.80.0.tgz", + "integrity": "sha512-1ocZLJw/m/xlq0moaIHO7HV9nHRVBUvdc2zMzhsJGkFOrqfwWvexfCv5cYCPPLRhP+9p3yunoi4Ep5YhRORC9Q==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@jsonjoy.com/fs-node-utils": "4.68.1", + "@jsonjoy.com/fs-node-utils": "4.80.0", "tree-dump": "^1.1.0" }, "engines": { @@ -2465,15 +2467,15 @@ } }, "node_modules/@jsonjoy.com/fs-snapshot": { - "version": "4.68.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.68.1.tgz", - "integrity": "sha512-XZfP0FDZN32bbc4t2bZN2qRrYHg5AktJnzk22HRoKGK4BprrbNRH2k5ceSNS/kupKYcofCs+O841+xaAbjnxwQ==", + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.80.0.tgz", + "integrity": "sha512-3AV23Q1H228KTtJKTUE0nNq8FCtxSvtENOLrEat80wkAJCKHuqQEv8yjhpS8Bei2OEFJAnluGXp9Ri2SWBlROw==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", - "@jsonjoy.com/fs-node-utils": "4.68.1", + "@jsonjoy.com/fs-node-utils": "4.80.0", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, @@ -2844,6 +2846,66 @@ "node": "^20 || ^22 || ^24" } }, + "node_modules/@nextcloud/dialogs/node_modules/@nextcloud/files": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-4.0.0.tgz", + "integrity": "sha512-TmecnZIS+PGWGtRh7RpGEboCT4K6iTbHULUcfR6hs3eEzjDVsCc1Ldf8popGY/70lbpdlfYle8xbXnPIo3qaXA==", + "license": "AGPL-3.0-or-later", + "dependencies": { + "@nextcloud/auth": "^2.5.3", + "@nextcloud/capabilities": "^1.2.1", + "@nextcloud/l10n": "^3.4.1", + "@nextcloud/logger": "^3.0.3", + "@nextcloud/paths": "^3.0.0", + "@nextcloud/router": "^3.1.0", + "@nextcloud/sharing": "^0.3.0", + "is-svg": "^6.1.0", + "typescript-event-target": "^1.1.2", + "webdav": "^5.9.0" + }, + "engines": { + "node": "^24.0.0" + } + }, + "node_modules/@nextcloud/dialogs/node_modules/@nextcloud/files/node_modules/@nextcloud/files": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.12.2.tgz", + "integrity": "sha512-vBo8tf3Xh6efiF8CrEo3pKj9AtvAF6RdDGO1XKL65IxV8+UUd9Uxl2lUExHlzoDRRczCqfGfaWfRRaFhYqce5Q==", + "license": "AGPL-3.0-or-later", + "optional": true, + "dependencies": { + "@nextcloud/auth": "^2.5.3", + "@nextcloud/capabilities": "^1.2.1", + "@nextcloud/l10n": "^3.4.1", + "@nextcloud/logger": "^3.0.3", + "@nextcloud/paths": "^3.0.0", + "@nextcloud/router": "^3.1.0", + "@nextcloud/sharing": "^0.3.0", + "cancelable-promise": "^4.3.1", + "is-svg": "^6.1.0", + "typescript-event-target": "^1.1.1", + "webdav": "^5.8.0" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + } + }, + "node_modules/@nextcloud/dialogs/node_modules/@nextcloud/files/node_modules/@nextcloud/sharing": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@nextcloud/sharing/-/sharing-0.3.0.tgz", + "integrity": "sha512-kV7qeUZvd1fTKeFyH+W5Qq5rNOqG9rLATZM3U9MBxWXHJs3OxMqYQb8UQ3NYONzsX3zDGJmdQECIGHm1ei2sCA==", + "license": "GPL-3.0-or-later", + "dependencies": { + "@nextcloud/initial-state": "^3.0.0", + "is-svg": "^6.1.0" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + }, + "optionalDependencies": { + "@nextcloud/files": "^3.12.0" + } + }, "node_modules/@nextcloud/eslint-config": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/@nextcloud/eslint-config/-/eslint-config-8.4.2.tgz", @@ -2930,66 +2992,6 @@ "node": ">=10" } }, - "node_modules/@nextcloud/files": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-4.0.0.tgz", - "integrity": "sha512-TmecnZIS+PGWGtRh7RpGEboCT4K6iTbHULUcfR6hs3eEzjDVsCc1Ldf8popGY/70lbpdlfYle8xbXnPIo3qaXA==", - "license": "AGPL-3.0-or-later", - "dependencies": { - "@nextcloud/auth": "^2.5.3", - "@nextcloud/capabilities": "^1.2.1", - "@nextcloud/l10n": "^3.4.1", - "@nextcloud/logger": "^3.0.3", - "@nextcloud/paths": "^3.0.0", - "@nextcloud/router": "^3.1.0", - "@nextcloud/sharing": "^0.3.0", - "is-svg": "^6.1.0", - "typescript-event-target": "^1.1.2", - "webdav": "^5.9.0" - }, - "engines": { - "node": "^24.0.0" - } - }, - "node_modules/@nextcloud/files/node_modules/@nextcloud/files": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.12.2.tgz", - "integrity": "sha512-vBo8tf3Xh6efiF8CrEo3pKj9AtvAF6RdDGO1XKL65IxV8+UUd9Uxl2lUExHlzoDRRczCqfGfaWfRRaFhYqce5Q==", - "license": "AGPL-3.0-or-later", - "optional": true, - "dependencies": { - "@nextcloud/auth": "^2.5.3", - "@nextcloud/capabilities": "^1.2.1", - "@nextcloud/l10n": "^3.4.1", - "@nextcloud/logger": "^3.0.3", - "@nextcloud/paths": "^3.0.0", - "@nextcloud/router": "^3.1.0", - "@nextcloud/sharing": "^0.3.0", - "cancelable-promise": "^4.3.1", - "is-svg": "^6.1.0", - "typescript-event-target": "^1.1.1", - "webdav": "^5.8.0" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || ^24.0.0" - } - }, - "node_modules/@nextcloud/files/node_modules/@nextcloud/sharing": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nextcloud/sharing/-/sharing-0.3.0.tgz", - "integrity": "sha512-kV7qeUZvd1fTKeFyH+W5Qq5rNOqG9rLATZM3U9MBxWXHJs3OxMqYQb8UQ3NYONzsX3zDGJmdQECIGHm1ei2sCA==", - "license": "GPL-3.0-or-later", - "dependencies": { - "@nextcloud/initial-state": "^3.0.0", - "is-svg": "^6.1.0" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || ^24.0.0" - }, - "optionalDependencies": { - "@nextcloud/files": "^3.12.0" - } - }, "node_modules/@nextcloud/initial-state": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@nextcloud/initial-state/-/initial-state-3.0.0.tgz", @@ -3055,9 +3057,9 @@ } }, "node_modules/@nextcloud/router": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-3.1.0.tgz", - "integrity": "sha512-e4dkIaxRSwdZJlZFpn9x03QgBn/Sa2hN1hp/BA7+AbzykmSAlKuWfdmX8j/8ewrLpQwYmZR23IZO9XwpJXq2Uw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-3.2.0.tgz", + "integrity": "sha512-an4gJ7mlDZGtYq8zTxp4Sg+iLlKKGvQ9ZH3Dp6ga/IlDNAP7VeMVGphsY798ORjncAp9+sEf370L5zpsnuxgng==", "license": "GPL-3.0-or-later", "dependencies": { "@nextcloud/typings": "^1.10.0" @@ -3082,6 +3084,68 @@ "@nextcloud/files": "^3.12.2 || ^4.0.0" } }, + "node_modules/@nextcloud/sharing/node_modules/@nextcloud/files": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-4.0.0.tgz", + "integrity": "sha512-TmecnZIS+PGWGtRh7RpGEboCT4K6iTbHULUcfR6hs3eEzjDVsCc1Ldf8popGY/70lbpdlfYle8xbXnPIo3qaXA==", + "license": "AGPL-3.0-or-later", + "optional": true, + "dependencies": { + "@nextcloud/auth": "^2.5.3", + "@nextcloud/capabilities": "^1.2.1", + "@nextcloud/l10n": "^3.4.1", + "@nextcloud/logger": "^3.0.3", + "@nextcloud/paths": "^3.0.0", + "@nextcloud/router": "^3.1.0", + "@nextcloud/sharing": "^0.3.0", + "is-svg": "^6.1.0", + "typescript-event-target": "^1.1.2", + "webdav": "^5.9.0" + }, + "engines": { + "node": "^24.0.0" + } + }, + "node_modules/@nextcloud/sharing/node_modules/@nextcloud/sharing": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@nextcloud/sharing/-/sharing-0.3.0.tgz", + "integrity": "sha512-kV7qeUZvd1fTKeFyH+W5Qq5rNOqG9rLATZM3U9MBxWXHJs3OxMqYQb8UQ3NYONzsX3zDGJmdQECIGHm1ei2sCA==", + "license": "GPL-3.0-or-later", + "optional": true, + "dependencies": { + "@nextcloud/initial-state": "^3.0.0", + "is-svg": "^6.1.0" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + }, + "optionalDependencies": { + "@nextcloud/files": "^3.12.0" + } + }, + "node_modules/@nextcloud/sharing/node_modules/@nextcloud/sharing/node_modules/@nextcloud/files": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.12.2.tgz", + "integrity": "sha512-vBo8tf3Xh6efiF8CrEo3pKj9AtvAF6RdDGO1XKL65IxV8+UUd9Uxl2lUExHlzoDRRczCqfGfaWfRRaFhYqce5Q==", + "license": "AGPL-3.0-or-later", + "optional": true, + "dependencies": { + "@nextcloud/auth": "^2.5.3", + "@nextcloud/capabilities": "^1.2.1", + "@nextcloud/l10n": "^3.4.1", + "@nextcloud/logger": "^3.0.3", + "@nextcloud/paths": "^3.0.0", + "@nextcloud/router": "^3.1.0", + "@nextcloud/sharing": "^0.3.0", + "cancelable-promise": "^4.3.1", + "is-svg": "^6.1.0", + "typescript-event-target": "^1.1.1", + "webdav": "^5.8.0" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + } + }, "node_modules/@nextcloud/stylelint-config": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@nextcloud/stylelint-config/-/stylelint-config-3.2.2.tgz", @@ -3113,9 +3177,9 @@ } }, "node_modules/@nextcloud/vue": { - "version": "9.11.0", - "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-9.11.0.tgz", - "integrity": "sha512-LRsyU9Mxs0b2xWqbxygps8O7/i1z4QhNQD9/QDni+VqFY3V0N95mAgmKJm6vfoFuxTAMXERp9bjqFn3UV542gw==", + "version": "9.13.1", + "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-9.13.1.tgz", + "integrity": "sha512-6ZeHm+coHJ6wsavulll4CVl3tk5ehjh7p6pW9GU1BgfDkOsbx5Pv4U8/uVk7yOTn4oAo695XTHhFN17pyI1peQ==", "license": "AGPL-3.0-or-later", "dependencies": { "@ckpack/vue-color": "^1.6.0", @@ -3128,7 +3192,7 @@ "@nextcloud/initial-state": "^3.0.0", "@nextcloud/l10n": "^3.4.1", "@nextcloud/logger": "^3.0.3", - "@nextcloud/router": "^3.1.0", + "@nextcloud/router": "^3.2.0", "@nextcloud/sharing": "^0.4.0", "@nextcloud/vue-select": "^4.1.0", "@vuepic/vue-datepicker": "^11.0.3", @@ -3137,7 +3201,7 @@ "blurhash": "^2.0.5", "clone": "^2.1.2", "debounce": "^3.0.0", - "dompurify": "^3.4.14", + "dompurify": "^3.4.15", "emoji-mart-vue-fast": "^15.0.5", "escape-html": "^1.0.3", "floating-vue": "^5.2.2", @@ -3161,10 +3225,22 @@ "unist-builder": "^4.0.0", "unist-util-visit-parents": "^6.0.2", "vue": "^3.5.18", - "vue-router": "^5.2.0" + "vue-router": "^5.3.1" }, "engines": { "node": "^20.11.0 || ^22 || ^24" + }, + "peerDependencies": { + "@nextcloud/dialogs": "^7.4.0", + "@nextcloud/files": "^4.1.0" + }, + "peerDependenciesMeta": { + "@nextcloud/dialogs": { + "optional": true + }, + "@nextcloud/files": { + "optional": true + } } }, "node_modules/@nextcloud/vue-select": { @@ -4011,12 +4087,6 @@ "@types/sizzle": "*" } }, - "node_modules/@types/jsesc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz", - "integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==", - "license": "MIT" - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -5691,9 +5761,9 @@ } }, "node_modules/axios": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", - "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", + "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", @@ -5915,9 +5985,10 @@ "peer": true }, "node_modules/brace-expansion": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", - "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -7445,9 +7516,9 @@ } }, "node_modules/dompurify": { - "version": "3.4.14", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.14.tgz", - "integrity": "sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==", + "version": "3.4.16", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.16.tgz", + "integrity": "sha512-sqo+pNp3qRhCIpbgRi1y8Tgk27Bo2Ry7w0dC1NBeNTdZChWjz9Xb/KOoZbRP/R6pQZ80Qw8YhXw13hWWBbMRnQ==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -8091,10 +8162,11 @@ } }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "balanced-match": "^1.0.0", @@ -8209,10 +8281,11 @@ } }, "node_modules/eslint-plugin-n/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "balanced-match": "^1.0.0", @@ -8354,10 +8427,11 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "balanced-match": "^1.0.0", @@ -9245,9 +9319,9 @@ } }, "node_modules/glob-to-regex.js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", - "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.3.1.tgz", + "integrity": "sha512-zhWhMRsgnOym+1txhUmYJGD6eUsbUdrZv55gCYKuJX8t2p3+M8My23PD09bEIsreGUs5pnSa/idmcfp2/IlqGg==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -9263,10 +9337,11 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "balanced-match": "^1.0.0", @@ -10928,7 +11003,9 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, "license": "MIT", + "peer": true, "bin": { "jsesc": "bin/jsesc" }, @@ -10972,7 +11049,9 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "license": "MIT", + "peer": true, "bin": { "json5": "lib/cli.js" }, @@ -11474,24 +11553,24 @@ } }, "node_modules/memfs": { - "version": "4.68.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.68.1.tgz", - "integrity": "sha512-OD+IDRUvIxu3QHL+nFm9gdyugInD27FDJ+sl4B5QgomPHXMlbw+GP918P8VNKu2FkNlVeqBkpzkwROpamVifRw==", + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.80.0.tgz", + "integrity": "sha512-N70W2isi44XdGn7uPo9NJ4zRukcdkwSFHlnYx42SWOH/+hzqNc8P7+tVoA2up4z7faHvNi1RSNCFjxoYQ71VPQ==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@jsonjoy.com/fs-core": "4.68.1", - "@jsonjoy.com/fs-fsa": "4.68.1", - "@jsonjoy.com/fs-node": "4.68.1", - "@jsonjoy.com/fs-node-builtins": "4.68.1", - "@jsonjoy.com/fs-node-to-fsa": "4.68.1", - "@jsonjoy.com/fs-node-utils": "4.68.1", - "@jsonjoy.com/fs-print": "4.68.1", - "@jsonjoy.com/fs-snapshot": "4.68.1", + "@jsonjoy.com/fs-core": "4.80.0", + "@jsonjoy.com/fs-fsa": "4.80.0", + "@jsonjoy.com/fs-node": "4.80.0", + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "@jsonjoy.com/fs-node-to-fsa": "4.80.0", + "@jsonjoy.com/fs-node-utils": "4.80.0", + "@jsonjoy.com/fs-print": "4.80.0", + "@jsonjoy.com/fs-snapshot": "4.80.0", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", - "glob-to-regex.js": "^1.0.1", + "glob-to-regex.js": "^1.3.1", "thingies": "^2.5.0", "tree-dump": "^1.0.3", "tslib": "^2.0.0" @@ -12999,16 +13078,22 @@ } }, "node_modules/pkg-types": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", - "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.3.tgz", + "integrity": "sha512-j/lCFdcppV0JxWpCEITdbDltBxPP6cHT+yNJ6Go2OgoSA9518X847X9z0p6LtA4Nc16+eQzCZjRrWanTGvHJ5w==", "license": "MIT", "dependencies": { - "confbox": "^0.2.4", - "exsolve": "^1.0.8", + "confbox": "^0.3.1", + "exsolve": "^1.1.1", "pathe": "^2.0.3" } }, + "node_modules/pkg-types/node_modules/confbox": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", + "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", + "license": "MIT" + }, "node_modules/pkijs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", @@ -16629,9 +16714,9 @@ } }, "node_modules/unplugin-utils/node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "license": "MIT", "engines": { "node": ">=12" @@ -16641,9 +16726,9 @@ } }, "node_modules/unplugin/node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "license": "MIT", "engines": { "node": ">=12" @@ -16987,17 +17072,16 @@ } }, "node_modules/vue-router": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.2.0.tgz", - "integrity": "sha512-QAC5i0LEb1GLG0LXDQmHu8L7FX12j0KwU/JTKmLQUJMrn04gQdKP6Du+p0QwpHb3iy71vBlqnHQ8WAfOSAWhqw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.3.1.tgz", + "integrity": "sha512-GDBZzgmILxA/kFnkFbJjQZdZ2QQbngnIMMuoUcjhZIfH1RGMaPjPwX5ASnV38qamuA9uhO0RDjSBHTDNG2uXyQ==", "license": "MIT", "dependencies": { - "@babel/generator": "^8.0.0", "@vue-macros/common": "^3.1.3", "@vue/devtools-api": "^8.1.5", "ast-walker-scope": "^0.9.0", "chokidar": "^5.0.0", - "json5": "^2.2.3", + "confbox": "^0.2.4", "local-pkg": "^1.2.1", "magic-string": "^0.30.21", "mlly": "^1.8.2", @@ -17008,8 +17092,7 @@ "scule": "^1.3.0", "tinyglobby": "^0.2.17", "unplugin": "^3.3.0", - "unplugin-utils": "^0.3.2", - "yaml": "^2.9.0" + "unplugin-utils": "^0.3.2" }, "funding": { "url": "https://github.com/sponsors/posva" @@ -17036,69 +17119,6 @@ } } }, - "node_modules/vue-router/node_modules/@babel/generator": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz", - "integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^8.0.0", - "@babel/types": "^8.0.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "@types/jsesc": "^2.5.0", - "jsesc": "^3.0.2" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/vue-router/node_modules/@babel/helper-string-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", - "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/vue-router/node_modules/@babel/helper-validator-identifier": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.4.tgz", - "integrity": "sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==", - "license": "MIT", - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/vue-router/node_modules/@babel/parser": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.4.tgz", - "integrity": "sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g==", - "license": "MIT", - "dependencies": { - "@babel/types": "^8.0.4" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, - "node_modules/vue-router/node_modules/@babel/types": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.4.tgz", - "integrity": "sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^8.0.0", - "@babel/helper-validator-identifier": "^8.0.4" - }, - "engines": { - "node": "^22.18.0 || >=24.11.0" - } - }, "node_modules/vue-router/node_modules/chokidar": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", @@ -17115,9 +17135,9 @@ } }, "node_modules/vue-router/node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "license": "MIT", "engines": { "node": ">=12" @@ -17349,16 +17369,17 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-8.1.1.tgz", - "integrity": "sha512-JrxAE/HwNmEnTkzkUGHFItHpGalzuEIUDifXhjAkIEHZzHK/ZJFVoTQF5ubZQlgeRireqLdr2lZttrrmOH61IA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-8.3.0.tgz", + "integrity": "sha512-jFNNMB29ugmkEUFW9cpWNqcWFQNwuMTMVoRpKVAiz/5nh3gA4x9x6DRPhNHGc/SLre7pEP9yrAPDAmCvdCblKg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "memfs": "^4.56.10", + "ansi-html-community": "^0.0.8", + "memfs": "^4.68.2", "mime-types": "^3.0.2", - "range-parser": "^1.2.1", + "range-parser": "^1.3.0", "schema-utils": "^4.3.3" }, "engines": { @@ -17789,21 +17810,6 @@ "license": "ISC", "peer": true }, - "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 265d7349..2032fb19 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@nextcloud/logger": "^3.0.2", "@nextcloud/password-confirmation": "^6.1.0", "@nextcloud/router": "^3.0.1", - "@nextcloud/vue": "^9.11.0", + "@nextcloud/vue": "^9.13.1", "jstz": "^2.1.1", "vue": "^3.5.17", "vue-material-design-icons": "^5.3.1" diff --git a/tests/unit/Service/OIDCServiceTest.php b/tests/unit/Service/OIDCServiceTest.php new file mode 100644 index 00000000..512d6e56 --- /dev/null +++ b/tests/unit/Service/OIDCServiceTest.php @@ -0,0 +1,88 @@ +discoveryService = $this->createMock(DiscoveryService::class); + $this->clientHelper = $this->createMock(HttpClientHelper::class); + $this->provider = $this->createMock(Provider::class); + $this->oidcService = new OIDCService( + $this->discoveryService, + $this->createMock(LoggerInterface::class), + $this->clientHelper, + $this->createMock(ICrypto::class), + ); + + $this->discoveryService->method('obtainDiscovery') + ->willReturn(['userinfo_endpoint' => 'https://idp.example.org/userinfo']); + } + + private static function createRsaKeyPair(): array { + $key = openssl_pkey_new(['private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA]); + openssl_pkey_export($key, $privateKey); + return [$privateKey, openssl_pkey_get_details($key)['key']]; + } + + public function testUserinfoJsonResponse(): void { + $this->clientHelper->method('get') + ->willReturn(json_encode(['sub' => 'alice', 'email' => 'alice@example.org'])); + $this->discoveryService->expects($this->never())->method('obtainJWK'); + + $this->assertSame( + ['sub' => 'alice', 'email' => 'alice@example.org'], + $this->oidcService->userinfo($this->provider, 'access-token'), + ); + } + + public function testUserinfoSignedJwtResponse(): void { + [$privateKey, $publicKey] = self::createRsaKeyPair(); + $jwt = JWT::encode(['sub' => 'alice', 'email' => 'alice@example.org'], $privateKey, 'RS256', 'key1'); + + $this->clientHelper->method('get')->willReturn($jwt); + $this->discoveryService->expects($this->once()) + ->method('obtainJWK') + ->with($this->provider, $jwt) + ->willReturn(['key1' => new Key($publicKey, 'RS256')]); + + $this->assertSame( + ['sub' => 'alice', 'email' => 'alice@example.org'], + $this->oidcService->userinfo($this->provider, 'access-token'), + ); + } + + public function testUserinfoJwtWithInvalidSignature(): void { + [, $publicKey] = self::createRsaKeyPair(); + [$otherPrivateKey] = self::createRsaKeyPair(); + $jwt = JWT::encode(['sub' => 'mallory'], $otherPrivateKey, 'RS256', 'key1'); + + $this->clientHelper->method('get')->willReturn($jwt); + $this->discoveryService->method('obtainJWK') + ->willReturn(['key1' => new Key($publicKey, 'RS256')]); + + $this->assertSame([], $this->oidcService->userinfo($this->provider, 'access-token')); + } +} diff --git a/tests/unit/User/BackendTest.php b/tests/unit/User/BackendTest.php index 8045da60..806af8b4 100644 --- a/tests/unit/User/BackendTest.php +++ b/tests/unit/User/BackendTest.php @@ -27,6 +27,7 @@ use OCP\IUserManager; use OCP\IUserSession; use OCP\Server; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; @@ -60,6 +61,7 @@ class BackendTest extends \Test\TestCase { private const TOKEN_USER_ID = 'oidc-token-user'; private const PROVIDER_ID = 1; + private const BEARER_CREDENTIALS = 'a-valid-token'; private IConfig&MockObject $config; private UserMapper&MockObject $userMapper; @@ -120,11 +122,7 @@ protected function setUp(): void { */ public function testBearerAuthWithAutoProvisioningDoesNotLogTheUserIn(): void { $this->givenAValidBearerToken(); - - $user = $this->givenAUserThatHasLoggedInBefore(self::TOKEN_USER_ID); - $this->userManager->method('userExists')->with(self::TOKEN_USER_ID)->willReturn(true); - $this->userManager->method('get')->with(self::TOKEN_USER_ID)->willReturn($user); - $this->ldapService->method('isLdapDeletedUser')->with($user)->willReturn(false); + $this->givenTheTokenUserExists(); $this->assertResolvesUserWithoutLoggingIn(self::TOKEN_USER_ID); } @@ -159,16 +157,87 @@ public function testBearerAuthForUserOfAnotherBackendDoesNotLogTheUserIn(): void } /** - * Wires up a request carrying a bearer token that the self encoded validator - * accepts for a provider with bearer checking turned on. + * RFC 9110, section 11.1 (and RFC 7235, section 2.1 before it): the auth-scheme + * of an Authorization header is case-insensitive, and it is separated from the + * credentials by one or more spaces or horizontal tabs. The guard in + * getCurrentUserId() used to accept only the exact spellings 'bearer ' and + * 'Bearer ', so a client sending 'BEARER ' was turned away before any + * token validator ran, even though the strip right after it was already + * case-insensitive. + * + * @see https://github.com/nextcloud/user_oidc/pull/1386 + */ + #[DataProvider('acceptedAuthorizationHeaderProvider')] + public function testBearerSchemeIsRecognisedRegardlessOfSpellingAndSeparator(string $authorizationHeader): void { + $this->givenAValidBearerToken(authorizationHeader: $authorizationHeader); + $this->givenTheTokenUserExists(); + + $this->assertResolvesUserWithoutLoggingIn(self::TOKEN_USER_ID); + } + + /** + * Accepting every spelling of the scheme must not make the guard accept anything + * that is not a bearer token: none of these may reach a token validator. + */ + #[DataProvider('rejectedAuthorizationHeaderProvider')] + public function testNonBearerAuthorizationHeaderIsNotValidated(string $authorizationHeader): void { + $this->givenARequestWithAuthorizationHeader($authorizationHeader); + + $validator = $this->createMock(SelfEncodedValidator::class); + $validator->expects($this->never())->method('isValidBearerToken'); + $this->overwriteService(SelfEncodedValidator::class, $validator); + + $this->assertSame('', $this->backend->getCurrentUserId()); + } + + /** + * @return array + */ + public static function acceptedAuthorizationHeaderProvider(): array { + return [ + 'canonical spelling' => ['Bearer ' . self::BEARER_CREDENTIALS], + 'all lower case' => ['bearer ' . self::BEARER_CREDENTIALS], + 'all upper case' => ['BEARER ' . self::BEARER_CREDENTIALS], + 'mixed case' => ['BeArEr ' . self::BEARER_CREDENTIALS], + 'several spaces' => ['Bearer ' . self::BEARER_CREDENTIALS], + 'horizontal tab' => ["Bearer\t" . self::BEARER_CREDENTIALS], + 'tab and space' => ["Bearer \t " . self::BEARER_CREDENTIALS], + ]; + } + + /** + * @return array + */ + public static function rejectedAuthorizationHeaderProvider(): array { + return [ + 'no header at all' => [''], + 'another scheme' => ['Basic dXNlcjpwYXNzd29yZA=='], + // the scheme and the credentials need a separator between them + 'no separator' => ['Bearertoken'], + 'scheme only' => ['Bearer'], + // these do pass the scheme guard and are caught by the empty credentials + // check right after the strip + 'empty credentials' => ['Bearer '], + 'empty credentials, upper case' => ['BEARER '], + 'whitespace only credentials' => ["Bearer \t "], + // the scheme has to come first + 'leading whitespace' => [' Bearer ' . self::BEARER_CREDENTIALS], + 'scheme in the middle' => ['X Bearer ' . self::BEARER_CREDENTIALS], + ]; + } + + /** + * Wires up a request carrying $authorizationHeader for a provider with bearer + * checking turned on. No token validator is registered: callers decide whether + * the credentials are expected to reach one. * * @param array $systemConfig the 'user_oidc' system config */ - private function givenAValidBearerToken(array $systemConfig = []): void { + private function givenARequestWithAuthorizationHeader(string $authorizationHeader, array $systemConfig = []): void { $this->config->method('getSystemValue')->with('user_oidc', [])->willReturn($systemConfig); $this->request->method('getHeader') ->with(Application::OIDC_API_REQ_HEADER) - ->willReturn('Bearer a-valid-token'); + ->willReturn($authorizationHeader); $provider = new Provider(); $provider->setId(self::PROVIDER_ID); @@ -183,12 +252,43 @@ private function givenAValidBearerToken(array $systemConfig = []): void { $this->discoveryService->method('obtainDiscovery')->willReturn([]); $this->timeFactory->method('getTime')->willReturn(1700000000); + } + + /** + * Wires up a request carrying a bearer token that the self encoded validator + * accepts for a provider with bearer checking turned on. + * + * @param array $systemConfig the 'user_oidc' system config + */ + private function givenAValidBearerToken( + array $systemConfig = [], + string $authorizationHeader = 'Bearer ' . self::BEARER_CREDENTIALS, + ): void { + $this->givenARequestWithAuthorizationHeader($authorizationHeader, $systemConfig); $validator = $this->createMock(SelfEncodedValidator::class); - $validator->method('isValidBearerToken')->willReturn(self::TOKEN_USER_ID); + // the validator only accepts the credentials, so a guard or a strip that + // mangles the auth-scheme cannot pass this by returning the uid regardless + $validator->method('isValidBearerToken')->willReturnCallback( + static fn (Provider $provider, string $bearerToken): ?string + => $bearerToken === self::BEARER_CREDENTIALS ? self::TOKEN_USER_ID : null + ); $this->overwriteService(SelfEncodedValidator::class, $validator); } + /** + * The token user already exists, so auto-provisioning reuses it instead of + * creating it. + */ + private function givenTheTokenUserExists(): IUser&MockObject { + $user = $this->givenAUserThatHasLoggedInBefore(self::TOKEN_USER_ID); + $this->userManager->method('userExists')->with(self::TOKEN_USER_ID)->willReturn(true); + $this->userManager->method('get')->with(self::TOKEN_USER_ID)->willReturn($user); + $this->ldapService->method('isLdapDeletedUser')->with($user)->willReturn(false); + + return $user; + } + private function givenAUserThatHasLoggedInBefore(string $uid): IUser&MockObject { $user = $this->createMock(IUser::class); $user->method('getUID')->willReturn($uid);