mirror of
https://github.com/Forceu/Gokapi.git
synced 2026-01-30 13:08:32 -06:00
132 lines
3.9 KiB
Go
132 lines
3.9 KiB
Go
package authentication
|
|
|
|
import (
|
|
"crypto/subtle"
|
|
"github.com/coreos/go-oidc/v3/oidc"
|
|
"github.com/forceu/gokapi/internal/configuration"
|
|
"github.com/forceu/gokapi/internal/models"
|
|
"github.com/forceu/gokapi/internal/webserver/authentication/sessionmanager"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
// CookieOauth is the cookie name used for login
|
|
const CookieOauth = "state"
|
|
|
|
// Internal authentication method uses a user / password combination handled by Gokapi
|
|
const Internal = 0
|
|
|
|
// OAuth2 authentication retrieves the users email with Open Connect ID
|
|
const OAuth2 = 1
|
|
|
|
// Header authentication relies on a header from a reverse proxy to parse the user name
|
|
const Header = 2
|
|
|
|
// Disabled authentication ignores all internal authentication procedures. A reverse proxy needs to restrict access
|
|
const Disabled = 3
|
|
|
|
var authSettings models.AuthenticationConfig
|
|
|
|
// Init needs to be called first to process the authentication configuration
|
|
func Init(config models.AuthenticationConfig) {
|
|
authSettings = config
|
|
}
|
|
|
|
// IsAuthenticated returns true if the user provides a valid authentication
|
|
func IsAuthenticated(w http.ResponseWriter, r *http.Request) bool {
|
|
switch authSettings.Method {
|
|
case Internal:
|
|
return isGrantedSession(w, r)
|
|
case OAuth2:
|
|
return isGrantedSession(w, r)
|
|
case Header:
|
|
return isGrantedHeader(r)
|
|
case Disabled:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isGrantedHeader returns true if the user was authenticated by a proxy header if enabled
|
|
func isGrantedHeader(r *http.Request) bool {
|
|
|
|
if authSettings.HeaderKey == "" {
|
|
return false
|
|
}
|
|
value := r.Header.Get(authSettings.HeaderKey)
|
|
if value == "" {
|
|
return false
|
|
}
|
|
if len(authSettings.HeaderUsers) == 0 {
|
|
return true
|
|
}
|
|
return isUserInArray(value, authSettings.HeaderUsers)
|
|
}
|
|
|
|
func isUserInArray(userEntered string, strArray []string) bool {
|
|
for _, user := range strArray {
|
|
if strings.ToLower(user) == strings.ToLower(userEntered) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// CheckOauthUser checks if the user is allowed to use the Gokapi instance
|
|
func CheckOauthUser(userInfo *oidc.UserInfo, w http.ResponseWriter) {
|
|
if isValidOauthUser(userInfo.Email) {
|
|
// TODO revoke session if oauth is not valid any more
|
|
sessionmanager.CreateSession(w)
|
|
redirect(w, "admin")
|
|
return
|
|
}
|
|
redirect(w, "error-auth")
|
|
}
|
|
|
|
func isValidOauthUser(name string) bool {
|
|
if name == "" {
|
|
return false
|
|
}
|
|
if len(authSettings.OauthUsers) == 0 {
|
|
return true
|
|
}
|
|
return isUserInArray(name, authSettings.OauthUsers)
|
|
}
|
|
|
|
// isGrantedSession returns true if the user holds a valid internal session cookie
|
|
func isGrantedSession(w http.ResponseWriter, r *http.Request) bool {
|
|
return sessionmanager.IsValidSession(w, r)
|
|
}
|
|
|
|
// IsCorrectUsernameAndPassword checks if a provided username and password is correct
|
|
func IsCorrectUsernameAndPassword(username, password string) bool {
|
|
return IsEqualStringConstantTime(username, authSettings.Username) &&
|
|
IsEqualStringConstantTime(configuration.HashPasswordCustomSalt(password, authSettings.SaltAdmin), authSettings.Password)
|
|
}
|
|
|
|
// IsEqualStringConstantTime uses ConstantTimeCompare to prevent timing attack.
|
|
func IsEqualStringConstantTime(s1, s2 string) bool {
|
|
return subtle.ConstantTimeCompare(
|
|
[]byte(strings.ToLower(s1)),
|
|
[]byte(strings.ToLower(s2))) == 1
|
|
}
|
|
|
|
// Sends a redirect HTTP output to the client. Variable url is used to redirect to ./url
|
|
func redirect(w http.ResponseWriter, url string) {
|
|
_, _ = io.WriteString(w, "<html><head><meta http-equiv=\"Refresh\" content=\"0; URL=./"+url+"\"></head></html>")
|
|
}
|
|
|
|
// Logout logs the user out and removes the session
|
|
func Logout(w http.ResponseWriter, r *http.Request) {
|
|
if authSettings.Method == Internal || authSettings.Method == OAuth2 {
|
|
sessionmanager.LogoutSession(w, r)
|
|
}
|
|
redirect(w, "login")
|
|
}
|
|
|
|
// IsLogoutAvailable returns true if a logout button should be shown with the current form of authentication
|
|
func IsLogoutAvailable() bool {
|
|
return authSettings.Method == Internal || authSettings.Method == OAuth2
|
|
}
|