From 8fee50d07c5f4f36edc57f5d24e7e9aef3cabfc8 Mon Sep 17 00:00:00 2001 From: Henry Dollman Date: Wed, 17 Jul 2024 21:50:57 -0400 Subject: [PATCH] option to disable password login --- .gitattributes | 1 + main.go | 30 +- migrations/1720568457_collections_snapshot.go | 6 +- readme.md | 8 +- site/public/favicon-green.svg | 2 +- site/public/favicon-red.svg | 2 +- site/public/favicon.svg | 2 +- site/src/components/login/auth-form.tsx | 259 +++++++++--------- .../src/components/login/forgot-pass-form.tsx | 6 +- site/src/components/login/login.tsx | 18 +- 10 files changed, 190 insertions(+), 144 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..794f361 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +linguist-language=Go \ No newline at end of file diff --git a/main.go b/main.go index 7a27002..101ab03 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,6 @@ import ( "github.com/pocketbase/pocketbase/apis" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/models" - "github.com/pocketbase/pocketbase/plugins/migratecmd" "github.com/pocketbase/pocketbase/tools/cron" "github.com/pocketbase/pocketbase/tools/mailer" "golang.org/x/crypto/ssh" @@ -38,10 +37,31 @@ func main() { // loosely check if it was executed using "go run" isGoRun := strings.HasPrefix(os.Args[0], os.TempDir()) - // enable auto creation of migration files when making collection changes in the Admin UI - migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{ - // (the isGoRun check is to enable it only during development) - Automigrate: isGoRun, + // // enable auto creation of migration files when making collection changes in the Admin UI + // migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{ + // // (the isGoRun check is to enable it only during development) + // Automigrate: isGoRun, + // }) + + // set auth settings + app.OnAfterBootstrap().Add(func(e *core.BootstrapEvent) error { + usersCollection, err := app.Dao().FindCollectionByNameOrId("users") + if err != nil { + return err + } + usersAuthOptions := usersCollection.AuthOptions() + if os.Getenv("DISABLE_PASSWORD_AUTH") == "true" { + usersAuthOptions.AllowEmailAuth = false + usersAuthOptions.AllowUsernameAuth = false + } else { + usersAuthOptions.AllowEmailAuth = true + usersAuthOptions.AllowUsernameAuth = true + } + usersCollection.SetOptions(usersAuthOptions) + if err := app.Dao().SaveCollection(usersCollection); err != nil { + return err + } + return nil }) // serve site diff --git a/migrations/1720568457_collections_snapshot.go b/migrations/1720568457_collections_snapshot.go index 2be2d9d..71d03dd 100644 --- a/migrations/1720568457_collections_snapshot.go +++ b/migrations/1720568457_collections_snapshot.go @@ -266,13 +266,13 @@ func init() { "options": { "allowEmailAuth": true, "allowOAuth2Auth": true, - "allowUsernameAuth": true, + "allowUsernameAuth": false, "exceptEmailDomains": null, "manageRule": null, "minPasswordLength": 8, "onlyEmailDomains": null, - "onlyVerified": false, - "requireEmail": false + "onlyVerified": true, + "requireEmail": true } }, { diff --git a/readme.md b/readme.md index cec739f..e503771 100644 --- a/readme.md +++ b/readme.md @@ -29,6 +29,12 @@ The hub and agent are distributed as single binary files, as well as docker imag ### Binary +## Environment Variables + +| Name | Default | Description | +| ----------------------- | ------- | ------------------------------------------------- | +| `DISABLE_PASSWORD_AUTH` | unset | Disables password authentication if set to `true` | + ## OAuth / OIDC integration Beszel supports OpenID Connect and many OAuth2 authentication providers (see list below). To enable this, you will need to: @@ -64,7 +70,7 @@ Beszel supports OpenID Connect and many OAuth2 authentication providers (see lis - Yandex -## API +## REST API Because Beszel is built on top of PocketBase, you can use the normal PocketBase API to read or update your data in your own applications. diff --git a/site/public/favicon-green.svg b/site/public/favicon-green.svg index cf1bbee..5608df1 100644 --- a/site/public/favicon-green.svg +++ b/site/public/favicon-green.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/site/public/favicon-red.svg b/site/public/favicon-red.svg index 34daf83..20e282f 100644 --- a/site/public/favicon-red.svg +++ b/site/public/favicon-red.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/site/public/favicon.svg b/site/public/favicon.svg index d5f3cac..b53efa0 100644 --- a/site/public/favicon.svg +++ b/site/public/favicon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/site/src/components/login/auth-form.tsx b/site/src/components/login/auth-form.tsx index a8e2c04..ce60547 100644 --- a/site/src/components/login/auth-form.tsx +++ b/site/src/components/login/auth-form.tsx @@ -10,12 +10,11 @@ import { Dialog, DialogContent, DialogTrigger, - DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog' -import { useEffect, useState } from 'react' -import { AuthProviderInfo } from 'pocketbase' +import { useState } from 'react' +import { AuthMethodsList } from 'pocketbase' import { Link } from '../router' const honeypot = v.literal('') @@ -57,26 +56,16 @@ const showLoginFaliedToast = () => { export function UserAuthForm({ className, isFirstRun, + authMethods, ...props }: { className?: string isFirstRun: boolean + authMethods: AuthMethodsList }) { const [isLoading, setIsLoading] = useState(false) const [isGitHubLoading, setIsOauthLoading] = useState(false) const [errors, setErrors] = useState>({}) - const [authProviders, setAuthProviders] = useState([]) - - useEffect(() => { - pb.collection('users') - .listAuthMethods() - .then((methods) => { - console.log('methods', methods) - console.log('password active', methods.emailPassword) - setAuthProviders(methods.authProviders) - console.log('auth providers', authProviders) - }) - }, []) async function handleSubmit(e: React.FormEvent) { e.preventDefault() @@ -130,120 +119,133 @@ export function UserAuthForm({ } } + if (!authMethods) { + return null + } + return (
-
setErrors({})}> -
- {isFirstRun && ( -
- - - - {errors?.username &&

{errors.username}

} -
- )} -
- - - - {errors?.email &&

{errors.email}

} -
-
- - - - {errors?.password &&

{errors.password}

} -
- {isFirstRun && ( -
- - - - {errors?.passwordConfirm && ( -

{errors.passwordConfirm}

+ {authMethods.emailPassword && ( + <> + setErrors({})}> +
+ {isFirstRun && ( +
+ + + + {errors?.username && ( +

{errors.username}

+ )} +
)} +
+ + + + {errors?.email &&

{errors.email}

} +
+
+ + + + {errors?.password &&

{errors.password}

} +
+ {isFirstRun && ( +
+ + + + {errors?.passwordConfirm && ( +

{errors.passwordConfirm}

+ )} +
+ )} +
+ {/* honeypot */} + + +
+ +
+ +
+
+ +
+
+ Or continue with
- )} -
- {/* honeypot */} - -
- -
- -
-
- -
-
- Or continue with -
-
+ + )} - {authProviders.length > 0 && ( -
- {authProviders.map((provider) => ( + {authMethods.authProviders.length > 0 && ( +
+ {authMethods.authProviders.map((provider) => (
)} - {!authProviders.length && ( + {!authMethods.authProviders.length && ( )} - - Forgot password? - + + {authMethods.emailPassword && ( + + Forgot password? + + )}
) } diff --git a/site/src/components/login/forgot-pass-form.tsx b/site/src/components/login/forgot-pass-form.tsx index dc00fea..97a304c 100644 --- a/site/src/components/login/forgot-pass-form.tsx +++ b/site/src/components/login/forgot-pass-form.tsx @@ -1,4 +1,4 @@ -import { LoaderCircle, MailIcon, SendIcon } from 'lucide-react' +import { LoaderCircle, MailIcon, SendHorizonalIcon } from 'lucide-react' import { Input } from '../ui/input' import { Label } from '../ui/label' import { useCallback, useState } from 'react' @@ -70,7 +70,7 @@ export default function ForgotPassword() { {isLoading ? ( ) : ( - + )} Reset password @@ -78,7 +78,7 @@ export default function ForgotPassword() { - diff --git a/site/src/components/login/login.tsx b/site/src/components/login/login.tsx index e0001fa..c3755bc 100644 --- a/site/src/components/login/login.tsx +++ b/site/src/components/login/login.tsx @@ -5,10 +5,12 @@ import { pb } from '@/lib/stores' import { useStore } from '@nanostores/react' import ForgotPassword from './forgot-pass-form' import { $router } from '../router' +import { AuthMethodsList } from 'pocketbase' export default function () { const page = useStore($router) const [isFirstRun, setFirstRun] = useState(false) + const [authMethods, setAuthMethods] = useState() useEffect(() => { document.title = 'Login / Beszel' @@ -18,6 +20,14 @@ export default function () { }) }, []) + useEffect(() => { + pb.collection('users') + .listAuthMethods() + .then((methods) => { + setAuthMethods(methods) + }) + }, []) + const subtitle = useMemo(() => { if (isFirstRun) { return 'Please create an admin account' @@ -28,9 +38,13 @@ export default function () { } }, [isFirstRun, page]) + if (!authMethods) { + return null + } + return (
-
+

@@ -41,7 +55,7 @@ export default function () { {page?.path === '/forgot-password' ? ( ) : ( - + )}