-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(authn): Basic Autentication support
- Loading branch information
Showing
9 changed files
with
158 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package helper | ||
|
||
import ( | ||
"crypto/sha1" | ||
"encoding/base64" | ||
"encoding/csv" | ||
"regexp" | ||
|
||
"golang.org/x/crypto/bcrypt" | ||
) | ||
|
||
var ( | ||
shaRe = regexp.MustCompile(`^{SHA}`) | ||
bcrRe = regexp.MustCompile(`^\$2b\$|^\$2a\$|^\$2y\$`) | ||
) | ||
|
||
// HtpasswdFile is a map for usernames to passwords. | ||
type HtpasswdFile struct { | ||
location string | ||
users map[string]string | ||
} | ||
|
||
// newHtpasswdFromFile reads the users and passwords from a htpasswd file and returns them. | ||
func NewHtpasswdFromFile(location string) (*HtpasswdFile, error) { | ||
r, err := OpenResource(location) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer r.Close() | ||
|
||
cr := csv.NewReader(r) | ||
cr.Comma = ':' | ||
cr.Comment = '#' | ||
cr.TrimLeadingSpace = true | ||
|
||
records, err := cr.ReadAll() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
users := make(map[string]string) | ||
for _, record := range records { | ||
users[record[0]] = record[1] | ||
} | ||
|
||
return &HtpasswdFile{ | ||
location: location, | ||
users: users, | ||
}, nil | ||
} | ||
|
||
func (h *HtpasswdFile) Authenticate(username string, password string) bool { | ||
pwd, exists := h.users[username] | ||
if !exists { | ||
return false | ||
} | ||
|
||
switch { | ||
case shaRe.MatchString(pwd): | ||
d := sha1.New() | ||
_, _ = d.Write([]byte(password)) | ||
if pwd[5:] == base64.StdEncoding.EncodeToString(d.Sum(nil)) { | ||
return true | ||
} | ||
case bcrRe.MatchString(pwd): | ||
err := bcrypt.CompareHashAndPassword([]byte(pwd), []byte(password)) | ||
if err == nil { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,30 @@ | ||
package middleware | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/rs/zerolog/log" | ||
) | ||
|
||
const tpl = "using %s as authentication backend" | ||
const usingAuthNMsg = "using authentication" | ||
|
||
// Auth is a middleware to authenticate HTTP request | ||
func Auth(method string) Middleware { | ||
switch method { | ||
case "mock": | ||
log.Info().Msg(fmt.Sprintf(tpl, "Mock")) | ||
switch { | ||
case method == "mock": | ||
log.Info().Str("method", method).Msg(usingAuthNMsg) | ||
return MockAuth | ||
case "proxy": | ||
log.Info().Msg(fmt.Sprintf(tpl, "Proxy")) | ||
case method == "proxy": | ||
log.Info().Str("method", method).Msg(usingAuthNMsg) | ||
return ProxyAuth | ||
default: | ||
log.Info().Str("authority", method).Msg(fmt.Sprintf(tpl, "OpenID Connect")) | ||
case strings.HasPrefix(method, "file://"): | ||
log.Info().Str("method", "basic").Str("htpasswd", method).Msg(usingAuthNMsg) | ||
return BasicAuth(method) | ||
case strings.HasPrefix(method, "https://"): | ||
log.Info().Str("method", "bearer").Str("authority", method).Msg(usingAuthNMsg) | ||
return OpenIDConnectJWTAuth(method) | ||
default: | ||
log.Fatal().Str("method", method).Msg("non supported authentication method") | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package middleware | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
|
||
"github.com/ncarlier/readflow/pkg/constant" | ||
"github.com/ncarlier/readflow/pkg/helper" | ||
"github.com/ncarlier/readflow/pkg/service" | ||
"github.com/rs/zerolog/log" | ||
) | ||
|
||
// BasicAuth is a middleware to checks HTTP request credentials from Basic AuthN method | ||
func BasicAuth(location string) Middleware { | ||
htpasswd, err := helper.NewHtpasswdFromFile(location) | ||
if err != nil { | ||
log.Fatal().Err(err).Str("location", location).Msg("unable to read htpasswd file") | ||
} | ||
|
||
return func(inner http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
ctx := r.Context() | ||
username, password, ok := r.BasicAuth() | ||
if ok && htpasswd.Authenticate(username, password) { | ||
user, err := service.Lookup().GetOrRegisterUser(ctx, username) | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return | ||
} | ||
ctx = context.WithValue(ctx, constant.ContextUser, *user) | ||
ctx = context.WithValue(ctx, constant.ContextUserID, *user.ID) | ||
ctx = context.WithValue(ctx, constant.ContextIsAdmin, false) | ||
inner.ServeHTTP(w, r.WithContext(ctx)) | ||
return | ||
} | ||
w.Header().Set("WWW-Authenticate", `Basic realm="readflow", charset="UTF-8"`) | ||
jsonErrors(w, "Unauthorized", 401) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
demo:$2y$05$pyVCV7lwL1Scis6Lz.KyZuS9..KCD2y7dhKBkEzXlR9RH3VVNqdLG |