zrhoffman commented on code in PR #7110:
URL: https://github.com/apache/trafficcontrol/pull/7110#discussion_r1131409243
##########
traffic_ops/traffic_ops_golang/login/login.go:
##########
@@ -108,135 +108,184 @@ Subject: {{.InstanceName}} Password Reset Request` +
"\r\n\r" + `
</html>
`))
+// LoginHandler first attempts to verify and parse user information from an
optionally
+// provided client TLS certificate. If it fails at any point, it will fall
back and
+// continue with the standard submitted form authentication.
func LoginHandler(db *sqlx.DB, cfg config.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
authenticated := false
form := auth.PasswordForm{}
- if err := json.NewDecoder(r.Body).Decode(&form); err != nil {
- api.HandleErr(w, r, nil, http.StatusBadRequest, err,
nil)
- return
- }
- if form.Username == "" || form.Password == "" {
- api.HandleErr(w, r, nil, http.StatusBadRequest,
errors.New("username and password are required"), nil)
- return
- }
- resp := struct {
- tc.Alerts
- }{}
+ var resp tc.Alerts
dbCtx, cancelTx := context.WithTimeout(r.Context(),
time.Duration(cfg.DBQueryTimeoutSeconds)*time.Second)
defer cancelTx()
- userAllowed, err, blockingErr :=
auth.CheckLocalUserIsAllowed(form, db, dbCtx)
- if blockingErr != nil {
- api.HandleErr(w, r, nil, http.StatusServiceUnavailable,
nil, fmt.Errorf("error checking local user password: %s\n",
blockingErr.Error()))
- return
- }
- if err != nil {
- log.Errorf("checking local user: %s\n", err.Error())
+
+ // Attempt to perform client certificate authentication. If
fails, goto standard form auth. If the
+ // certificate was verified, has a UID, and the UID matches an
existing user we consider this to
+ // be a successful login.
+ {
+ // No certs provided by the client. Skip to form
authentication
+ if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 {
+ goto FormAuth
+ }
+
+ // If no configuration is set, skip to form auth
+ if cfg.ClientCertAuth == nil ||
len(cfg.ClientCertAuth.RootCertsDir) == 0 {
+ goto FormAuth
+ }
+
+ // Perform certificate verification to ensure it is
valid against Root CAs
+ err := auth.VerifyClientCertificate(r,
cfg.ClientCertAuth.RootCertsDir)
+ if err != nil {
+ log.Warnf("ClientCertAuth: error attempting to
verify client provided TLS certificate. err: %s\n", err)
+ goto FormAuth
+ }
+
+ // Client provided a verified certificate. Extract UID
value.
+ form.Username =
auth.ParseClientCertificateUID(r.TLS.PeerCertificates[0])
+ if len(form.Username) == 0 {
+ log.Infoln("ClientCertAuth: client provided
certificate did not contain a UID object identifier or value")
+ goto FormAuth
+ }
+
+ // Check if user exists locally (TODB) and has a role.
+ var blockingErr error
+ authenticated, err, blockingErr =
auth.CheckLocalUserIsAllowed(form.Username, db, dbCtx)
+ if blockingErr != nil {
+ api.HandleErr(w, r, nil,
http.StatusServiceUnavailable, nil, fmt.Errorf("error checking local user has
role: %s", blockingErr.Error()))
+ return
+ }
+ if err != nil {
+ log.Warnf("ClientCertAuth: checking local user:
%s\n", err.Error())
+ }
+
+ // Check LDAP if enabled
+ if !authenticated && cfg.LDAPEnabled {
+ _, authenticated, err =
auth.LookupUserDN(form.Username, cfg.ConfigLDAP)
+ if err != nil {
+ log.Warnf("ClientCertAuth: checking
ldap user: %s\n", err.Error())
+ }
+ }
}
Review Comment:
Fixed in #7110
##########
traffic_ops/traffic_ops_golang/login/login.go:
##########
@@ -108,135 +108,184 @@ Subject: {{.InstanceName}} Password Reset Request` +
"\r\n\r" + `
</html>
`))
+// LoginHandler first attempts to verify and parse user information from an
optionally
+// provided client TLS certificate. If it fails at any point, it will fall
back and
+// continue with the standard submitted form authentication.
func LoginHandler(db *sqlx.DB, cfg config.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
authenticated := false
form := auth.PasswordForm{}
- if err := json.NewDecoder(r.Body).Decode(&form); err != nil {
- api.HandleErr(w, r, nil, http.StatusBadRequest, err,
nil)
- return
- }
- if form.Username == "" || form.Password == "" {
- api.HandleErr(w, r, nil, http.StatusBadRequest,
errors.New("username and password are required"), nil)
- return
- }
- resp := struct {
- tc.Alerts
- }{}
+ var resp tc.Alerts
dbCtx, cancelTx := context.WithTimeout(r.Context(),
time.Duration(cfg.DBQueryTimeoutSeconds)*time.Second)
defer cancelTx()
- userAllowed, err, blockingErr :=
auth.CheckLocalUserIsAllowed(form, db, dbCtx)
- if blockingErr != nil {
- api.HandleErr(w, r, nil, http.StatusServiceUnavailable,
nil, fmt.Errorf("error checking local user password: %s\n",
blockingErr.Error()))
- return
- }
- if err != nil {
- log.Errorf("checking local user: %s\n", err.Error())
+
+ // Attempt to perform client certificate authentication. If
fails, goto standard form auth. If the
+ // certificate was verified, has a UID, and the UID matches an
existing user we consider this to
+ // be a successful login.
+ {
+ // No certs provided by the client. Skip to form
authentication
+ if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 {
+ goto FormAuth
+ }
+
+ // If no configuration is set, skip to form auth
+ if cfg.ClientCertAuth == nil ||
len(cfg.ClientCertAuth.RootCertsDir) == 0 {
+ goto FormAuth
+ }
+
+ // Perform certificate verification to ensure it is
valid against Root CAs
+ err := auth.VerifyClientCertificate(r,
cfg.ClientCertAuth.RootCertsDir)
+ if err != nil {
+ log.Warnf("ClientCertAuth: error attempting to
verify client provided TLS certificate. err: %s\n", err)
+ goto FormAuth
+ }
+
+ // Client provided a verified certificate. Extract UID
value.
+ form.Username =
auth.ParseClientCertificateUID(r.TLS.PeerCertificates[0])
+ if len(form.Username) == 0 {
+ log.Infoln("ClientCertAuth: client provided
certificate did not contain a UID object identifier or value")
+ goto FormAuth
+ }
+
+ // Check if user exists locally (TODB) and has a role.
+ var blockingErr error
+ authenticated, err, blockingErr =
auth.CheckLocalUserIsAllowed(form.Username, db, dbCtx)
+ if blockingErr != nil {
+ api.HandleErr(w, r, nil,
http.StatusServiceUnavailable, nil, fmt.Errorf("error checking local user has
role: %s", blockingErr.Error()))
+ return
+ }
+ if err != nil {
+ log.Warnf("ClientCertAuth: checking local user:
%s\n", err.Error())
Review Comment:
Fixed in #7110
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]