mirror of
https://github.com/portainer/portainer.git
synced 2025-07-28 01:39:39 +02:00
feat(global): add authentication support with single admin account
This commit is contained in:
parent
1e5207517d
commit
4e77c72fa2
35 changed files with 1475 additions and 220 deletions
88
api/auth.go
Normal file
88
api/auth.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/asaskevich/govalidator"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
credentials struct {
|
||||
Username string `valid:"alphanum,required"`
|
||||
Password string `valid:"length(8)"`
|
||||
}
|
||||
authResponse struct {
|
||||
JWT string `json:"jwt"`
|
||||
}
|
||||
)
|
||||
|
||||
func hashPassword(password string) (string, error) {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return string(hash), nil
|
||||
}
|
||||
|
||||
func checkPasswordValidity(password string, hash string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
}
|
||||
|
||||
// authHandler defines a handler function used to authenticate users
|
||||
func (api *api) authHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
w.Header().Set("Allow", "POST")
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to parse request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var credentials credentials
|
||||
err = json.Unmarshal(body, &credentials)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to parse credentials", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = govalidator.ValidateStruct(credentials)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid credentials format", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var username = credentials.Username
|
||||
var password = credentials.Password
|
||||
u, err := api.dataStore.getUserByUsername(username)
|
||||
if err != nil {
|
||||
log.Printf("User not found: %s", username)
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
err = checkPasswordValidity(password, u.Password)
|
||||
if err != nil {
|
||||
log.Printf("Invalid credentials for user: %s", username)
|
||||
http.Error(w, "Invalid credentials", http.StatusUnprocessableEntity)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := api.generateJWTToken(username)
|
||||
if err != nil {
|
||||
log.Printf("Unable to generate JWT token: %s", err.Error())
|
||||
http.Error(w, "Unable to generate JWT token", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response := authResponse{
|
||||
JWT: token,
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue