package server import ( "html/template" "net/http" ) // The pages: sign-in, the vault list, the administration pages in // pagesadmin.go, and the three one-shot pages a browser is the right place // for — approving a device, redeeming an invite and finishing an OIDC round // trip. There is no JavaScript anywhere in them: every control is a form, // which is what makes the whole interface work in a locked-down browser and // keeps the security surface down to the CSRF token. var pages = template.Must(template.New("layout").Parse(` {{define "layout"}} {{.Title}} · KeepOrg
{{if .Nav}}{{end}}{{template "body" .}}
{{end}} `)) var deviceTmpl = template.Must(template.Must(pages.Clone()).Parse(` {{define "body"}}

Approve this device

Sign in to let the client on your device sync your databases.

{{if .Error}}

{{.Error}}

{{end}}
{{if .OIDC}}Sign in with single sign-on →{{end}} {{end}}`)) var inviteTmpl = template.Must(template.Must(pages.Clone()).Parse(` {{define "body"}}

Create your account

You were invited to a KeepOrg server.

{{if .Error}}

{{.Error}}

{{end}}
{{end}}`)) var signInTmpl = template.Must(template.Must(pages.Clone()).Parse(` {{define "body"}}

KeepOrg

Sign in to create and manage your vaults.

{{if .Error}}

{{.Error}}

{{end}}
{{if .OIDC}}Sign in with single sign-on →{{end}} {{end}}`)) var vaultsTmpl = template.Must(template.Must(pages.Clone()).Parse(` {{define "body"}}

Your vaults

The files your KeepOrg client keeps in sync.

{{if .Error}}

{{.Error}}

{{end}} {{if .Notice}}

{{.Notice}}

{{end}} {{if .Vaults}}
{{range .Vaults}}{{end}}
VaultAccessOn conflictVersionLast saved
{{.Name}}.kdbx {{.Perm}} {{.Strategy}} {{.Version}} {{.Updated}}{{if .By}}
by {{.By}}{{end}}
{{else}}

No vaults yet. Create one below, then open it from the KeepOrg client on your computer or phone.

{{end}} {{if .Groups}}
Create a vault

This password encrypts the vault itself and is what everyone with access types into KeePass. It is not your account password, and it cannot be recovered: write it down before you continue.

Merging requires the server to keep this password, so a merge vault can be read by whoever controls the server. Any other setting keeps the key off the server.

{{else}}

You do not administer a group yet, so there is nowhere to put a vault. Ask an administrator to add you to one.

{{end}}
Change your account password

This is the password you sign in with, not any vault's password. Changing it signs out your other sessions.

{{end}}`)) var doneTmpl = template.Must(template.Must(pages.Clone()).Parse(` {{define "body"}}

{{.Title}}

{{.Message}}

{{end}}`)) func (s *Server) renderDevice(w http.ResponseWriter, code, errMsg string) { render(w, deviceTmpl, map[string]any{ "Title": "Approve device", "Code": code, "Error": errMsg, "OIDC": s.oidc != nil, }) } func (s *Server) renderInvite(w http.ResponseWriter, token, errMsg string) { render(w, inviteTmpl, map[string]any{"Title": "Create account", "Token": token, "Error": errMsg}) } func (s *Server) renderSignIn(w http.ResponseWriter, errMsg string) { render(w, signInTmpl, map[string]any{ "Title": "Sign in", "Error": errMsg, "OIDC": s.oidc != nil, }) } func (s *Server) renderVaults(w http.ResponseWriter, sess *webSession, errMsg, notice string) { vaults, groups := s.vaultRows(sess.user) d := s.shell(sess, "Vaults", "Vaults") d["Vaults"], d["Groups"], d["Error"], d["Notice"] = vaults, groups, errMsg, notice render(w, vaultsTmpl, d) } func (s *Server) renderDone(w http.ResponseWriter, title, msg string) { render(w, doneTmpl, map[string]any{"Title": title, "Message": msg}) } func render(w http.ResponseWriter, t *template.Template, data map[string]any) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Referrer-Policy", "no-referrer") _ = t.ExecuteTemplate(w, "layout", data) }