2019-05-06 01:40:41 +03:00
|
|
|
package middlewares
|
|
|
|
|
|
|
|
import (
|
2021-10-11 10:58:29 +03:00
|
|
|
"fmt"
|
2021-10-11 10:10:30 +03:00
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
2020-10-16 17:11:14 +03:00
|
|
|
conf "github.com/muety/wakapi/config"
|
2021-01-22 01:19:17 +03:00
|
|
|
"github.com/muety/wakapi/models"
|
|
|
|
"github.com/muety/wakapi/services"
|
2020-05-24 14:41:19 +03:00
|
|
|
"github.com/muety/wakapi/utils"
|
2019-05-06 01:40:41 +03:00
|
|
|
)
|
|
|
|
|
2021-10-11 11:00:48 +03:00
|
|
|
const (
|
|
|
|
// queryApiKey is the query parameter name for api key.
|
|
|
|
queryApiKey = "api_key"
|
|
|
|
)
|
|
|
|
|
2021-10-11 10:58:29 +03:00
|
|
|
var (
|
|
|
|
errEmptyKey = fmt.Errorf("the api_key is empty")
|
|
|
|
)
|
|
|
|
|
2019-05-06 01:40:41 +03:00
|
|
|
type AuthenticateMiddleware struct {
|
2021-02-06 22:09:08 +03:00
|
|
|
config *conf.Config
|
|
|
|
userSrvc services.IUserService
|
|
|
|
optionalForPaths []string
|
2021-02-12 20:37:30 +03:00
|
|
|
redirectTarget string // optional
|
2019-05-21 15:23:41 +03:00
|
|
|
}
|
|
|
|
|
2021-02-06 22:09:08 +03:00
|
|
|
func NewAuthenticateMiddleware(userService services.IUserService) *AuthenticateMiddleware {
|
2020-05-24 14:41:19 +03:00
|
|
|
return &AuthenticateMiddleware{
|
2021-02-06 22:09:08 +03:00
|
|
|
config: conf.Get(),
|
|
|
|
userSrvc: userService,
|
|
|
|
optionalForPaths: []string{},
|
2019-05-21 15:23:41 +03:00
|
|
|
}
|
2019-05-06 01:40:41 +03:00
|
|
|
}
|
|
|
|
|
2021-02-07 00:32:03 +03:00
|
|
|
func (m *AuthenticateMiddleware) WithOptionalFor(paths []string) *AuthenticateMiddleware {
|
2021-02-06 22:09:08 +03:00
|
|
|
m.optionalForPaths = paths
|
2021-02-07 00:32:03 +03:00
|
|
|
return m
|
2021-02-06 22:09:08 +03:00
|
|
|
}
|
|
|
|
|
2021-02-12 20:37:30 +03:00
|
|
|
func (m *AuthenticateMiddleware) WithRedirectTarget(path string) *AuthenticateMiddleware {
|
|
|
|
m.redirectTarget = path
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2020-05-24 15:50:04 +03:00
|
|
|
func (m *AuthenticateMiddleware) Handler(h http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
m.ServeHTTP(w, r, h.ServeHTTP)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *AuthenticateMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
2019-05-21 23:40:59 +03:00
|
|
|
var user *models.User
|
2020-05-24 14:41:19 +03:00
|
|
|
user, err := m.tryGetUserByCookie(r)
|
2019-05-21 23:40:59 +03:00
|
|
|
|
|
|
|
if err != nil {
|
2021-10-11 10:58:29 +03:00
|
|
|
user, err = m.tryGetUserByApiKeyHeader(r)
|
2019-05-21 23:40:59 +03:00
|
|
|
}
|
2021-10-11 10:10:30 +03:00
|
|
|
if err != nil {
|
2021-10-11 10:58:29 +03:00
|
|
|
user, err = m.tryGetUserByApiKeyQuery(r)
|
2021-10-11 10:10:30 +03:00
|
|
|
}
|
2019-05-21 23:40:59 +03:00
|
|
|
|
2021-02-06 22:09:08 +03:00
|
|
|
if err != nil || user == nil {
|
|
|
|
if m.isOptional(r.URL.Path) {
|
|
|
|
next(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-02-12 20:37:30 +03:00
|
|
|
if m.redirectTarget == "" {
|
2020-05-24 14:41:19 +03:00
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
2021-02-13 13:23:58 +03:00
|
|
|
w.Write([]byte(conf.ErrUnauthorized))
|
2020-05-24 14:41:19 +03:00
|
|
|
} else {
|
2022-01-17 10:25:29 +03:00
|
|
|
http.SetCookie(w, m.config.GetClearCookie(models.AuthCookieKey))
|
2021-02-12 20:37:30 +03:00
|
|
|
http.Redirect(w, r, m.redirectTarget, http.StatusFound)
|
2020-05-24 14:41:19 +03:00
|
|
|
}
|
2019-05-06 01:40:41 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-03-26 15:10:10 +03:00
|
|
|
SetPrincipal(r, user)
|
|
|
|
next(w, r)
|
2019-05-21 23:40:59 +03:00
|
|
|
}
|
|
|
|
|
2021-02-06 22:09:08 +03:00
|
|
|
func (m *AuthenticateMiddleware) isOptional(requestPath string) bool {
|
|
|
|
for _, p := range m.optionalForPaths {
|
|
|
|
if strings.HasPrefix(requestPath, p) || requestPath == p {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-10-11 10:58:29 +03:00
|
|
|
func (m *AuthenticateMiddleware) tryGetUserByApiKeyHeader(r *http.Request) (*models.User, error) {
|
2020-05-24 14:41:19 +03:00
|
|
|
key, err := utils.ExtractBearerAuth(r)
|
2019-05-06 01:40:41 +03:00
|
|
|
if err != nil {
|
2020-05-24 14:41:19 +03:00
|
|
|
return nil, err
|
2019-05-06 01:40:41 +03:00
|
|
|
}
|
|
|
|
|
2019-05-21 15:23:41 +03:00
|
|
|
var user *models.User
|
2020-05-24 14:41:19 +03:00
|
|
|
userKey := strings.TrimSpace(key)
|
2021-01-22 01:19:17 +03:00
|
|
|
user, err = m.userSrvc.GetUserByKey(userKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-05-06 01:40:41 +03:00
|
|
|
}
|
2020-05-24 14:41:19 +03:00
|
|
|
return user, nil
|
2019-05-21 23:40:59 +03:00
|
|
|
}
|
2019-05-06 01:40:41 +03:00
|
|
|
|
2021-10-11 10:58:29 +03:00
|
|
|
func (m *AuthenticateMiddleware) tryGetUserByApiKeyQuery(r *http.Request) (*models.User, error) {
|
2021-10-11 11:00:48 +03:00
|
|
|
key := r.URL.Query().Get(queryApiKey)
|
2021-10-11 10:10:30 +03:00
|
|
|
var user *models.User
|
|
|
|
userKey := strings.TrimSpace(key)
|
2021-10-11 10:58:29 +03:00
|
|
|
if userKey == "" {
|
|
|
|
return nil, errEmptyKey
|
|
|
|
}
|
2021-10-11 10:10:30 +03:00
|
|
|
user, err := m.userSrvc.GetUserByKey(userKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return user, nil
|
|
|
|
}
|
|
|
|
|
2020-05-24 14:41:19 +03:00
|
|
|
func (m *AuthenticateMiddleware) tryGetUserByCookie(r *http.Request) (*models.User, error) {
|
2021-01-31 16:34:54 +03:00
|
|
|
username, err := utils.ExtractCookieAuth(r, m.config)
|
2019-05-21 23:40:59 +03:00
|
|
|
if err != nil {
|
2020-05-24 14:41:19 +03:00
|
|
|
return nil, err
|
2019-05-21 23:40:59 +03:00
|
|
|
}
|
|
|
|
|
2021-01-31 16:34:54 +03:00
|
|
|
user, err := m.userSrvc.GetUserById(*username)
|
2020-05-25 23:24:29 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-31 16:34:54 +03:00
|
|
|
// no need to check password here, as securecookie decoding will fail anyway,
|
|
|
|
// if cookie is not properly signed
|
2020-05-25 23:24:29 +03:00
|
|
|
|
2020-05-24 14:41:19 +03:00
|
|
|
return user, nil
|
2019-05-06 01:40:41 +03:00
|
|
|
}
|