API: Implement OIDC redirect endpoint #782

Requires further testing and refinement before it can be released.

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2024-07-01 16:50:53 +02:00
parent d25b555dbc
commit a97f8d0795
131 changed files with 756 additions and 409 deletions

View File

@@ -5,7 +5,8 @@
</div>
</noscript>
<div id="splash-info" class="has-js splash-info">
{{if .config.LegalInfo}}
{{if .error }}<span class="legal-info">{{ .error }}</span>
{{else if .config.LegalInfo}}
{{if .config.LegalUrl}}<a href="{{ .config.LegalUrl }}" target="_blank" class="legal-info">{{ .config.LegalInfo }}</a>
{{else}}<span class="legal-info">{{ .config.LegalInfo }}</span>{{end}}
{{else}}

View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en" data-color-mode="dark" data-light-theme="light" data-dark-theme="dark" class="overflow-y-hidden">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport"
content="width=device-width, initial-scale=1.0{{if not .config.Settings.UI.Zoom }}, maximum-scale=1.0, user-scalable=no{{end}}">
<title>{{if and .config.SiteCaption .config.Sponsor }}{{ .config.SiteCaption }}{{else}}{{ .config.Name }}{{end}}</title>
{{template "favicons.gohtml" .}}
<link rel="stylesheet" href="{{ .config.CssUri }}">
<link rel="manifest" href="{{ .config.ManifestUri }}" crossorigin="use-credentials">
</head>
<body class="{{ .config.Flags }} nojs">
{{template "app.gohtml" .}}
<script>{{ if eq .status "success" }}
window.localStorage.setItem("sessionId", {{ .session_id }});
window.localStorage.setItem("authToken", {{ .access_token }});
window.localStorage.setItem("provider", {{ .provider }});
window.localStorage.setItem("user", JSON.stringify({{ .user }}));
window.localStorage.removeItem("authError");
{{ else if eq .status "failed" }}
window.localStorage.setItem("authError", {{ .error }});
{{ else }}
window.localStorage.removeItem("sessionId");
window.localStorage.removeItem("authToken");
window.localStorage.removeItem("provider");
window.localStorage.removeItem("user");
window.localStorage.removeItem("authError");
{{ end }}
window.location.href = {{ .config.LoginUri }};
</script>
</body>
</html>

View File

