mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-22 05:33:44 +01:00
Compare commits
6 Commits
copilot/fi
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
096b682f0a | ||
|
|
e4d8bb2ada | ||
|
|
3becf046e6 | ||
|
|
a21b3257d4 | ||
|
|
5f9ab577bb | ||
|
|
0a969bb64d |
@@ -1,14 +0,0 @@
|
||||
entgo.io/ent v0.14.5 h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4=
|
||||
entgo.io/ent v0.14.5/go.mod h1:zTzLmWtPvGpmSwtkaayM2cm5m819NdM7z7tYPq3vN0U=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/sysadminsmedia/homebox/backend v0.0.0-20251212183312-2d1d3d927bfd h1:QULUJSgHc4rSlTjb2qYT6FIgwDWFCqEpnYqc/ltsrkk=
|
||||
github.com/sysadminsmedia/homebox/backend v0.0.0-20251212183312-2d1d3d927bfd/go.mod h1:jB+tPmHtPDM1VnAjah0gvcRfP/s7c+rtQwpA8cvZD/U=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -124,7 +124,7 @@ func (ctrl *V1Controller) HandleAuthLogin(ps ...AuthProvider) errchain.HandlerFu
|
||||
return validate.NewUnauthorizedError()
|
||||
}
|
||||
|
||||
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true)
|
||||
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true, newToken.AttachmentToken)
|
||||
return server.JSON(w, http.StatusOK, TokenResponse{
|
||||
Token: "Bearer " + newToken.Raw,
|
||||
ExpiresAt: newToken.ExpiresAt,
|
||||
@@ -178,7 +178,7 @@ func (ctrl *V1Controller) HandleAuthRefresh() errchain.HandlerFunc {
|
||||
return validate.NewUnauthorizedError()
|
||||
}
|
||||
|
||||
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, false)
|
||||
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, false, newToken.AttachmentToken)
|
||||
return server.JSON(w, http.StatusOK, newToken)
|
||||
}
|
||||
}
|
||||
@@ -187,7 +187,7 @@ func noPort(host string) string {
|
||||
return strings.Split(host, ":")[0]
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) setCookies(w http.ResponseWriter, domain, token string, expires time.Time, remember bool) {
|
||||
func (ctrl *V1Controller) setCookies(w http.ResponseWriter, domain, token string, expires time.Time, remember bool, attachmentToken string) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieNameRemember,
|
||||
Value: strconv.FormatBool(remember),
|
||||
@@ -219,6 +219,19 @@ func (ctrl *V1Controller) setCookies(w http.ResponseWriter, domain, token string
|
||||
HttpOnly: false,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
// Set attachment token cookie (accessible to frontend, not HttpOnly)
|
||||
if attachmentToken != "" {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "hb.auth.attachment_token",
|
||||
Value: attachmentToken,
|
||||
Expires: expires,
|
||||
Domain: domain,
|
||||
Secure: ctrl.cookieSecure,
|
||||
HttpOnly: false,
|
||||
Path: "/",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) unsetCookies(w http.ResponseWriter, domain string) {
|
||||
@@ -252,6 +265,17 @@ func (ctrl *V1Controller) unsetCookies(w http.ResponseWriter, domain string) {
|
||||
HttpOnly: false,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
// Unset attachment token cookie
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "hb.auth.attachment_token",
|
||||
Value: "",
|
||||
Expires: time.Unix(0, 0),
|
||||
Domain: domain,
|
||||
Secure: ctrl.cookieSecure,
|
||||
HttpOnly: false,
|
||||
Path: "/",
|
||||
})
|
||||
}
|
||||
|
||||
// HandleOIDCLogin godoc
|
||||
@@ -310,7 +334,7 @@ func (ctrl *V1Controller) HandleOIDCCallback() errchain.HandlerFunc {
|
||||
}
|
||||
|
||||
// Set cookies and redirect to home
|
||||
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true)
|
||||
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true, newToken.AttachmentToken)
|
||||
http.Redirect(w, r, "/home", http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -325,6 +325,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
|
||||
@@ -347,6 +349,8 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/olahol/melody v1.4.0 h1:Pa5SdeZL/zXPi1tJuMAPDbl4n3gQOThSL6G1p4qZ4SI=
|
||||
github.com/olahol/melody v1.4.0/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
@@ -389,6 +393,10 @@ github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAX
|
||||
github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
||||
@@ -97,35 +97,12 @@ func ToItemAttachment(attachment *ent.Attachment) ItemAttachment {
|
||||
}
|
||||
}
|
||||
|
||||
// normalizePath converts backslashes to forward slashes and trims slashes from both ends
|
||||
// This ensures consistent path separators for blob storage which expects forward slashes
|
||||
func normalizePath(path string) string {
|
||||
path = strings.ReplaceAll(path, "\\", "/")
|
||||
return strings.Trim(path, "/")
|
||||
}
|
||||
|
||||
func (r *AttachmentRepo) path(gid uuid.UUID, hash string) string {
|
||||
// Always use forward slashes for consistency across platforms
|
||||
// This ensures paths are stored in the database with forward slashes
|
||||
return fmt.Sprintf("%s/documents/%s", gid.String(), hash)
|
||||
return filepath.Join(gid.String(), "documents", hash)
|
||||
}
|
||||
|
||||
func (r *AttachmentRepo) fullPath(relativePath string) string {
|
||||
// Normalize path separators to forward slashes for blob storage
|
||||
// The blob library expects forward slashes in keys regardless of OS
|
||||
normalizedRelativePath := normalizePath(relativePath)
|
||||
|
||||
// Always use forward slashes when joining paths for blob storage
|
||||
if r.storage.PrefixPath == "" {
|
||||
return normalizedRelativePath
|
||||
}
|
||||
normalizedPrefix := normalizePath(r.storage.PrefixPath)
|
||||
|
||||
if normalizedPrefix == "" {
|
||||
return normalizedRelativePath
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s/%s", normalizedPrefix, normalizedRelativePath)
|
||||
return filepath.Join(r.storage.PrefixPath, relativePath)
|
||||
}
|
||||
|
||||
func (r *AttachmentRepo) GetFullPath(relativePath string) string {
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
|
||||
)
|
||||
|
||||
func TestAttachmentRepo_Create(t *testing.T) {
|
||||
@@ -282,58 +281,3 @@ func TestAttachmentRepo_SettingPhotoPrimaryStillWorks(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.False(t, photo1.Primary, "Photo 1 should no longer be primary after setting Photo 2 as primary")
|
||||
}
|
||||
|
||||
func TestAttachmentRepo_PathNormalization(t *testing.T) {
|
||||
// Test that paths always use forward slashes
|
||||
repo := &AttachmentRepo{
|
||||
storage: config.Storage{
|
||||
PrefixPath: ".data",
|
||||
},
|
||||
}
|
||||
|
||||
testGUID := uuid.MustParse("eb6bf410-a1a8-478d-a803-ca3948368a0c")
|
||||
testHash := "f295eb01-18a9-4631-a797-70bd9623edd4.png"
|
||||
|
||||
// Test path() method - should always return forward slashes
|
||||
relativePath := repo.path(testGUID, testHash)
|
||||
assert.Equal(t, "eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png", relativePath)
|
||||
assert.NotContains(t, relativePath, "\\", "path() should not contain backslashes")
|
||||
|
||||
// Test fullPath() with forward slash input (from database)
|
||||
fullPath := repo.fullPath("eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png")
|
||||
assert.Equal(t, ".data/eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png", fullPath)
|
||||
assert.NotContains(t, fullPath, "\\", "fullPath() should not contain backslashes")
|
||||
|
||||
// Test fullPath() with backslash input (legacy Windows paths from old database)
|
||||
fullPathWithBackslash := repo.fullPath("eb6bf410-a1a8-478d-a803-ca3948368a0c\\documents\\f295eb01-18a9-4631-a797-70bd9623edd4.png")
|
||||
assert.Equal(t, ".data/eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png", fullPathWithBackslash)
|
||||
assert.NotContains(t, fullPathWithBackslash, "\\", "fullPath() should normalize backslashes to forward slashes")
|
||||
|
||||
// Test with Windows-style prefix path
|
||||
repoWindows := &AttachmentRepo{
|
||||
storage: config.Storage{
|
||||
PrefixPath: ".data",
|
||||
},
|
||||
}
|
||||
fullPathWindows := repoWindows.fullPath("eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png")
|
||||
assert.NotContains(t, fullPathWindows, "\\", "fullPath() should normalize Windows paths")
|
||||
|
||||
// Test empty prefix
|
||||
repoNoPrefix := &AttachmentRepo{
|
||||
storage: config.Storage{
|
||||
PrefixPath: "",
|
||||
},
|
||||
}
|
||||
fullPathNoPrefix := repoNoPrefix.fullPath("eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png")
|
||||
assert.Equal(t, "eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png", fullPathNoPrefix)
|
||||
|
||||
// Test with single slash prefix (like in tests)
|
||||
repoSlashPrefix := &AttachmentRepo{
|
||||
storage: config.Storage{
|
||||
PrefixPath: "/",
|
||||
},
|
||||
}
|
||||
fullPathSlashPrefix := repoSlashPrefix.fullPath("eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png")
|
||||
assert.Equal(t, "eb6bf410-a1a8-478d-a803-ca3948368a0c/documents/f295eb01-18a9-4631-a797-70bd9623edd4.png", fullPathSlashPrefix)
|
||||
assert.NotContains(t, fullPathSlashPrefix, "//", "fullPath() should not have double slashes")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export default [
|
||||
{text: 'Installation', link: '/en/installation'},
|
||||
{text: 'Configure', link: '/en/configure'},
|
||||
{text: 'Storage', link: '/en/configure/storage'},
|
||||
{text: 'OIDC', link: '/en/configure/oidc'},
|
||||
{text: 'Upgrade Guide', link: '/en/upgrade'},
|
||||
{text: 'Migration Guide', link: '/en/migration'},
|
||||
]
|
||||
|
||||
@@ -73,6 +73,83 @@ aside: false
|
||||
| HBOX_THUMBNAIL_HEIGHT | 500 | height for generated thumbnails in pixels |
|
||||
| HBOX_BARCODE_TOKEN_BARCODESPIDER | | API token for BarcodeSpider.com service used for barcode product lookups. If not set, barcode product lookups will not be performed. |
|
||||
|
||||
```sh
|
||||
Options:
|
||||
--barcode-token-barcodespider <string>
|
||||
--database-database <string>
|
||||
--database-driver <string> (default: sqlite3)
|
||||
--database-host <string>
|
||||
--database-password <string>
|
||||
--database-port <string>
|
||||
--database-pub-sub-conn-string <string> (default: mem://{{ .Topic }})
|
||||
--database-sqlite-path <string> (default: ./.data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1&_time_format=sqlite)
|
||||
--database-ssl-cert <string>
|
||||
--database-ssl-key <string>
|
||||
--database-ssl-mode <string> (default: require)
|
||||
--database-ssl-root-cert <string>
|
||||
--database-username <string>
|
||||
--debug-enabled <bool> (default: false)
|
||||
--debug-port <string> (default: 4000)
|
||||
--demo <bool>
|
||||
-h, --help display this help message
|
||||
--label-maker-additional-information <string>
|
||||
--label-maker-bold-font-path <string>
|
||||
--label-maker-dynamic-length <bool> (default: true)
|
||||
--label-maker-font-size <float> (default: 32.0)
|
||||
--label-maker-height <int> (default: 200)
|
||||
--label-maker-label-service-timeout <int>
|
||||
--label-maker-label-service-url <string>
|
||||
--label-maker-margin <int> (default: 32)
|
||||
--label-maker-padding <int> (default: 32)
|
||||
--label-maker-print-command <string>
|
||||
--label-maker-regular-font-path <string>
|
||||
--label-maker-width <int> (default: 526)
|
||||
--log-format <string> (default: text)
|
||||
--log-level <string> (default: info)
|
||||
--mailer-from <string>
|
||||
--mailer-host <string>
|
||||
--mailer-password <string>
|
||||
--mailer-port <int>
|
||||
--mailer-username <string>
|
||||
--mode <string> (default: development)
|
||||
--oidc-allowed-groups <string>
|
||||
--oidc-auto-redirect <bool> (default: false)
|
||||
--oidc-button-text <string> (default: Sign in with OIDC)
|
||||
--oidc-client-id <string>
|
||||
--oidc-client-secret <string>
|
||||
--oidc-email-claim <string> (default: email)
|
||||
--oidc-email-verified-claim <string> (default: email_verified)
|
||||
--oidc-enabled <bool> (default: false)
|
||||
--oidc-group-claim <string> (default: groups)
|
||||
--oidc-issuer-url <string>
|
||||
--oidc-name-claim <string> (default: name)
|
||||
--oidc-request-timeout <duration> (default: 30s)
|
||||
--oidc-scope <string> (default: openid profile email)
|
||||
--oidc-state-expiry <duration> (default: 10m)
|
||||
--oidc-verify-email <bool> (default: false)
|
||||
--options-allow-analytics <bool> (default: false)
|
||||
--options-allow-local-login <bool> (default: true)
|
||||
--options-allow-registration <bool> (default: true)
|
||||
--options-auto-increment-asset-id <bool> (default: true)
|
||||
--options-currency-config <string>
|
||||
--options-github-release-check <bool> (default: true)
|
||||
--options-hostname <string>
|
||||
--options-trust-proxy <bool> (default: false)
|
||||
--storage-conn-string <string> (default: file:///./)
|
||||
--storage-prefix-path <string> (default: .data)
|
||||
--thumbnail-enabled <bool> (default: true)
|
||||
--thumbnail-height <int> (default: 500)
|
||||
--thumbnail-width <int> (default: 500)
|
||||
-v, --version display version
|
||||
--web-host <string>
|
||||
--web-idle-timeout <duration> (default: 30s)
|
||||
--web-max-upload-size <int> (default: 10)
|
||||
--web-port <string> (default: 7745)
|
||||
--web-read-timeout <duration> (default: 10s)
|
||||
--web-write-timeout <duration> (default: 10s)
|
||||
```
|
||||
:::
|
||||
|
||||
### HBOX_WEB_HOST examples
|
||||
|
||||
| Value | Notes |
|
||||
@@ -170,114 +247,4 @@ For SQLite in production:
|
||||
|
||||
## OIDC Configuration
|
||||
|
||||
HomeBox supports OpenID Connect (OIDC) authentication, allowing users to login using external identity providers like Keycloak, Authentik, Google, Microsoft, etc.
|
||||
|
||||
### Basic OIDC Setup
|
||||
|
||||
1. **Enable OIDC**: Set `HBOX_OIDC_ENABLED=true`
|
||||
2. **Provider Configuration**: Set the required provider details:
|
||||
- `HBOX_OIDC_ISSUER_URL`: Your OIDC provider's issuer URL
|
||||
- `HBOX_OIDC_CLIENT_ID`: Client ID from your OIDC provider
|
||||
- `HBOX_OIDC_CLIENT_SECRET`: Client secret from your OIDC provider
|
||||
|
||||
3. **Configure Redirect URI**: In your OIDC provider, set the redirect URI to:
|
||||
`https://your-homebox-domain.com/api/v1/users/login/oidc/callback`
|
||||
|
||||
### Advanced OIDC Configuration
|
||||
|
||||
- **Group Authorization**: Use `HBOX_OIDC_ALLOWED_GROUPS` to restrict access to specific groups
|
||||
- **Custom Claims**: Configure `HBOX_OIDC_GROUP_CLAIM`, `HBOX_OIDC_EMAIL_CLAIM`, and `HBOX_OIDC_NAME_CLAIM` if your provider uses different claim names
|
||||
- **Auto Redirect to OIDC**: Set `HBOX_OIDC_AUTO_REDIRECT=true` to automatically redirect users directly to OIDC
|
||||
- **Local Login**: Set `HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=false` to completely disable username/password login
|
||||
- **Email Verification**: Set `HBOX_OIDC_VERIFY_EMAIL=true` to require email verification from the OIDC provider
|
||||
|
||||
### Security Considerations
|
||||
|
||||
::: warning OIDC Security
|
||||
- Store `HBOX_OIDC_CLIENT_SECRET` securely (use environment variables, not config files)
|
||||
- Use HTTPS for production deployments
|
||||
- Configure proper redirect URIs in your OIDC provider
|
||||
- Consider setting `HBOX_OIDC_ALLOWED_GROUPS` for group-based access control
|
||||
:::
|
||||
|
||||
::: tip CLI Arguments
|
||||
If you're deploying without docker you can use command line arguments to configure the application. Run `homebox --help`
|
||||
for more information.
|
||||
|
||||
```sh
|
||||
Options:
|
||||
--barcode-token-barcodespider <string>
|
||||
--database-database <string>
|
||||
--database-driver <string> (default: sqlite3)
|
||||
--database-host <string>
|
||||
--database-password <string>
|
||||
--database-port <string>
|
||||
--database-pub-sub-conn-string <string> (default: mem://{{ .Topic }})
|
||||
--database-sqlite-path <string> (default: ./.data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1&_time_format=sqlite)
|
||||
--database-ssl-cert <string>
|
||||
--database-ssl-key <string>
|
||||
--database-ssl-mode <string> (default: require)
|
||||
--database-ssl-root-cert <string>
|
||||
--database-username <string>
|
||||
--debug-enabled <bool> (default: false)
|
||||
--debug-port <string> (default: 4000)
|
||||
--demo <bool>
|
||||
-h, --help display this help message
|
||||
--label-maker-additional-information <string>
|
||||
--label-maker-bold-font-path <string>
|
||||
--label-maker-dynamic-length <bool> (default: true)
|
||||
--label-maker-font-size <float> (default: 32.0)
|
||||
--label-maker-height <int> (default: 200)
|
||||
--label-maker-label-service-timeout <int>
|
||||
--label-maker-label-service-url <string>
|
||||
--label-maker-margin <int> (default: 32)
|
||||
--label-maker-padding <int> (default: 32)
|
||||
--label-maker-print-command <string>
|
||||
--label-maker-regular-font-path <string>
|
||||
--label-maker-width <int> (default: 526)
|
||||
--log-format <string> (default: text)
|
||||
--log-level <string> (default: info)
|
||||
--mailer-from <string>
|
||||
--mailer-host <string>
|
||||
--mailer-password <string>
|
||||
--mailer-port <int>
|
||||
--mailer-username <string>
|
||||
--mode <string> (default: development)
|
||||
--oidc-allowed-groups <string>
|
||||
--oidc-auto-redirect <bool> (default: false)
|
||||
--oidc-button-text <string> (default: Sign in with OIDC)
|
||||
--oidc-client-id <string>
|
||||
--oidc-client-secret <string>
|
||||
--oidc-email-claim <string> (default: email)
|
||||
--oidc-email-verified-claim <string> (default: email_verified)
|
||||
--oidc-enabled <bool> (default: false)
|
||||
--oidc-group-claim <string> (default: groups)
|
||||
--oidc-issuer-url <string>
|
||||
--oidc-name-claim <string> (default: name)
|
||||
--oidc-request-timeout <duration> (default: 30s)
|
||||
--oidc-scope <string> (default: openid profile email)
|
||||
--oidc-state-expiry <duration> (default: 10m)
|
||||
--oidc-verify-email <bool> (default: false)
|
||||
--options-allow-analytics <bool> (default: false)
|
||||
--options-allow-local-login <bool> (default: true)
|
||||
--options-allow-registration <bool> (default: true)
|
||||
--options-auto-increment-asset-id <bool> (default: true)
|
||||
--options-currency-config <string>
|
||||
--options-github-release-check <bool> (default: true)
|
||||
--options-hostname <string>
|
||||
--options-trust-proxy <bool> (default: false)
|
||||
--storage-conn-string <string> (default: file:///./)
|
||||
--storage-prefix-path <string> (default: .data)
|
||||
--thumbnail-enabled <bool> (default: true)
|
||||
--thumbnail-height <int> (default: 500)
|
||||
--thumbnail-width <int> (default: 500)
|
||||
-v, --version display version
|
||||
--web-host <string>
|
||||
--web-idle-timeout <duration> (default: 30s)
|
||||
--web-max-upload-size <int> (default: 10)
|
||||
--web-port <string> (default: 7745)
|
||||
--web-read-timeout <duration> (default: 10s)
|
||||
--web-write-timeout <duration> (default: 10s)
|
||||
```
|
||||
|
||||
:::
|
||||
For configuring OpenID Connect (OIDC) authentication, refer to the [OIDC Configuration Guide](/en/configure/oidc).
|
||||
|
||||
38
docs/en/configure/oidc.md
Normal file
38
docs/en/configure/oidc.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Configure OIDC
|
||||
|
||||
HomeBox supports OpenID Connect (OIDC) authentication, allowing users to login using external identity providers like Keycloak, Authentik, Authelia, Google, Microsoft, etc.
|
||||
|
||||
## Basic OIDC Setup
|
||||
|
||||
1. **Enable OIDC**: Set `HBOX_OIDC_ENABLED=true`.
|
||||
2. **Provider Configuration**: Set the required provider details:
|
||||
- `HBOX_OIDC_ISSUER_URL`: Your OIDC provider's issuer URL (this should be a URL without a trailing slash).
|
||||
- `HBOX_OIDC_CLIENT_ID`: Client ID from your OIDC provider.
|
||||
- `HBOX_OIDC_CLIENT_SECRET`: Client secret from your OIDC provider.
|
||||
- If you are using a reverse proxy, it may be necessary to set `HBOX_OPTIONS_TRUST_PROXY=true` to ensure `https` is correctly detected.
|
||||
|
||||
3. **Configure Redirect URI**: In your OIDC provider, set the redirect URI to:
|
||||
`https://your-homebox-domain.example.com/api/v1/users/login/oidc/callback`.
|
||||
|
||||
## Advanced OIDC Configuration
|
||||
|
||||
- **Group Authorization**: Use `HBOX_OIDC_ALLOWED_GROUPS` to restrict access to specific groups, e.g. `HBOX_OIDC_ALLOWED_GROUPS=admin,homebox`.
|
||||
- Some providers require the `groups` scope to return group claims, include it in `HBOX_OIDC_SCOPE` (e.g. `openid profile email groups`) or configure the provider to release the claim.
|
||||
- **Custom Claims**: Configure `HBOX_OIDC_GROUP_CLAIM`, `HBOX_OIDC_EMAIL_CLAIM`, and `HBOX_OIDC_NAME_CLAIM` if your provider uses different claim names.
|
||||
- These default to `HBOX_OIDC_GROUP_CLAIM=groups`, `HBOX_OIDC_EMAIL_CLAIM=email` and `HBOX_OIDC_NAME_CLAIM=name`.
|
||||
- **Auto Redirect to OIDC**: Set `HBOX_OIDC_AUTO_REDIRECT=true` to automatically redirect users directly to OIDC.
|
||||
- **Local Login**: Set `HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=false` to completely disable username/password login.
|
||||
- **Email Verification**: Set `HBOX_OIDC_VERIFY_EMAIL=true` to require email verification from the OIDC provider.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
::: warning OIDC Security
|
||||
- Store `HBOX_OIDC_CLIENT_SECRET` securely (use environment variables, not config files).
|
||||
- Use HTTPS for production deployments.
|
||||
- Configure proper redirect URIs in your OIDC provider.
|
||||
- Consider setting `HBOX_OIDC_ALLOWED_GROUPS` for group-based access control.
|
||||
:::
|
||||
|
||||
::: tip CLI Arguments
|
||||
If you're deploying without docker you can use command line arguments to configure the application. Run `homebox --help` for more information.
|
||||
:::
|
||||
@@ -52,7 +52,7 @@ services:
|
||||
environment:
|
||||
- HBOX_LOG_LEVEL=info
|
||||
- HBOX_LOG_FORMAT=text
|
||||
- HBOX_WEB_MAX_FILE_UPLOAD=10
|
||||
- HBOX_WEB_MAX_UPLOAD_SIZE=10
|
||||
# Please consider allowing analytics to help us improve Homebox (basic computer information, no personal data)
|
||||
- HBOX_OPTIONS_ALLOW_ANALYTICS=false
|
||||
volumes:
|
||||
|
||||
@@ -81,17 +81,6 @@
|
||||
errorMessage.value = t("scanner.error");
|
||||
};
|
||||
|
||||
const checkPermissionsError = async () => {
|
||||
if (navigator.permissions) {
|
||||
const permissionStatus = await navigator.permissions.query({ name: "camera" as PermissionName });
|
||||
if (permissionStatus.state === "denied") {
|
||||
errorMessage.value = t("scanner.permission_denied");
|
||||
console.error("Camera permission denied");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleButtonClick = () => {
|
||||
openDialog(DialogID.ProductImport, { params: { barcode: detectedBarcode.value } });
|
||||
};
|
||||
@@ -103,11 +92,19 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (await checkPermissionsError()) {
|
||||
try {
|
||||
// Request camera permission first
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
||||
stream.getTracks().forEach(track => track.stop());
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error && err.name === "NotAllowedError") {
|
||||
errorMessage.value = t("scanner.permission_denied");
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
try {
|
||||
const devices = await codeReader.listVideoInputDevices();
|
||||
sources.value = devices;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
{{ btn.name.value }}
|
||||
<Shortcut
|
||||
v-if="btn.shortcut"
|
||||
class="ml-auto hidden group-hover:inline"
|
||||
class="invisible ml-auto group-hover:visible"
|
||||
:keys="btn.shortcut.replace('Shift', '⇧').split('+')"
|
||||
/>
|
||||
</DropdownMenuItem>
|
||||
|
||||
Reference in New Issue
Block a user