Add Certificate helper function

This commit is contained in:
Adnan Maolood 2020-10-21 17:47:34 -04:00
parent 8c4c00b31a
commit 1634c2c11c
2 changed files with 84 additions and 73 deletions

View File

@ -33,19 +33,19 @@ var (
) )
func main() { func main() {
handler := &gmi.ServeMux{} var mux gmi.ServeMux
handler.HandleFunc("/", welcome) mux.HandleFunc("/", welcome)
handler.HandleFunc("/login", login) mux.HandleFunc("/login", login)
handler.HandleFunc("/login/password", loginPassword) mux.HandleFunc("/login/password", loginPassword)
handler.HandleFunc("/profile", profile) mux.HandleFunc("/profile", profile)
handler.HandleFunc("/admin", admin) mux.HandleFunc("/admin", admin)
handler.HandleFunc("/logout", logout) mux.HandleFunc("/logout", logout)
server := &gmi.Server{} var server gmi.Server
if err := server.CertificateStore.Load("/var/lib/gemini/certs"); err != nil { if err := server.CertificateStore.Load("/var/lib/gemini/certs"); err != nil {
log.Fatal(err) log.Fatal(err)
} }
server.Register("localhost", handler) server.Register("localhost", &mux)
if err := server.ListenAndServe(); err != nil { if err := server.ListenAndServe(); err != nil {
log.Fatal(err) log.Fatal(err)
@ -59,78 +59,90 @@ func getSession(crt *x509.Certificate) (*session, bool) {
} }
func welcome(w *gmi.ResponseWriter, r *gmi.Request) { func welcome(w *gmi.ResponseWriter, r *gmi.Request) {
w.Write([]byte("Welcome to this example.\n=> /login Login\n")) fmt.Fprintln(w, "Welcome to this example.")
fmt.Fprintln(w, "=> /login Login")
} }
func login(w *gmi.ResponseWriter, r *gmi.Request) { func login(w *gmi.ResponseWriter, r *gmi.Request) {
gmi.WithCertificate(w, r, func(cert *x509.Certificate) { cert, ok := gmi.Certificate(w, r)
username, ok := gmi.Input(w, r, "Username") if !ok {
if !ok { return
return }
} username, ok := gmi.Input(w, r, "Username")
fingerprint := gmi.Fingerprint(cert) if !ok {
sessions[fingerprint] = &session{ return
username: username, }
} fingerprint := gmi.Fingerprint(cert)
gmi.Redirect(w, r, "/login/password") sessions[fingerprint] = &session{
}) username: username,
}
gmi.Redirect(w, r, "/login/password")
} }
func loginPassword(w *gmi.ResponseWriter, r *gmi.Request) { func loginPassword(w *gmi.ResponseWriter, r *gmi.Request) {
gmi.WithCertificate(w, r, func(cert *x509.Certificate) { cert, ok := gmi.Certificate(w, r)
session, ok := getSession(cert) if !ok {
if !ok { return
gmi.CertificateNotAuthorized(w, r) }
return session, ok := getSession(cert)
} if !ok {
gmi.CertificateNotAuthorized(w, r)
return
}
password, ok := gmi.SensitiveInput(w, r, "Password") password, ok := gmi.SensitiveInput(w, r, "Password")
if !ok { if !ok {
return return
} }
expected := logins[session.username].password expected := logins[session.username].password
if password == expected { if password == expected {
session.authorized = true session.authorized = true
gmi.Redirect(w, r, "/profile") gmi.Redirect(w, r, "/profile")
} else { } else {
gmi.SensitiveInput(w, r, "Wrong password. Try again") gmi.SensitiveInput(w, r, "Wrong password. Try again")
} }
})
} }
func logout(w *gmi.ResponseWriter, r *gmi.Request) { func logout(w *gmi.ResponseWriter, r *gmi.Request) {
gmi.WithCertificate(w, r, func(cert *x509.Certificate) { cert, ok := gmi.Certificate(w, r)
fingerprint := gmi.Fingerprint(cert) if !ok {
delete(sessions, fingerprint) return
}) }
w.Write([]byte("Successfully logged out.\n")) fingerprint := gmi.Fingerprint(cert)
delete(sessions, fingerprint)
fmt.Fprintln(w, "Successfully logged out.")
} }
func profile(w *gmi.ResponseWriter, r *gmi.Request) { func profile(w *gmi.ResponseWriter, r *gmi.Request) {
gmi.WithCertificate(w, r, func(cert *x509.Certificate) { cert, ok := gmi.Certificate(w, r)
session, ok := getSession(cert) if !ok {
if !ok { return
gmi.CertificateNotAuthorized(w, r) }
return session, ok := getSession(cert)
} if !ok {
user := logins[session.username] gmi.CertificateNotAuthorized(w, r)
profile := fmt.Sprintf("Username: %s\nAdmin: %t\n=> /logout Logout", session.username, user.admin) return
w.Write([]byte(profile)) }
}) user := logins[session.username]
fmt.Fprintln(w, "Username:", session.username)
fmt.Fprintln(w, "Admin:", user.admin)
fmt.Fprintln(w, "=> /logout Logout")
} }
func admin(w *gmi.ResponseWriter, r *gmi.Request) { func admin(w *gmi.ResponseWriter, r *gmi.Request) {
gmi.WithCertificate(w, r, func(cert *x509.Certificate) { cert, ok := gmi.Certificate(w, r)
session, ok := getSession(cert) if !ok {
if !ok { return
gmi.CertificateNotAuthorized(w, r) }
return session, ok := getSession(cert)
} if !ok {
user := logins[session.username] gmi.CertificateNotAuthorized(w, r)
if !user.admin { return
gmi.CertificateNotAuthorized(w, r) }
return user := logins[session.username]
} if !user.admin {
w.Write([]byte("Welcome to the admin portal.\n")) gmi.CertificateNotAuthorized(w, r)
}) return
}
fmt.Fprintln(w, "Welcome to the admin portal.")
} }

View File

@ -311,15 +311,14 @@ func CertificateNotAuthorized(w *ResponseWriter, r *Request) {
w.WriteHeader(StatusCertificateNotAuthorized, "Certificate not authorized") w.WriteHeader(StatusCertificateNotAuthorized, "Certificate not authorized")
} }
// WithCertificate either responds with CertificateRequired if the client did // Certificate returns the request certificate. If one is not provided,
// not provide a certificate, or calls f with the first ceritificate provided. // it returns nil and responds with StatusCertificateRequired.
func WithCertificate(w *ResponseWriter, r *Request, f func(*x509.Certificate)) { func Certificate(w *ResponseWriter, r *Request) (*x509.Certificate, bool) {
if len(r.TLS.PeerCertificates) == 0 { if len(r.TLS.PeerCertificates) == 0 {
CertificateRequired(w, r) CertificateRequired(w, r)
return return nil, false
} }
cert := r.TLS.PeerCertificates[0] return r.TLS.PeerCertificates[0], true
f(cert)
} }
// ResponderFunc is a wrapper around a bare function that implements Handler. // ResponderFunc is a wrapper around a bare function that implements Handler.