@@ -1876,9 +1876,9 @@
}
},
"node_modules/@csstools/cascade-layer-name-parser": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.11.tgz",
"integrity": "sha512-yhsonEAhaWRQvHFYhSzOUobH2Ev++fMci+ppFRagw0qVSPlcPV4FnNmlwpM/b2BM10ZeMRkVV4So6YRswD0O0w==",
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.12.tgz",
"integrity": "sha512-iNCCOnaoycAfcIot3v/orjkTol+j8+Z5xgpqxUpZSdqeaxCADQZtldHhlvzDipmi7OoWdcJUO6DRZcnkMSBEIg==",
"funding": [
{
"type": "github",
@@ -1894,14 +1894,14 @@
"node": "^14 || ^16 || >=18"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1"
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2"
}
},
"node_modules/@csstools/color-helpers": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-4.2.0.tgz",
"integrity": "sha512-hJJrSBzbfGxUsaR6X4Bzd/FLx0F1ulKnR5ljY9AiXCtsR+H+zSWQDFWlKES1BRaVZTDHLpIIHS9K2o0h+JLlrg==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-4.2.1.tgz",
"integrity": "sha512-CEypeeykO9AN7JWkr1OEOQb0HRzZlPWGwV0Ya6DuVgFdDi6g3ma/cPZ5ZPZM4AWQikDpq/0llnGGlIL+j8afzw==",
"funding": [
{
"type": "github",
@@ -1918,9 +1918,9 @@
}
},
"node_modules/@csstools/css-calc": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-1.2.2.tgz",
"integrity": "sha512-0owrl7AruDRKAxoSIW8XzJdz7GnuW3AOj4rYLfmXsoKIX2ZZzttzGXoiC8n8V08X7wIBlEWWVB4C8fAN18+I6Q==",
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-1.2.3.tgz",
"integrity": "sha512-rlOh81K3CvtY969Od5b1h29YT6MpCHejMCURKrRrXFeCpz67HGaBNvBmWT5S7S+CKn+V7KJ+qxSmK8jNd/aZWA==",
"funding": [
{
"type": "github",
@@ -1936,14 +1936,14 @@
"node": "^14 || ^16 || >=18"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1"
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2"
}
},
"node_modules/@csstools/css-color-parser": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-2.0.2.tgz",
"integrity": "sha512-Agx2YmxTcZ7TfB7KNZQ+iekaxbWSdblvtA35aTwE3KfuYyjOlCg3P4KGGdQF/cjm1pHWVSBo5duF/BRfZ8s07A==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-2.0.3.tgz",
"integrity": "sha512-Qqhb5I/gEh1wI4brf6Kmy0Xn4J1IqO8OTDKWGRsBYtL4bGkHcV9i0XI2Mmo/UYFtSRoXW/RmKTcMh6sCI433Cw==",
"funding": [
{
"type": "github",
@@ -1956,21 +1956,21 @@
],
"license": "MIT",
"dependencies": {
"@csstools/color-helpers": "^4.2.0",
"@csstools/css-calc": "^1.2.2"
"@csstools/color-helpers": "^4.2.1",
"@csstools/css-calc": "^1.2.3"
},
"engines": {
"node": "^14 || ^16 || >=18"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1"
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2"
}
},
"node_modules/@csstools/css-parser-algorithms": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.3.tgz",
"integrity": "sha512-xI/tL2zxzEbESvnSxwFgwvy5HS00oCXxL4MLs6HUiDcYfwowsoQaABKxUElp1ARITrINzBnsECOc1q0eg2GOrA==",
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.0.tgz",
"integrity": "sha512-qvBMcOU/uWFCH/VO0MYe0AMs0BGMWAt6FTryMbFIKYtZtVnqTZtT8ktv5o718llkaGZWomJezJZjq3vJDHeJNQ==",
"funding": [
{
"type": "github",
@@ -1986,13 +1986,13 @@
"node": "^14 || ^16 || >=18"
},
"peerDependencies": {
"@csstools/css-tokenizer": "^2.3.1"
"@csstools/css-tokenizer": "^2.3.2"
}
},
"node_modules/@csstools/css-tokenizer": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.3.1.tgz",
"integrity": "sha512-iMNHTyxLbBlWIfGtabT157LH9DUx9X8+Y3oymFEuMj8HNc+rpE3dPFGFgHjpKfjeFDjLjYIAIhXPGvS2lKxL9g==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.3.2.tgz",
"integrity": "sha512-0xYOf4pQpAaE6Sm2Q0x3p25oRukzWQ/O8hWVvhIt9Iv98/uu053u2CGm/g3kJ+P0vOYTAYzoU8Evq2pg9ZPXtw==",
"funding": [
{
"type": "github",
@@ -2009,9 +2009,9 @@
}
},
"node_modules/@csstools/media-query-list-parser": {
"version": "2.1.11",
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.11.tgz",
"integrity": "sha512-uox5MVhvNHqitPP+SynrB1o8oPxPMt2JLgp5ghJOWf54WGQ5OKu47efne49r1SWqs3wRP8xSWjnO9MBKxhB1dA==",
"version": "2.1.12",
"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.12.tgz",
"integrity": "sha512-t1/CdyVJzOQUiGUcIBXRzTAkWTFPxiPnoKwowKW2z9Uj78c2bBWI/X94BeVfUwVq1xtCjD7dnO8kS6WONgp8Jw==",
"funding": [
{
"type": "github",
@@ -2027,8 +2027,8 @@
"node": "^14 || ^16 || >=18"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1"
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2"
}
},
"node_modules/@csstools/postcss-cascade-layers": {
@@ -2058,9 +2058,9 @@
}
},
"node_modules/@csstools/postcss-color-function": {
"version": "3.0.16",
"resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-3.0.16.tgz",
"integrity": "sha512-KtmXfckANSKsLBoTQCzggvKft1cmmmDKYjFO4yVlB23nWUgGInVBTE9T5JLmH29NNdTWSEPLWPUxoQ6XiIEn2Q==",
"version": "3.0.17",
"resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-3.0.17.tgz",
"integrity": "sha512-hi6g5KHMvxpxf01LCVu5xnNxX5h2Vkn9aKRmspn2esWjWtshuTXVOavTjwvogA+Eycm9Rn21QTYNU+qbKw6IeQ==",
"funding": [
{
"type": "github",
@@ -2073,9 +2073,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-color-parser": "^2.0.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/css-color-parser": "^2.0.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
@@ -2087,9 +2087,9 @@
}
},
"node_modules/@csstools/postcss-color-mix-function": {
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-2.0.16.tgz",
"integrity": "sha512-BJnD1M5Pdypl1cJuwGuzVC52PqgzaObsDLu34jgf+QU7daVFqz432PvpqvXTmfTSNt4OckOT1QIzWexEFlDNXw==",
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-2.0.17.tgz",
"integrity": "sha512-Y65GHGCY1R+9+/5KrJjN7gAF1NZydng4AGknMggeUJIyo2ckLb4vBrlDmpIcHDdjQtV5631j1hxvalVTbpoiFw==",
"funding": [
{
"type": "github",
@@ -2102,9 +2102,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-color-parser": "^2.0.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/css-color-parser": "^2.0.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
@@ -2116,9 +2116,9 @@
}
},
"node_modules/@csstools/postcss-exponential-functions": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-1.0.7.tgz",
"integrity": "sha512-9usBPQX74OhiF/VuaVrp44UAPzqbKNyoaxEa6tbEXiFp+OAm3yB/TLRKyPUWg5tvvHGCduGJVdJJB3w8c8NBtA==",
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-1.0.8.tgz",
"integrity": "sha512-/4WHpu4MrCCsUWRaDreyBcdF+5xnudk1JJLg6aWREeMaSpr3vsD0eywmOXct3xUm28TCqKS//S86IlcDJJdzoQ==",
"funding": [
{
"type": "github",
@@ -2131,9 +2131,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-calc": "^1.2.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1"
"@csstools/css-calc": "^1.2.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2"
},
"engines": {
"node": "^14 || ^16 || >=18"
@@ -2169,9 +2169,9 @@
}
},
"node_modules/@csstools/postcss-gamut-mapping": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-1.0.9.tgz",
"integrity": "sha512-JmOeiBJj1RJriAkr+aLBaiYUpEqdNOIo3ERQ5a4uNzy18upzrQ6tz7m2Vt1GQpJ62zQj7rC5PjAhCoZCoyE31g==",
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-1.0.10.tgz",
"integrity": "sha512-iPz4/cO8YiNjAYdtAiKGBdKZdFlAvDtUr2AgvAMxCa83e9MwTIKmsJZC3Frw7VYmkfknmdElEZr1FJU+PmB2PA==",
"funding": [
{
"type": "github",
@@ -2184,9 +2184,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-color-parser": "^2.0.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1"
"@csstools/css-color-parser": "^2.0.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2"
},
"engines": {
"node": "^14 || ^16 || >=18"
@@ -2196,9 +2196,9 @@
}
},
"node_modules/@csstools/postcss-gradients-interpolation-method": {
"version": "4.0.17",
"resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-4.0.17.tgz",
"integrity": "sha512-qSNIqzLPKd2SadfWwHZv42lDRyYlLaM+Vx5rRIsnYCZbQxzFfe1XAwssrcCsHgba5bA6bi5oDoFCx0W+PRCpfw==",
"version": "4.0.18",
"resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-4.0.18.tgz",
"integrity": "sha512-rZH7RnNYY911I/n8+DRrcri89GffptdyuFDGGj/UbxDISFirdR1uI/wcur9KYR/uFHXqrnJjrfi1cisfB7bL+g==",
"funding": [
{
"type": "github",
@@ -2211,9 +2211,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-color-parser": "^2.0.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/css-color-parser": "^2.0.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
@@ -2225,9 +2225,9 @@
}
},
"node_modules/@csstools/postcss-hwb-function": {
"version": "3.0.15",
"resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-3.0.15.tgz",
"integrity": "sha512-l34fRiZ7o5+pULv7OplXniBTU4TuKYNNOv0abuvUanddWGSy3+YHlMKUSgcVFo0d1DorxPAhJSTCrugl+4OmMQ==",
"version": "3.0.16",
"resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-3.0.16.tgz",
"integrity": "sha512-nlC4D5xB7pomgR4kDZ1lqbVqrs6gxPqsM2OE5CkCn0EqCMxtqqtadtbK2dcFwzyujv3DL4wYNo+fgF4rJgLPZA==",
"funding": [
{
"type": "github",
@@ -2240,9 +2240,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-color-parser": "^2.0.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/css-color-parser": "^2.0.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
@@ -2329,9 +2329,9 @@
}
},
"node_modules/@csstools/postcss-light-dark-function": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-1.0.5.tgz",
"integrity": "sha512-kKM9dtEaVmSTb3scL2pgef62KyWv6SK19JiAnCCuiDhlRE6PADKzaPPBXmP3qj4IEgIH+cQhdEosB0eroU6Fnw==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-1.0.6.tgz",
"integrity": "sha512-bu+cxKpcTrMDMkVCv7QURwKNPZEuXA3J0Udvz3HfmQHt4+OIvvfvDpTgejFXdOliCU4zK9/QdqebPcYneygZtg==",
"funding": [
{
"type": "github",
@@ -2344,8 +2344,8 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
@@ -2448,9 +2448,9 @@
}
},
"node_modules/@csstools/postcss-logical-viewport-units": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-2.0.9.tgz",
"integrity": "sha512-iBBJuExgHwedFH9AqNOHWzZFgYnt17zhu1qWjmSihu1P5pw0lIG9q5t3uIgJJFDNmYoOGfBKan66z9u1QH8yBQ==",
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-2.0.10.tgz",
"integrity": "sha512-nGP0KanI/jXrUMpaIBz6mdy/vNs3d/cjbNYuoEc7lCdNkntmxZvwxC2zIKI8QzGWaYsh9jahozMVceZ0jNyjgg==",
"funding": [
{
"type": "github",
@@ -2463,7 +2463,7 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/utilities": "^1.0.0"
},
"engines": {
@@ -2474,9 +2474,9 @@
}
},
"node_modules/@csstools/postcss-media-minmax": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-1.1.6.tgz",
"integrity": "sha512-bc0frf2Lod53j6wEHVsaVElfvCf6uhc96v99M/wUfer4MmNYfO3YLx1kFuB8xXvb0AXiWx4fohCJqemHV3bfRg==",
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-1.1.7.tgz",
"integrity": "sha512-AjLG+vJvhrN2geUjYNvzncW1TJ+vC4QrVPGrLPxOSJ2QXC94krQErSW4aXMj0b13zhvVWeqf2NHIOVQknqV9cg==",
"funding": [
{
"type": "github",
@@ -2489,10 +2489,10 @@
],
"license": "MIT",
"dependencies": {
"@csstools/css-calc": "^1.2.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/media-query-list-parser": "^2.1.11"
"@csstools/css-calc": "^1.2.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/media-query-list-parser": "^2.1.12"
},
"engines": {
"node": "^14 || ^16 || >=18"
@@ -2502,9 +2502,9 @@
}
},
"node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-2.0.9.tgz",
"integrity": "sha512-PR0s3tFSxPoKoPLoKuiZuYhwQC5bQxq/gFfywX2u/kh8rMzesARPZYKxE71I3jHWi6KDHGZl9Xb5xcFPwtvLiQ==",
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-2.0.10.tgz",
"integrity": "sha512-DXae3i7OYJTejxcoUuf/AOIpy+6FWfGGKo/I3WefZI538l3k+ErU6V2xQOx/UmUXT2FDIdE1Ucl9JkZib2rEsA==",
"funding": [
{
"type": "github",
@@ -2517,9 +2517,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/media-query-list-parser": "^2.1.11"
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/media-query-list-parser": "^2.1.12"
},
"engines": {
"node": "^14 || ^16 || >=18"
@@ -2580,9 +2580,9 @@
}
},
"node_modules/@csstools/postcss-oklab-function": {
"version": "3.0.16",
"resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-3.0.16.tgz",
"integrity": "sha512-zm8nND+EraZrmbO4mgcT8FrJrAQUfWNfMmbV5uTCpWtAcO5ycX3E3bO8T1TjczKYRxC5QMM/91n9YExYCF4Mvw==",
"version": "3.0.17",
"resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-3.0.17.tgz",
"integrity": "sha512-kIng3Xmw6NKUvD/eEoHGwbyDFXDsuzsVGtNo3ndgZYYqy+DLiD+3drxwRKiViE5LUieLB1ERczXpLVmpSw61eg==",
"funding": [
{
"type": "github",
@@ -2595,9 +2595,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-color-parser": "^2.0.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/css-color-parser": "^2.0.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
@@ -2634,9 +2634,9 @@
}
},
"node_modules/@csstools/postcss-relative-color-syntax": {
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-2.0.16.tgz",
"integrity": "sha512-TSM8fVqJkT8JZDranZPnkpxjU/Q1sNR192lXMND+EcKOUjYa6uYpGSfHgjnWjCRiBSciettS+sL7y9wmnas7qQ==",
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-2.0.17.tgz",
"integrity": "sha512-EVckAtG8bocItZflXLJ50Su+gwg/4Jhkz1BztyNsT0/svwS6QMAeLjyUA75OsgtejNWQHvBMWna4xc9LCqdjrQ==",
"funding": [
{
"type": "github",
@@ -2649,9 +2649,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-color-parser": "^2.0.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/css-color-parser": "^2.0.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
@@ -2688,9 +2688,9 @@
}
},
"node_modules/@csstools/postcss-stepped-value-functions": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-3.0.8.tgz",
"integrity": "sha512-X76+thsvsmH/SkqVbN+vjeFKe1ABGLRx8/Wl68QTb/zvJWdzgx5S/nbszZP5O3nTRc5eI8NxIOrQUiy30fR+0g==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-3.0.9.tgz",
"integrity": "sha512-uAw1J8hiZ0mM1DLaziI7CP5oagSwDnS5kufuROGIJFzESYfTqNVS3b7FgDZto9AxXdkwI+Sn48+cvG8PwzGMog==",
"funding": [
{
"type": "github",
@@ -2703,9 +2703,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-calc": "^1.2.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1"
"@csstools/css-calc": "^1.2.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2"
},
"engines": {
"node": "^14 || ^16 || >=18"
@@ -2715,9 +2715,9 @@
}
},
"node_modules/@csstools/postcss-text-decoration-shorthand": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-3.0.6.tgz",
"integrity": "sha512-Q8HEu4AEiwNVZBD6+DpQ8M9SajpMow4+WtmndWIAv8qxDtDYL4JK1xXWkhOGk28PrcJawOvkrEZ8Ri59UN1TJw==",
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-3.0.7.tgz",
"integrity": "sha512-+cptcsM5r45jntU6VjotnkC9GteFR7BQBfZ5oW7inLCxj7AfLGAzMbZ60hKTP13AULVZBdxky0P8um0IBfLHVA==",
"funding": [
{
"type": "github",
@@ -2730,7 +2730,7 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/color-helpers": "^4.2.0",
"@csstools/color-helpers": "^4.2.1",
"postcss-value-parser": "^4.2.0"
},
"engines": {
@@ -2741,9 +2741,9 @@
}
},
"node_modules/@csstools/postcss-trigonometric-functions": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-3.0.8.tgz",
"integrity": "sha512-zEzyGriPqoIYFgHJqWNy8bmoxjM4+ONyTap1ZzQK/Lll/VsCYvx0IckB33W/u89uLSVeeB8xC7uTrkoQ7ogKyQ==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-3.0.9.tgz",
"integrity": "sha512-rCAtKX3EsH91ZIHoxFzAAcMQeQCS+PsjzHl6fvsGXz/SV3lqzSmO7MWgFXyPktC2zjZXgOObAJ/2QkhMqVpgNg==",
"funding": [
{
"type": "github",
@@ -2756,9 +2756,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-calc": "^1.2.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1"
"@csstools/css-calc": "^1.2.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2"
},
"engines": {
"node": "^14 || ^16 || >=18"
@@ -2880,9 +2880,9 @@
}
},
"node_modules/@eslint-community/regexpp": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz",
"integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==",
"version": "4.11.0",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
"integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
"license": "MIT",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
@@ -3619,42 +3619,42 @@
"license": "ISC"
},
"node_modules/@vue/compiler-core": {
"version": "3.4.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.30.tgz",
"integrity": "sha512-ZL8y4Xxdh8O6PSwfdZ1IpQ24PjTAieOz3jXb/MDTfDtANcKBMxg1KLm6OX2jofsaQGYfIVzd3BAG22i56/cF1w==",
"version": "3.4.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.31.tgz",
"integrity": "sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@babel/parser": "^7.24.7",
"@vue/shared": "3.4.30",
"@vue/shared": "3.4.31",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.4.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.30.tgz",
"integrity": "sha512-+16Sd8lYr5j/owCbr9dowcNfrHd+pz+w2/b5Lt26Oz/kB90C9yNbxQ3bYOvt7rI2bxk0nqda39hVcwDFw85c2Q==",
"version": "3.4.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.31.tgz",
"integrity": "sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"@vue/compiler-core": "3.4.30",
"@vue/shared": "3.4.30"
"@vue/compiler-core": "3.4.31",
"@vue/shared": "3.4.31"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.4.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.30.tgz",
"integrity": "sha512-8vElKklHn/UY8+FgUFlQrYAPbtiSB2zcgeRKW7HkpSRn/JjMRmZvuOtwDx036D1aqKNSTtXkWRfqx53Qb+HmMg==",
"version": "3.4.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.31.tgz",
"integrity": "sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"@babel/parser": "^7.24.7",
"@vue/compiler-core": "3.4.30",
"@vue/compiler-dom": "3.4.30",
"@vue/compiler-ssr": "3.4.30",
"@vue/shared": "3.4.30",
"@vue/compiler-core": "3.4.31",
"@vue/compiler-dom": "3.4.31",
"@vue/compiler-ssr": "3.4.31",
"@vue/shared": "3.4.31",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.10",
"postcss": "^8.4.38",
@@ -3662,14 +3662,14 @@
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.4.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.30.tgz",
"integrity": "sha512-ZJ56YZGXJDd6jky4mmM0rNaNP6kIbQu9LTKZDhcpddGe/3QIalB1WHHmZ6iZfFNyj5mSypTa4+qDJa5VIuxMSg==",
"version": "3.4.31",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.31.tgz",
"integrity": "sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==",
"license": "MIT",
"optional": true,
"dependencies": {
"@vue/compiler-dom": "3.4.30",
"@vue/shared": "3.4.30"
"@vue/compiler-dom": "3.4.31",
"@vue/shared": "3.4.31"
}
},
"node_modules/@vue/component-compiler-utils": {
@@ -3747,9 +3747,9 @@
"license": "ISC"
},
"node_modules/@vue/shared": {
"version": "3.4.30",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.30.tgz",
"integrity": "sha512-CLg+f8RQCHQnKvuHY9adMsMaQOcqclh6Z5V9TaoMgy0ut0tz848joZ7/CYFFyF/yZ5i2yaw7Fn498C+CNZVHIg==",
"version": "3.4.31",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.31.tgz",
"integrity": "sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==",
"license": "MIT",
"optional": true
},
@@ -5017,9 +5017,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001638",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001638.tgz",
"integrity": "sha512-5SuJUJ7cZnhPpeLHaH0c/HPAnAHZvS6ElWyHK9GSIbVOQABLzowiI2pjmpvZ1WEbkyz46iFd4UXlOHR5SqgfMQ==",
"version": "1.0.30001639",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001639.tgz",
"integrity": "sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==",
"funding": [
{
"type": "opencollective",
@@ -6439,9 +6439,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.812",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.812.tgz",
"integrity": "sha512-7L8fC2Ey/b6SePDFKR2zHAy4mbdp1/38Yk5TsARO66W3hC5KEaeKMMHoxwtuH+jcu2AYLSn9QX04i95t6Fl1Hg==",
"version": "1.4.815",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.815.tgz",
"integrity": "sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==",
"license": "ISC"
},
"node_modules/emoji-regex": {
@@ -6953,9 +6953,9 @@
}
},
"node_modules/eslint-plugin-es-x": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.7.0.tgz",
"integrity": "sha512-aP3qj8BwiEDPttxQkZdI221DLKq9sI/qHolE2YSQL1/9+xk7dTV+tB1Fz8/IaCA+lnLA1bDEnvaS2LKs0k2Uig==",
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz",
"integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==",
"funding": [
"https://github.com/sponsors/ota-meshi",
"https://opencollective.com/eslint"
@@ -6964,7 +6964,7 @@
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.1.2",
"@eslint-community/regexpp": "^4.6.0",
"@eslint-community/regexpp": "^4.11.0",
"eslint-compat-utils": "^0.5.1"
},
"engines": {
@@ -7166,9 +7166,9 @@
}
},
"node_modules/eslint-plugin-promise": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.2.0.tgz",
"integrity": "sha512-QmAqwizauvnKOlifxyDj2ObfULpHQawlg/zQdgEixur9vl0CvZGv/LCJV2rtj3210QCoeGBzVMfMXqGAOr/4fA==",
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.4.0.tgz",
"integrity": "sha512-/KWWRaD3fGkVCZsdR0RU53PSthFmoHVhZl+y9+6DqeDLSikLdlUVpVEAmI6iCRR5QyOjBYBqHZV/bdv4DJ4Gtw==",
"license": "ISC",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -11486,9 +11486,9 @@
}
},
"node_modules/path-scurry/node_modules/lru-cache": {
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
"integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz",
"integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==",
"license": "ISC",
"engines": {
"node": "14 || >=16.14"
@@ -11656,9 +11656,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"version": "8.4.39",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz",
"integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==",
"funding": [
{
"type": "opencollective",
@@ -11676,7 +11676,7 @@
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"picocolors": "^1.0.1",
"source-map-js": "^1.2.0"
},
"engines": {
@@ -11740,9 +11740,9 @@
}
},
"node_modules/postcss-color-functional-notation": {
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-6.0.11.tgz",
"integrity": "sha512-gJ+hAtAsgBF4w7eh28Pg7EA60lx7vE5xO/B/yZawaI6FYHky+5avA9YSe73nJHnAMEVFpCMeJc6Wts5g+niksg==",
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-6.0.12.tgz",
"integrity": "sha512-LGLWl6EDofJwDHMElYvt4YU9AeH+oijzOfeKhE0ebuu0aBSDeEg7CfFXMi0iiXWV1VKxn3MLGOtcBNnOiQS9Yg==",
"funding": [
{
"type": "github",
@@ -11755,9 +11755,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-color-parser": "^2.0.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/css-color-parser": "^2.0.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
@@ -11855,9 +11855,9 @@
}
},
"node_modules/postcss-custom-media": {
"version": "10.0.6",
"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-10.0.6.tgz",
"integrity": "sha512-BjihQoIO4Wjqv9fQNExSJIim8UAmkhLxuJnhJsLTRFSba1y1MhxkJK5awsM//6JJ+/Tu5QUxf624RQAvKHv6SA==",
"version": "10.0.7",
"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-10.0.7.tgz",
"integrity": "sha512-o2k5nnvRZhF36pr1fGFM7a1EMTcNdKNO70Tp1g2lfpYgiwIctR7ic4acBCDHBMYRcQ8mFlaBB1QsEywqrSIaFQ==",
"funding": [
{
"type": "github",
@@ -11870,10 +11870,10 @@
],
"license": "MIT",
"dependencies": {
"@csstools/cascade-layer-name-parser": "^1.0.11",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/media-query-list-parser": "^2.1.11"
"@csstools/cascade-layer-name-parser": "^1.0.12",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/media-query-list-parser": "^2.1.12"
},
"engines": {
"node": "^14 || ^16 || >=18"
@@ -11883,9 +11883,9 @@
}
},
"node_modules/postcss-custom-properties": {
"version": "13.3.10",
"resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-13.3.10.tgz",
"integrity": "sha512-ejaalIpl7p0k0L5ngIZ86AZGmp3m1KdeOCbSQTK4gQcB1ncaoPTHorw206+tsZRIhIDYvh5ZButEje6740YDXw==",
"version": "13.3.11",
"resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-13.3.11.tgz",
"integrity": "sha512-CAIgz03I/GMhVbAKIi3u3P8j5JY2KHl0TlePcfUX3OUy8t0ynnWvyJaS1D92pEAw1LjmeKWi7+aIU0s53iYdOQ==",
"funding": [
{
"type": "github",
@@ -11898,9 +11898,9 @@
],
"license": "MIT",
"dependencies": {
"@csstools/cascade-layer-name-parser": "^1.0.11",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/cascade-layer-name-parser": "^1.0.12",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/utilities": "^1.0.0",
"postcss-value-parser": "^4.2.0"
},
@@ -11912,9 +11912,9 @@
}
},
"node_modules/postcss-custom-selectors": {
"version": "7.1.10",
"resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-7.1.10.tgz",
"integrity": "sha512-bV/6+IExyT2J4kMzX6c+ZMlN1xDfjcC4ePr1ywKezcTgwgUn11qQN3jdzFBpo8Dk1K7vO/OYOwMb5AtJP4JZcg==",
"version": "7.1.11",
"resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-7.1.11.tgz",
"integrity": "sha512-IoGprXOueDJL5t3ZuWR+QzPpmrQCFNhvoICsg0vDSehGwWNG0YV/Z4A+zouGRonC7NJThoV+A8A74IEMqMQUQw==",
"funding": [
{
"type": "github",
@@ -11927,10 +11927,10 @@
],
"license": "MIT",
"dependencies": {
"@csstools/cascade-layer-name-parser": "^1.0.11",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"postcss-selector-parser": "^6.0.13"
"@csstools/cascade-layer-name-parser": "^1.0.12",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"postcss-selector-parser": "^6.1.0"
},
"engines": {
"node": "^14 || ^16 || >=18"
@@ -12167,9 +12167,9 @@
}
},
"node_modules/postcss-lab-function": {
"version": "6.0.16",
"resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-6.0.16.tgz",
"integrity": "sha512-QWv0VxfjgIl8jBR/wuQcm/o31jn4P/LwzYuVKzNQoO5t7HPcU0d3RfWUiDrHN3frmSv+YYZppr3P81tKFTDyqg==",
"version": "6.0.17",
"resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-6.0.17.tgz",
"integrity": "sha512-QzjC6/3J6XKZzHGuUKhWNvlDMfWo+08dQOfQj4vWQdpZFdOxCh9QCR4w4XbV68EkdzywJie1mcm81jwFyV0+kg==",
"funding": [
{
"type": "github",
@@ -12182,9 +12182,9 @@
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-color-parser": "^2.0.2",
"@csstools/css-parser-algorithms": "^2.6.3",
"@csstools/css-tokenizer": "^2.3.1",
"@csstools/css-color-parser": "^2.0.3",
"@csstools/css-parser-algorithms": "^2.7.0",
"@csstools/css-tokenizer": "^2.3.2",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/utilities": "^1.0.0"
},
@@ -12679,9 +12679,9 @@
}
},
"node_modules/postcss-preset-env": {
"version": "9.5.14",
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.5.14.tgz",
"integrity": "sha512-gTMi+3kENN/mN+K59aR+vEOjlkujTmmXJcM9rnAqGh9Y/euQ/ypdp9rd8mO1eoIjAD8vNS15+xbkBxoi+65BqQ==",
"version": "9.5.15",
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-9.5.15.tgz",
"integrity": "sha512-z/2akOVQChOGAdzaUR4pQrDOM3xGZc5/k4THHWyREbWAfngaJATA2SkEQMkiyV5Y/EoSwE0nt0IiaIs6CMmxfQ==",
"funding": [
{
"type": "github",
@@ -12695,48 +12695,48 @@
"license": "MIT-0",
"dependencies": {
"@csstools/postcss-cascade-layers": "^4.0.6",
"@csstools/postcss-color-function": "^3.0.16",
"@csstools/postcss-color-mix-function": "^2.0.16",
"@csstools/postcss-exponential-functions": "^1.0.7",
"@csstools/postcss-color-function": "^3.0.17",
"@csstools/postcss-color-mix-function": "^2.0.17",
"@csstools/postcss-exponential-functions": "^1.0.8",
"@csstools/postcss-font-format-keywords": "^3.0.2",
"@csstools/postcss-gamut-mapping": "^1.0.9",
"@csstools/postcss-gradients-interpolation-method": "^4.0.17",
"@csstools/postcss-hwb-function": "^3.0.15",
"@csstools/postcss-gamut-mapping": "^1.0.10",
"@csstools/postcss-gradients-interpolation-method": "^4.0.18",
"@csstools/postcss-hwb-function": "^3.0.16",
"@csstools/postcss-ic-unit": "^3.0.6",
"@csstools/postcss-initial": "^1.0.1",
"@csstools/postcss-is-pseudo-class": "^4.0.8",
"@csstools/postcss-light-dark-function": "^1.0.5",
"@csstools/postcss-light-dark-function": "^1.0.6",
"@csstools/postcss-logical-float-and-clear": "^2.0.1",
"@csstools/postcss-logical-overflow": "^1.0.1",
"@csstools/postcss-logical-overscroll-behavior": "^1.0.1",
"@csstools/postcss-logical-resize": "^2.0.1",
"@csstools/postcss-logical-viewport-units": "^2.0.9",
"@csstools/postcss-media-minmax": "^1.1.6",
"@csstools/postcss-media-queries-aspect-ratio-number-values": "^2.0.9",
"@csstools/postcss-logical-viewport-units": "^2.0.10",
"@csstools/postcss-media-minmax": "^1.1.7",
"@csstools/postcss-media-queries-aspect-ratio-number-values": "^2.0.10",
"@csstools/postcss-nested-calc": "^3.0.2",
"@csstools/postcss-normalize-display-values": "^3.0.2",
"@csstools/postcss-oklab-function": "^3.0.16",
"@csstools/postcss-oklab-function": "^3.0.17",
"@csstools/postcss-progressive-custom-properties": "^3.2.0",
"@csstools/postcss-relative-color-syntax": "^2.0.16",
"@csstools/postcss-relative-color-syntax": "^2.0.17",
"@csstools/postcss-scope-pseudo-class": "^3.0.1",
"@csstools/postcss-stepped-value-functions": "^3.0.8",
"@csstools/postcss-text-decoration-shorthand": "^3.0.6",
"@csstools/postcss-trigonometric-functions": "^3.0.8",
"@csstools/postcss-stepped-value-functions": "^3.0.9",
"@csstools/postcss-text-decoration-shorthand": "^3.0.7",
"@csstools/postcss-trigonometric-functions": "^3.0.9",
"@csstools/postcss-unset-value": "^3.0.1",
"autoprefixer": "^10.4.19",
"browserslist": "^4.22.3",
"browserslist": "^4.23.1",
"css-blank-pseudo": "^6.0.2",
"css-has-pseudo": "^6.0.5",
"css-prefers-color-scheme": "^9.0.1",
"cssdb": "^8.0.0",
"postcss-attribute-case-insensitive": "^6.0.3",
"postcss-clamp": "^4.1.0",
"postcss-color-functional-notation": "^6.0.11",
"postcss-color-functional-notation": "^6.0.12",
"postcss-color-hex-alpha": "^9.0.4",
"postcss-color-rebeccapurple": "^9.0.3",
"postcss-custom-media": "^10.0.6",
"postcss-custom-properties": "^13.3.10",
"postcss-custom-selectors": "^7.1.10",
"postcss-custom-media": "^10.0.7",
"postcss-custom-properties": "^13.3.11",
"postcss-custom-selectors": "^7.1.11",
"postcss-dir-pseudo-class": "^8.0.1",
"postcss-double-position-gradients": "^5.0.6",
"postcss-focus-visible": "^9.0.1",
@@ -12744,7 +12744,7 @@
"postcss-font-variant": "^5.0.0",
"postcss-gap-properties": "^5.0.1",
"postcss-image-set-function": "^6.0.3",
"postcss-lab-function": "^6.0.16",
"postcss-lab-function": "^6.0.17",
"postcss-logical": "^7.0.1",
"postcss-nesting": "^12.1.5",
"postcss-opacity-percentage": "^2.0.0",
@@ -14447,9 +14447,9 @@
}
},
"node_modules/string-width": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz",
"integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==",
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^10.3.0",
@@ -16607,9 +16607,9 @@
}
},
"node_modules/yocto-queue": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",
"integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz",
"integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==",
"license": "MIT",
"engines": {
"node": ">=12.20"

View File

@@ -425,7 +425,7 @@ export default {
return;
}
if (this.confirmAction === "") {
if (this.confirmAction === "" && this.$session.provider !== "oidc") {
this.confirmAction = "onGenerate";
return;
}

View File

@@ -209,7 +209,7 @@ export class User extends RestModel {
}
isRemote() {
return this.AuthProvider && this.AuthProvider === "ldap";
return this.AuthProvider && (this.AuthProvider === "ldap" || this.AuthProvider === "oidc");
}
authInfo() {

View File

@@ -80,3 +80,21 @@ export const ScopeOptions = () => {
*/
];
};
// GrantTypes maps grant types to their display name.
export const GrantTypes = () => {
return {
"": "Default",
cli: "CLI",
implicit: "Implicit",
session: $gettext("Session"),
password: $gettext("Password"),
client_credentials: "Client Credentials",
share_token: "Share Token",
refresh_token: "Refresh Token",
authorization_code: "Authorization Code",
"urn:ietf:params:oauth:grant-type:jwt-bearer": "JWT Bearer Assertion",
"urn:ietf:params:oauth:grant-type:saml2-bearer": "SAML2 Bearer Assertion",
"urn:ietf:params:oauth:grant-type:token-exchange": "Token Exchange",
};
};

View File

@@ -173,6 +173,11 @@ export default {
},
created() {
this.$scrollbar.hide(this.$isMobile);
const authError = window.localStorage.getItem("authError");
if (authError) {
this.$notify.error(authError);
window.localStorage.removeItem("authError");
}
},
destroyed() {
this.$scrollbar.show();

1
go.mod
View File

@@ -98,7 +98,6 @@ require (
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidbyttow/govips v0.0.0-20201026223743-b1b72c7305d9 // indirect
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect

6
go.sum
View File

@@ -58,8 +58,6 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidbyttow/govips v0.0.0-20201026223743-b1b72c7305d9 h1:nXNjG6pGALu++DSxtrdO79AZhtU+jZnyMlq3FTkFH74=
github.com/davidbyttow/govips v0.0.0-20201026223743-b1b72c7305d9/go.mod h1:HLCb4NOCQTqIYx+Mu01rZChNoP/vDwQNVFaKWUTzuxk=
github.com/davidbyttow/govips/v2 v2.14.0 h1:il3pX0XMZ5nlwipkFJHRZ3vGzcdXWApARalJxNpRHJU=
github.com/davidbyttow/govips/v2 v2.14.0/go.mod h1:eglyvgm65eImDiJJk4wpj9LSz4pWivPzWgDqkxWJn5k=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
@@ -471,7 +469,6 @@ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201024042810-be3efd7ff127/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
@@ -520,7 +517,6 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -641,7 +637,6 @@ google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6h
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -653,7 +648,6 @@ gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -7,7 +7,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -3,7 +3,7 @@ package api
import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/pkg/authn"

View File

@@ -8,7 +8,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/session"
"github.com/photoprism/photoprism/pkg/header"
)

View File

@@ -10,7 +10,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -8,7 +8,7 @@ import (
"github.com/gin-gonic/gin"
"gopkg.in/yaml.v2"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/mutex"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/customize"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/mutex"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/i18n"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/search"
"github.com/photoprism/photoprism/pkg/txt"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/pkg/i18n"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/photoprism"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/photoprism"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/clean"
)

View File

@@ -9,7 +9,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -11,7 +11,7 @@ import (
"github.com/dustin/go-humanize/english"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -8,7 +8,7 @@ import (
"github.com/dustin/go-humanize/english"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/search"
"github.com/photoprism/photoprism/pkg/txt"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/query"

View File

@@ -9,7 +9,7 @@ import (
"github.com/dustin/go-humanize/english"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/crop"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"

View File

@@ -11,7 +11,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/common/expfmt"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/get"
)

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/txt"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -7,6 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/server/limiter"
"github.com/photoprism/photoprism/pkg/authn"
"github.com/photoprism/photoprism/pkg/header"
"github.com/photoprism/photoprism/pkg/i18n"
@@ -28,7 +29,7 @@ func OIDCLogin(router *gin.RouterGroup) {
// Get client IP address for logs and rate limiting checks.
clientIp := ClientIP(c)
actor := "unknown client"
actor := "unknown user"
action := "login"
// Get global config.
@@ -45,6 +46,16 @@ func OIDCLogin(router *gin.RouterGroup) {
return
}
// Check request rate limit.
var r *limiter.Request
r = limiter.Login.Request(clientIp)
// Abort if failure rate limit is exceeded.
if r.Reject() || limiter.Auth.Reject(clientIp) {
limiter.AbortJSON(c)
return
}
// Get OIDC provider.
provider := get.OIDC()
@@ -54,6 +65,9 @@ func OIDCLogin(router *gin.RouterGroup) {
return
}
// Return the reserved request rate limit token.
r.Success()
// Handle OIDC login request.
provider.AuthCodeUrlHandler(c)
})

View File

@@ -5,11 +5,16 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/server/limiter"
"github.com/photoprism/photoprism/pkg/authn"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/header"
"github.com/photoprism/photoprism/pkg/i18n"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/photoprism/photoprism/pkg/unix"
)
// OIDCRedirect creates a new access token for authenticated users and then redirects the browser back to the app.
@@ -17,9 +22,12 @@ import (
// GET /api/v1/oidc/redirect
func OIDCRedirect(router *gin.RouterGroup) {
router.GET("/oidc/redirect", func(c *gin.Context) {
// Get global config.
conf := get.Config()
// Prevent CDNs from caching this endpoint.
if header.IsCdn(c.Request) {
AbortNotFound(c)
c.Redirect(http.StatusTemporaryRedirect, conf.LoginUri())
return
}
@@ -28,20 +36,34 @@ func OIDCRedirect(router *gin.RouterGroup) {
// Get client IP address for logs and rate limiting checks.
clientIp := ClientIP(c)
actor := "unknown client"
actor := "unknown user"
action := "redirect"
// Get global config.
conf := get.Config()
// Abort in public mode and if OIDC is disabled.
if get.Config().Public() {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrDisabledInPublicMode.Error()})
Abort(c, http.StatusForbidden, i18n.ErrForbidden)
c.Redirect(http.StatusTemporaryRedirect, conf.LoginUri())
return
} else if !conf.OIDCEnabled() {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrAuthenticationDisabled.Error()})
Abort(c, http.StatusMethodNotAllowed, i18n.ErrUnsupported)
c.Redirect(http.StatusTemporaryRedirect, conf.LoginUri())
return
}
// Check request rate limit.
var r *limiter.Request
r = limiter.Login.Request(clientIp)
// Abort if failure rate limit is exceeded.
if r.Reject() || limiter.Auth.Reject(clientIp) {
c.HTML(http.StatusTooManyRequests, "auth.gohtml", CreateSessionError(http.StatusTooManyRequests, i18n.Error(i18n.ErrForbidden)))
return
}
// Check if the required request parameters are present.
if c.Query("state") == "" || c.Query("code") == "" {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrAuthCodeRequired.Error()})
c.Redirect(http.StatusTemporaryRedirect, conf.LoginUri())
return
}
@@ -50,32 +72,156 @@ func OIDCRedirect(router *gin.RouterGroup) {
if provider == nil {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrAuthenticationDisabled.Error()})
Abort(c, http.StatusInternalServerError, i18n.ErrConnectionFailed)
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
}
_, claimErr := provider.CodeExchangeUserInfo(c)
userInfo, tokens, claimErr := provider.CodeExchangeUserInfo(c)
if claimErr != nil {
event.AuditErr([]string{clientIp, "oidc", actor, action, claimErr.Error()})
Abort(c, http.StatusForbidden, i18n.ErrForbidden)
return
}
// TODO 1: Create user account if it does not exist yet.
/*
user := &entity.User{
DisplayName: userInfo.GetName(),
UserName: oidc.UsernameFromUserInfo(userInfo),
UserEmail: userInfo.GetEmail(),
AuthID: userInfo.GetSubject(),
AuthProvider: authn.ProviderOIDC.String(),
} */
// Step 1: Create user account if it does not exist yet.
var user *entity.User
var err error
// TODO 2: Create and return user session.
// Find existing user record and update it, if necessary.
if oidcUser := entity.OidcUser(userInfo, conf.OIDCUsername()); oidcUser.UserName == "" || authn.ProviderOIDC.NotEqual(oidcUser.AuthProvider) {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrInvalidUsername.Error()})
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
} else if user = entity.FindUser(oidcUser); user != nil {
// Check if username and subject UID match.
if user.Username() == "" || oidcUser.UserName == "" || user.Username() != oidcUser.UserName {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrInvalidUsername.Error()})
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
} else if user.AuthID == "" || oidcUser.AuthID == "" || user.AuthID != oidcUser.AuthID {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrInvalidAuthID.Error()})
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
}
// TODO 3: Render HTML template to set the access token in localStorage.
actor = user.Username()
c.JSON(http.StatusMethodNotAllowed, gin.H{"status": StatusFailed})
// Update user profile information.
user.SetDisplayName(userInfo.GetName(), entity.SrcOIDC)
user.SetGivenName(userInfo.GetGivenName())
user.SetFamilyName(userInfo.GetFamilyName())
// Update user account.
if err = user.Save(); err != nil {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrAccountUpdateFailed.Error(), err.Error()})
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
}
} else if conf.OIDCRegister() {
// Create new user record.
user = &oidcUser
actor = user.Username()
// Set profile information.
user.SetDisplayName(userInfo.GetName(), entity.SrcOIDC)
user.SetGivenName(userInfo.GetGivenName())
user.SetFamilyName(userInfo.GetFamilyName())
user.Details().NickName = clean.Name(userInfo.GetNickname())
user.Details().ProfileURL = clean.Uri(userInfo.GetProfile())
user.Details().SiteURL = clean.Uri(userInfo.GetWebsite())
user.Details().UserGender = clean.Name(string(userInfo.GetGender()))
// Set UI locale.
if locale, _, _ := userInfo.GetLocale().Raw(); len(locale.String()) == 2 {
user.Settings().UILanguage = locale.String()
}
// Set UI timezone.
user.Settings().UITimeZone = userInfo.GetZoneinfo()
// Set address information, if available.
if addr := userInfo.GetAddress(); addr != nil {
user.Details().UserLocation = clean.Name(addr.GetLocality())
user.Details().UserCountry = clean.TypeLowerUnderscore(addr.GetCountry())
}
// Set birthday, if available.
if birthDate := txt.ParseTime(userInfo.GetBirthdate(), userInfo.GetZoneinfo()); !birthDate.IsZero() {
user.BornAt = &birthDate
user.Details().BirthDay = birthDate.Day()
user.Details().BirthMonth = int(birthDate.Month())
user.Details().BirthYear = birthDate.Year()
}
// Flag as verified?
if userInfo.IsEmailVerified() {
user.UserEmail = clean.Email(userInfo.GetEmail())
user.VerifiedAt = entity.TimeStamp()
}
// Set role and permissions.
user.SetRole(conf.OIDCRole().String())
user.CanLogin = true
user.WebDAV = conf.OIDCWebDAV()
// Create user account.
if err = user.Create(); err != nil {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrAccountCreateFailed.Error(), err.Error()})
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
}
} else {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrRegistrationDisabled.Error()})
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
}
// Login allowed?
if !user.CanLogIn() {
event.AuditErr([]string{clientIp, "oidc", actor, action, authn.ErrAccountDisabled.Error()})
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
}
// Step 2: Create user session.
sess := get.Session().New(c)
sess.SetProvider(authn.ProviderOIDC)
sess.SetMethod(authn.MethodDefault)
sess.SetUser(user)
sess.SetGrantType(authn.GrantAuthorizationCode)
// Set identity provider tokens.
sess.IdToken = tokens.IDToken
sess.AccessToken = tokens.AccessToken
sess.RefreshToken = tokens.RefreshToken
// Set session expiration and timeout.
sess.SetExpiresIn(unix.Day)
sess.SetTimeout(-1)
// Save session after successful authentication.
if sess, err = get.Session().Save(sess); err != nil {
event.AuditErr([]string{clientIp, "oidc", actor, action, "%s"}, err)
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrInvalidCredentials)))
return
} else if sess == nil {
c.HTML(http.StatusUnauthorized, "auth.gohtml", CreateSessionError(http.StatusUnauthorized, i18n.Error(i18n.ErrUnexpected)))
return
}
// Return the reserved request rate limit token after successful authentication.
r.Success()
// Response includes user data, session data, and client config values.
response := CreateSessionResponse(sess.AuthToken(), sess, conf.ClientSession(sess))
// Log success.
event.AuditInfo([]string{clientIp, "oidc", actor, action, authn.Succeeded})
// Update login timestamp.
user.UpdateLoginTime()
// Step 3: Render HTML template to set the access token in localStorage.
c.HTML(http.StatusOK, "auth.gohtml", response)
})
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/classify"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"

