mirror of https://github.com/portainer/portainer
113 lines
3.2 KiB
Go
113 lines
3.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"github.com/portainer/portainer"
|
|
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
|
|
"github.com/asaskevich/govalidator"
|
|
"github.com/gorilla/mux"
|
|
httperror "github.com/portainer/portainer/http/error"
|
|
"github.com/portainer/portainer/http/security"
|
|
)
|
|
|
|
// AuthHandler represents an HTTP API handler for managing authentication.
|
|
type AuthHandler struct {
|
|
*mux.Router
|
|
Logger *log.Logger
|
|
authDisabled bool
|
|
UserService portainer.UserService
|
|
CryptoService portainer.CryptoService
|
|
JWTService portainer.JWTService
|
|
}
|
|
|
|
const (
|
|
// ErrInvalidCredentialsFormat is an error raised when credentials format is not valid
|
|
ErrInvalidCredentialsFormat = portainer.Error("Invalid credentials format")
|
|
// ErrInvalidCredentials is an error raised when credentials for a user are invalid
|
|
ErrInvalidCredentials = portainer.Error("Invalid credentials")
|
|
// ErrAuthDisabled is an error raised when trying to access the authentication endpoints
|
|
// when the server has been started with the --no-auth flag
|
|
ErrAuthDisabled = portainer.Error("Authentication is disabled")
|
|
)
|
|
|
|
// NewAuthHandler returns a new instance of AuthHandler.
|
|
func NewAuthHandler(bouncer *security.RequestBouncer, authDisabled bool) *AuthHandler {
|
|
h := &AuthHandler{
|
|
Router: mux.NewRouter(),
|
|
Logger: log.New(os.Stderr, "", log.LstdFlags),
|
|
authDisabled: authDisabled,
|
|
}
|
|
h.Handle("/auth",
|
|
bouncer.PublicAccess(http.HandlerFunc(h.handlePostAuth)))
|
|
|
|
return h
|
|
}
|
|
|
|
func (handler *AuthHandler) handlePostAuth(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
httperror.WriteMethodNotAllowedResponse(w, []string{http.MethodPost})
|
|
return
|
|
}
|
|
|
|
if handler.authDisabled {
|
|
httperror.WriteErrorResponse(w, ErrAuthDisabled, http.StatusServiceUnavailable, handler.Logger)
|
|
return
|
|
}
|
|
|
|
var req postAuthRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
httperror.WriteErrorResponse(w, ErrInvalidJSON, http.StatusBadRequest, handler.Logger)
|
|
return
|
|
}
|
|
|
|
_, err := govalidator.ValidateStruct(req)
|
|
if err != nil {
|
|
httperror.WriteErrorResponse(w, ErrInvalidCredentialsFormat, http.StatusBadRequest, handler.Logger)
|
|
return
|
|
}
|
|
|
|
var username = req.Username
|
|
var password = req.Password
|
|
|
|
u, err := handler.UserService.UserByUsername(username)
|
|
if err == portainer.ErrUserNotFound {
|
|
httperror.WriteErrorResponse(w, err, http.StatusNotFound, handler.Logger)
|
|
return
|
|
} else if err != nil {
|
|
httperror.WriteErrorResponse(w, err, http.StatusInternalServerError, handler.Logger)
|
|
return
|
|
}
|
|
|
|
err = handler.CryptoService.CompareHashAndData(u.Password, password)
|
|
if err != nil {
|
|
httperror.WriteErrorResponse(w, ErrInvalidCredentials, http.StatusUnprocessableEntity, handler.Logger)
|
|
return
|
|
}
|
|
|
|
tokenData := &portainer.TokenData{
|
|
ID: u.ID,
|
|
Username: u.Username,
|
|
Role: u.Role,
|
|
}
|
|
token, err := handler.JWTService.GenerateToken(tokenData)
|
|
if err != nil {
|
|
httperror.WriteErrorResponse(w, err, http.StatusInternalServerError, handler.Logger)
|
|
return
|
|
}
|
|
|
|
encodeJSON(w, &postAuthResponse{JWT: token}, handler.Logger)
|
|
}
|
|
|
|
type postAuthRequest struct {
|
|
Username string `valid:"required"`
|
|
Password string `valid:"required"`
|
|
}
|
|
|
|
type postAuthResponse struct {
|
|
JWT string `json:"jwt"`
|
|
}
|