View File

@@ -7,7 +7,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/pkg/clean"

View File

@@ -7,7 +7,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/get"
)

View File

@@ -7,7 +7,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -7,7 +7,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/header"

View File

@@ -5,6 +5,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/get"
)
// CreateSessionResponse returns the authentication response data for POST requests
@@ -13,6 +14,16 @@ func CreateSessionResponse(authToken string, sess *entity.Session, conf config.C
return GetSessionResponse(authToken, sess, conf)
}
// CreateSessionError returns an authentication error response.
func CreateSessionError(code int, err error) gin.H {
return gin.H{
"status": StatusFailed,
"code": code,
"error": err.Error(),
"config": get.Config().ClientPublic(),
}
}
// GetSessionResponse returns the authentication response data for GET requests
// based on the session and configuration.
func GetSessionResponse(authToken string, sess *entity.Session, conf config.ClientConfig) gin.H {

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/search"
"github.com/photoprism/photoprism/pkg/txt"

View File

@@ -7,7 +7,7 @@ import (
"github.com/gabriel-vasile/mimetype"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -6,7 +6,7 @@ import (
"github.com/dustin/go-humanize/english"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -6,7 +6,7 @@ import (
"github.com/dustin/go-humanize/english"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -6,7 +6,7 @@ import (
"github.com/dustin/go-humanize/english"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -11,7 +11,7 @@ import (
"github.com/dustin/go-humanize/english"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"

View File

@@ -7,7 +7,7 @@ import (
"github.com/gorilla/websocket"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
)

View File

@@ -8,7 +8,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/event"
)

View File

@@ -12,7 +12,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/photoprism"

View File

@@ -43,7 +43,7 @@ func NewClient(iss *url.URL, clientId, clientSecret, customScopes, siteUrl strin
return nil, err
}
u.Path = path.Join(u.Path, config.OIDCRedirectUri)
u.Path = path.Join(u.Path, config.OidcRedirectUri)
log.Debugf("oidc: redirect uri %s", u.String())
var hashKey, encryptKey []byte
@@ -113,12 +113,10 @@ func (c *Client) AuthCodeUrlHandler(ctx *gin.Context) {
handle(ctx.Writer, ctx.Request)
}
func (c *Client) CodeExchangeUserInfo(ctx *gin.Context) (oidc.UserInfo, error) {
var userinfo oidc.UserInfo
userinfoClosure := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens, state string, rp rp.RelyingParty, info oidc.UserInfo) {
log.Debugf("oidc: UserInfo: %s %s %s %s %s", info.GetEmail(), info.GetSubject(), info.GetNickname(), info.GetName(), info.GetPreferredUsername())
userinfo = info
func (c *Client) CodeExchangeUserInfo(ctx *gin.Context) (userInfo oidc.UserInfo, tokens *oidc.Tokens, err error) {
userinfoClosure := func(w http.ResponseWriter, r *http.Request, t *oidc.Tokens, state string, rp rp.RelyingParty, i oidc.UserInfo) {
userInfo = i
tokens = t
}
//you could also just take the access_token and id_token without calling the userinfo endpoint:
@@ -133,14 +131,14 @@ func (c *Client) CodeExchangeUserInfo(ctx *gin.Context) (oidc.UserInfo, error) {
//handle := rp.CodeExchangeHandler(tokeninfoClosure, c)
handle(ctx.Writer, ctx.Request)
log.Debugf("oidc: current request state: %v", ctx.Writer.Status())
// log.Debugf("oidc: current request state: %v", ctx.Writer.Status())
if sc := ctx.Writer.Status(); sc != 0 && sc != http.StatusOK {
err := ctx.Writer.Header().Get("oidc_error")
if err == "" {
return nil, errors.New("oidc: couldn't exchange auth code and thus not retrieve external user info (unknown error)")
if oidcErr := ctx.Writer.Header().Get("oidc_error"); oidcErr == "" {
return userInfo, tokens, errors.New("tailed to exchange the authentication code and retrieve the user information")
} else {
return userInfo, tokens, errors.New(oidcErr)
}
return nil, errors.New(ctx.Writer.Header().Get("oidc_error"))
}
return userinfo, nil
return userInfo, tokens, nil
}

View File

@@ -3,7 +3,7 @@ package commands
import (
"github.com/urfave/cli"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/pkg/authn"
"github.com/photoprism/photoprism/pkg/unix"
)

View File

@@ -3,7 +3,7 @@ package commands
import (
"github.com/urfave/cli"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
)
// Usage hints for the user management subcommands.

View File

@@ -90,7 +90,7 @@ func TestCliFlag_Default(t *testing.T) {
}
func TestCliFlag_EnvVar(t *testing.T) {
hasdefault := CliFlag{
hasDefault := CliFlag{
Flag: cli.StringFlag{
Name: "flag-with-default",
Usage: "`STRING`",
@@ -100,7 +100,7 @@ func TestCliFlag_EnvVar(t *testing.T) {
Tags: []string{"foo", "bar"},
}
assert.Equal(t, "PHOTOPRISM_DEFAULT", hasdefault.EnvVar())
assert.Equal(t, "PHOTOPRISM_DEFAULT", hasDefault.EnvVar())
}
func TestCliFlag_CommandFlag(t *testing.T) {

View File

@@ -1,5 +1,7 @@
package config
import "strings"
// Report returns global config values as a table for reporting.
func (f CliFlags) Report() (rows [][]string, cols []string) {
cols = []string{"Environment", "CLI Flag", "Default", "Description"}
@@ -11,7 +13,7 @@ func (f CliFlags) Report() (rows [][]string, cols []string) {
continue
}
rows = append(rows, []string{flag.EnvVar(), flag.CommandFlag(), flag.Default(), flag.Usage()})
rows = append(rows, []string{strings.ReplaceAll(flag.EnvVar(), ",", ", "), flag.CommandFlag(), flag.Default(), flag.Usage()})
}
return rows, cols

View File

@@ -4,7 +4,7 @@ import (
"strings"
"time"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/customize"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/query"

View File

@@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/customize"
"github.com/photoprism/photoprism/internal/entity"
)

View File

@@ -5,14 +5,17 @@ import (
"net/url"
"strings"
"unicode/utf8"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/pkg/authn"
"github.com/photoprism/photoprism/pkg/clean"
)
const (
OIDCDefaultScopes = "openid email profile"
OIDCDefaultProviderName = "OpenID"
OIDCDefaultProviderIcon = "img/oidc.svg"
OIDCLoginUri = ApiUri + "/oidc/login"
OIDCRedirectUri = ApiUri + "/oidc/redirect"
OidcDefaultProviderName = "OpenID"
OidcDefaultProviderIcon = "img/oidc.svg"
OidcLoginUri = ApiUri + "/oidc/login"
OidcRedirectUri = ApiUri + "/oidc/redirect"
)
// OIDCEnabled checks if sign-on via OpenID Connect (OIDC) is fully configured and enabled.
@@ -45,11 +48,6 @@ func (c *Config) OIDCUri() *url.URL {
}
}
// OIDCInsecure checks if OIDC issuer SSL/TLS certificate verification should be skipped.
func (c *Config) OIDCInsecure() bool {
return c.options.OIDCInsecure
}
// OIDCClient returns the Client ID for single sign-on via OIDC.
func (c *Config) OIDCClient() string {
return c.options.OIDCClient
@@ -63,7 +61,7 @@ func (c *Config) OIDCSecret() string {
// OIDCScopes returns the user information scopes for single sign-on via OIDC.
func (c *Config) OIDCScopes() string {
if c.options.OIDCScopes == "" {
return OIDCDefaultScopes
return authn.OidcScopes
}
return c.options.OIDCScopes
@@ -72,7 +70,7 @@ func (c *Config) OIDCScopes() string {
// OIDCProvider returns the OIDC provider name.
func (c *Config) OIDCProvider() string {
if c.options.OIDCProvider == "" {
return OIDCDefaultProviderName
return OidcDefaultProviderName
}
return c.options.OIDCProvider
@@ -81,20 +79,49 @@ func (c *Config) OIDCProvider() string {
// OIDCIcon returns the OIDC provider icon URI.
func (c *Config) OIDCIcon() string {
if c.options.OIDCIcon == "" {
return c.StaticAssetUri(OIDCDefaultProviderIcon)
return c.StaticAssetUri(OidcDefaultProviderIcon)
}
return c.options.OIDCIcon
}
// OIDCRedirect checks if unauthenticated users should automatically be redirected to the OIDC login page.
func (c *Config) OIDCRedirect() bool {
return c.options.OIDCRedirect
}
// OIDCRegister checks if new accounts may be created via OIDC.
func (c *Config) OIDCRegister() bool {
return c.options.OIDCRegister
}
// OIDCRedirect checks if unauthenticated users should automatically be redirected to the OIDC login page.
func (c *Config) OIDCRedirect() bool {
return c.options.OIDCRedirect
// OIDCUsername returns the claim to use as username when signing up via OIDC.
func (c *Config) OIDCUsername() string {
if c.options.OIDCUsername == authn.ClaimEmail {
return authn.ClaimEmail
}
return authn.ClaimUsername
}
// OIDCRole returns the default user role when signing up via OIDC.
func (c *Config) OIDCRole() acl.Role {
if c.options.OIDCRole == "" {
return acl.RoleGuest
}
role := acl.UserRoles[clean.Role(c.options.OIDCRole)]
if role != acl.RoleNone {
return role
}
return acl.RoleNone
}
// OIDCWebDAV checks if newly registered accounts should be allowed to use WebDAV if their role allows.
func (c *Config) OIDCWebDAV() bool {
return c.options.OIDCWebDAV
}
// DisableOIDC checks if single sign-on via OpenID Connect (OIDC) should be disabled.
@@ -104,12 +131,12 @@ func (c *Config) DisableOIDC() bool {
// OIDCLoginUri returns the OIDC login API endpoint URI.
func (c *Config) OIDCLoginUri() string {
return c.BaseUri(OIDCLoginUri)
return c.BaseUri(OidcLoginUri)
}
// OIDCRedirectUri returns the OIDC redirect API endpoint URI.
func (c *Config) OIDCRedirectUri() string {
return c.BaseUri(OIDCRedirectUri)
return c.BaseUri(OidcRedirectUri)
}
// OIDCReport returns the OpenID Connect config values as a table for reporting.
@@ -118,14 +145,16 @@ func (c *Config) OIDCReport() (rows [][]string, cols []string) {
rows = [][]string{
{"oidc-uri", c.OIDCUri().String()},
{"oidc-insecure", fmt.Sprintf("%t", c.OIDCInsecure())},
{"oidc-client", c.OIDCClient()},
{"oidc-secret", strings.Repeat("*", utf8.RuneCountInString(c.OIDCSecret()))},
{"oidc-scopes", c.OIDCScopes()},
{"oidc-provider", c.OIDCProvider()},
{"oidc-icon", c.OIDCIcon()},
{"oidc-register", fmt.Sprintf("%t", c.OIDCRegister())},
{"oidc-redirect", fmt.Sprintf("%t", c.OIDCRedirect())},
{"oidc-register", fmt.Sprintf("%t", c.OIDCRegister())},
{"oidc-username", c.OIDCUsername()},
{"oidc-role", c.OIDCRole().String()},
{"oidc-webdav", fmt.Sprintf("%t", c.OIDCWebDAV())},
{"disable-oidc", fmt.Sprintf("%t", c.DisableOIDC())},
}

View File

@@ -5,6 +5,9 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/pkg/authn"
)
func TestConfig_OIDCEnabled(t *testing.T) {
@@ -59,12 +62,6 @@ func TestConfig_OIDCUri(t *testing.T) {
assert.Equal(t, "", c.OIDCUri().String())
}
func TestConfig_OIDCInsecure(t *testing.T) {
c := NewConfig(CliTestContext())
assert.False(t, c.OIDCInsecure())
}
func TestConfig_OIDCClient(t *testing.T) {
c := NewConfig(CliTestContext())
@@ -80,11 +77,11 @@ func TestConfig_OIDCSecret(t *testing.T) {
func TestConfig_OIDCScopes(t *testing.T) {
c := NewConfig(CliTestContext())
assert.Equal(t, OIDCDefaultScopes, c.OIDCScopes())
assert.Equal(t, authn.OidcScopes, c.OIDCScopes())
c.options.OIDCScopes = ""
assert.Equal(t, OIDCDefaultScopes, c.OIDCScopes())
assert.Equal(t, authn.OidcScopes, c.OIDCScopes())
}
func TestConfig_OIDCProvider(t *testing.T) {
@@ -107,16 +104,42 @@ func TestConfig_OIDCIcon(t *testing.T) {
assert.Equal(t, "./test.svg", c.OIDCIcon())
}
func TestConfig_OIDCRedirect(t *testing.T) {
c := NewConfig(CliTestContext())
assert.False(t, c.OIDCRedirect())
}
func TestConfig_OIDCUsername(t *testing.T) {
c := NewConfig(CliTestContext())
assert.Equal(t, authn.ClaimUsername, c.OIDCUsername())
c.options.OIDCUsername = "email"
assert.Equal(t, authn.ClaimEmail, c.OIDCUsername())
c.options.OIDCUsername = ""
assert.Equal(t, authn.ClaimUsername, c.OIDCUsername())
}
func TestConfig_OIDCRegister(t *testing.T) {
c := NewConfig(CliTestContext())
assert.False(t, c.OIDCRegister())
}
func TestConfig_OIDCRedirect(t *testing.T) {
func TestConfig_OIDCRole(t *testing.T) {
c := NewConfig(CliTestContext())
assert.False(t, c.OIDCRedirect())
assert.Equal(t, acl.RoleGuest, c.OIDCRole())
}
func TestConfig_OIDCWebDAV(t *testing.T) {
c := NewConfig(CliTestContext())
assert.False(t, c.OIDCWebDAV())
}
func TestConfig_DisableOIDC(t *testing.T) {

View File

@@ -12,6 +12,7 @@ import (
"github.com/photoprism/photoprism/internal/ffmpeg"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/internal/ttl"
"github.com/photoprism/photoprism/pkg/authn"
"github.com/photoprism/photoprism/pkg/header"
"github.com/photoprism/photoprism/pkg/i18n"
"github.com/photoprism/photoprism/pkg/txt"
@@ -36,7 +37,7 @@ var Flags = CliFlags{
Name: "admin-user, login",
Usage: "`USERNAME` of the superadmin account that is created on first startup",
Value: "admin",
EnvVar: EnvVar("ADMIN_USER"),
EnvVar: EnvVar("ADMIN_USER") + "," + EnvVar("ADMIN_USERNAME"),
}}, {
Flag: cli.StringFlag{
Name: "admin-password, pw",
@@ -49,11 +50,6 @@ var Flags = CliFlags{
Value: "",
EnvVar: EnvVar("OIDC_URI"),
}}, {
Flag: cli.BoolFlag{
Name: "oidc-insecure",
Usage: "skip identity provider SSL/TLS certificate verification",
EnvVar: EnvVar("OIDC_INSECURE"),
}}, {
Flag: cli.StringFlag{
Name: "oidc-client",
Usage: "client `ID` for single sign-on via OpenID Connect",
@@ -70,7 +66,7 @@ var Flags = CliFlags{
Name: "oidc-scopes",
Hidden: true,
Usage: "user information `SCOPES` for single sign-on via OpenID Connect",
Value: OIDCDefaultScopes,
Value: authn.OidcScopes,
EnvVar: EnvVar("OIDC_SCOPES"),
}}, {
Flag: cli.StringFlag{
@@ -85,15 +81,26 @@ var Flags = CliFlags{
Value: "",
EnvVar: EnvVar("OIDC_ICON"),
}}, {
Flag: cli.BoolFlag{
Name: "oidc-redirect",
Usage: "automatically redirect unauthenticated users to the configured identity provider",
EnvVar: EnvVar("OIDC_REDIRECT"),
}}, {
Flag: cli.BoolFlag{
Name: "oidc-register",
Usage: "allow new users to create an account when they sign in with OpenID Connect",
EnvVar: EnvVar("OIDC_REGISTER"),
}}, {
Flag: cli.StringFlag{
Name: "oidc-username",
Usage: "username `CLAIM` for OpenID Connect users (preferred_username, email)",
Value: authn.ClaimUsername,
EnvVar: EnvVar("OIDC_USERNAME"),
}}, {
Flag: cli.BoolFlag{
Name: "oidc-redirect",
Usage: "automatically redirect unauthenticated users to the configured identity provider",
EnvVar: EnvVar("OIDC_REDIRECT"),
Name: "oidc-webdav",
Usage: "enable WebDAV for new OpenID Connect users if their role allows it",
EnvVar: EnvVar("OIDC_WEBDAV"),
}}, {
Flag: cli.BoolFlag{
Name: "disable-oidc",
@@ -274,7 +281,7 @@ var Flags = CliFlags{
Name: "index-workers, workers",
Usage: "maximum `NUMBER` of indexing workers, default depends on the number of physical cores",
Value: cpuid.CPU.PhysicalCores / 2,
EnvVar: EnvVar("INDEX_WORKERS") + ", " + EnvVar("WORKERS"),
EnvVar: EnvVar("INDEX_WORKERS") + "," + EnvVar("WORKERS"),
}}, {
Flag: cli.StringFlag{
Name: "index-schedule",
@@ -753,7 +760,7 @@ var Flags = CliFlags{
Name: "sips-exclude",
Usage: "file `EXTENSIONS` not to be used with Sips*macOS only*",
Value: "avif, avifs, thm",
EnvVar: EnvVar("SIPS_EXCLUDE") + ", " + EnvVar("SIPS_BLACKLIST"),
EnvVar: EnvVar("SIPS_EXCLUDE") + "," + EnvVar("SIPS_BLACKLIST"),
}}, {
Flag: cli.StringFlag{
Name: "darktable-bin",
@@ -765,7 +772,7 @@ var Flags = CliFlags{
Name: "darktable-exclude",
Usage: "file `EXTENSIONS` not to be used with Darktable",
Value: "thm",
EnvVar: EnvVar("DARKTABLE_EXCLUDE") + ", " + EnvVar("DARKTABLE_BLACKLIST"),
EnvVar: EnvVar("DARKTABLE_EXCLUDE") + "," + EnvVar("DARKTABLE_BLACKLIST"),
}}, {
Flag: cli.StringFlag{
Name: "darktable-cache-path",
@@ -789,7 +796,7 @@ var Flags = CliFlags{
Name: "rawtherapee-exclude",
Usage: "file `EXTENSIONS` not to be used with RawTherapee",
Value: "dng, thm",
EnvVar: EnvVar("RAWTHERAPEE_EXCLUDE") + ", " + EnvVar("RAWTHERAPEE_BLACKLIST"),
EnvVar: EnvVar("RAWTHERAPEE_EXCLUDE") + "," + EnvVar("RAWTHERAPEE_BLACKLIST"),
}}, {
Flag: cli.StringFlag{
Name: "imagemagick-bin",
@@ -801,7 +808,7 @@ var Flags = CliFlags{
Name: "imagemagick-exclude",
Usage: "file `EXTENSIONS` not to be used with ImageMagick",
Value: "heif, heic, heics, avif, avifs, jxl, thm",
EnvVar: EnvVar("IMAGEMAGICK_EXCLUDE") + ", " + EnvVar("IMAGEMAGICK_BLACKLIST"),
EnvVar: EnvVar("IMAGEMAGICK_EXCLUDE") + "," + EnvVar("IMAGEMAGICK_BLACKLIST"),
}}, {
Flag: cli.StringFlag{
Name: "heifconvert-bin",

View File

@@ -32,14 +32,16 @@ type Options struct {
RegisterUri string `yaml:"RegisterUri" json:"-" flag:"register-uri"`
LoginUri string `yaml:"LoginUri" json:"-" flag:"login-uri"`
OIDCUri string `yaml:"OIDCUri" json:"-" flag:"oidc-uri"`
OIDCInsecure bool `yaml:"OIDCInsecure" json:"-" flag:"oidc-insecure"`
OIDCClient string `yaml:"OIDCClient" json:"-" flag:"oidc-client"`
OIDCSecret string `yaml:"OIDCSecret" json:"-" flag:"oidc-secret"`
OIDCScopes string `yaml:"OIDCScopes" json:"-" flag:"oidc-scopes"`
OIDCProvider string `yaml:"OIDCProvider" json:"OIDCProvider" flag:"oidc-provider"`
OIDCIcon string `yaml:"OIDCIcon" json:"OIDCIcon" flag:"oidc-icon"`
OIDCRegister bool `yaml:"OIDCRegister" json:"OIDCRegister" flag:"oidc-register"`
OIDCRedirect bool `yaml:"OIDCRedirect" json:"OIDCRedirect" flag:"oidc-redirect"`
OIDCRegister bool `yaml:"OIDCRegister" json:"OIDCRegister" flag:"oidc-register"`
OIDCUsername string `yaml:"OIDCUsername" json:"-" flag:"oidc-username"`
OIDCRole string `yaml:"OIDCRole" json:"-" flag:"oidc-role"`
OIDCWebDAV bool `yaml:"OIDCWebDAV" json:"-" flag:"oidc-webdav"`
DisableOIDC bool `yaml:"DisableOIDC" json:"DisableOIDC" flag:"disable-oidc"`
SessionMaxAge int64 `yaml:"SessionMaxAge" json:"-" flag:"session-maxage"`
SessionTimeout int64 `yaml:"SessionTimeout" json:"-" flag:"session-timeout"`

View File

@@ -1,7 +1,7 @@
package config
import (
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/customize"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/pkg/fs"

View File

@@ -17,6 +17,7 @@ import (
"github.com/photoprism/photoprism/internal/customize"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/authn"
"github.com/photoprism/photoprism/pkg/capture"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/fs"
@@ -266,7 +267,7 @@ func CliTestContext() *cli.Context {
LogErr(c.Set("oidc-uri", config.OIDCUri))
LogErr(c.Set("oidc-client", config.OIDCClient))
LogErr(c.Set("oidc-secret", config.OIDCSecret))
LogErr(c.Set("oidc-scopes", OIDCDefaultScopes))
LogErr(c.Set("oidc-scopes", authn.OidcScopes))
LogErr(c.Set("storage-path", config.StoragePath))
LogErr(c.Set("sidecar-path", config.SidecarPath))
LogErr(c.Set("sidecar-yaml", fmt.Sprintf("%t", config.SidecarYaml)))

View File

@@ -1,7 +1,7 @@
package customize
import (
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/auth/acl"
)
// ApplyACL updates the current settings based on the access control list provided.

Some files were not shown because too many files have changed in this diff Show More