Feat/dashboard (#105)
This commit is contained in:
@@ -11,17 +11,19 @@ import (
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/robertkrimen/otto"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (string, int64, error) {
|
||||
t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE))
|
||||
// CreateAuthToken util to create JWT token, based on
|
||||
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
|
||||
func CreateAuthToken(user db.User, tokenType string, roles []string) (string, int64, error) {
|
||||
t := jwt.New(jwt.GetSigningMethod(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyJwtType).(string)))
|
||||
expiryBound := time.Hour
|
||||
if tokenType == enum.RefreshToken {
|
||||
if tokenType == constants.TokenTypeRefreshToken {
|
||||
// expires in 1 year
|
||||
expiryBound = time.Hour * 8760
|
||||
}
|
||||
@@ -33,12 +35,13 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
|
||||
var userMap map[string]interface{}
|
||||
json.Unmarshal(userBytes, &userMap)
|
||||
|
||||
claimKey := envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyJwtRoleClaim).(string)
|
||||
customClaims := jwt.MapClaims{
|
||||
"exp": expiresAt,
|
||||
"iat": time.Now().Unix(),
|
||||
"token_type": tokenType.String(),
|
||||
"allowed_roles": strings.Split(user.Roles, ","),
|
||||
constants.EnvData.JWT_ROLE_CLAIM: roles,
|
||||
"exp": expiresAt,
|
||||
"iat": time.Now().Unix(),
|
||||
"token_type": tokenType,
|
||||
"allowed_roles": strings.Split(user.Roles, ","),
|
||||
claimKey: roles,
|
||||
}
|
||||
|
||||
for k, v := range userMap {
|
||||
@@ -48,7 +51,7 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
|
||||
}
|
||||
|
||||
// check for the extra access token script
|
||||
accessTokenScript := os.Getenv("CUSTOM_ACCESS_TOKEN_SCRIPT")
|
||||
accessTokenScript := os.Getenv(constants.EnvKeyCustomAccessTokenScript)
|
||||
if accessTokenScript != "" {
|
||||
vm := otto.New()
|
||||
|
||||
@@ -79,7 +82,7 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
|
||||
|
||||
t.Claims = customClaims
|
||||
|
||||
token, err := t.SignedString([]byte(constants.EnvData.JWT_SECRET))
|
||||
token, err := t.SignedString([]byte(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyJwtSecret).(string)))
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
@@ -87,6 +90,8 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (st
|
||||
return token, expiresAt, nil
|
||||
}
|
||||
|
||||
// GetAuthToken helps in getting the JWT token from the
|
||||
// request cookie or authorization header
|
||||
func GetAuthToken(gc *gin.Context) (string, error) {
|
||||
token, err := GetCookie(gc)
|
||||
if err != nil || token == "" {
|
||||
@@ -101,12 +106,13 @@ func GetAuthToken(gc *gin.Context) (string, error) {
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// VerifyAuthToken helps in verifying the JWT token
|
||||
func VerifyAuthToken(token string) (map[string]interface{}, error) {
|
||||
var res map[string]interface{}
|
||||
claims := jwt.MapClaims{}
|
||||
|
||||
_, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(constants.EnvData.JWT_SECRET), nil
|
||||
return []byte(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyJwtSecret).(string)), nil
|
||||
})
|
||||
if err != nil {
|
||||
return res, err
|
||||
@@ -126,10 +132,12 @@ func VerifyAuthToken(token string) (map[string]interface{}, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, error) {
|
||||
return HashPassword(constants.EnvData.ADMIN_SECRET)
|
||||
// CreateAdminAuthToken creates the admin token based on secret key
|
||||
func CreateAdminAuthToken(tokenType string, c *gin.Context) (string, error) {
|
||||
return EncryptPassword(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string))
|
||||
}
|
||||
|
||||
// GetAdminAuthToken helps in getting the admin token from the request cookie
|
||||
func GetAdminAuthToken(gc *gin.Context) (string, error) {
|
||||
token, err := GetAdminCookie(gc)
|
||||
if err != nil || token == "" {
|
||||
@@ -143,7 +151,7 @@ func GetAdminAuthToken(gc *gin.Context) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(decodedValue), []byte(constants.EnvData.ADMIN_SECRET))
|
||||
err = bcrypt.CompareHashAndPassword([]byte(decodedValue), []byte(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string)))
|
||||
log.Println("error comparing hash:", err)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf(`unauthorized`)
|
||||
|
@@ -1,24 +1,11 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func WriteToFile(filename string, data string) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.WriteString(file, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return file.Sync()
|
||||
}
|
||||
|
||||
// StringSliceContains checks if a string slice contains a particular string
|
||||
func StringSliceContains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
@@ -27,3 +14,15 @@ func StringSliceContains(s []string, e string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SaveSessionInDB saves sessions generated for a given user with meta information
|
||||
// Not store token here as that could be security breach
|
||||
func SaveSessionInDB(userId string, c *gin.Context) {
|
||||
sessionData := db.Session{
|
||||
UserID: userId,
|
||||
UserAgent: GetUserAgent(c.Request),
|
||||
IP: GetIP(c.Request),
|
||||
}
|
||||
|
||||
db.Mgr.AddSession(sessionData)
|
||||
}
|
||||
|
@@ -4,27 +4,33 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SetCookie sets the cookie in the response. It sets 2 cookies
|
||||
// 1 COOKIE_NAME for the host (abc.com)
|
||||
// 2 COOKIE_NAME-client for the domain (sub.abc.com).
|
||||
// Note all sites don't allow 2nd type of cookie
|
||||
func SetCookie(gc *gin.Context, token string) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
|
||||
domain := GetDomainName(constants.EnvData.AUTHORIZER_URL)
|
||||
host, _ := GetHostParts(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string))
|
||||
domain := GetDomainName(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string))
|
||||
if domain != "localhost" {
|
||||
domain = "." + domain
|
||||
}
|
||||
|
||||
gc.SetSameSite(http.SameSiteNoneMode)
|
||||
gc.SetCookie(constants.EnvData.COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(constants.EnvData.COOKIE_NAME+"-client", token, 3600, "/", domain, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string), token, 3600, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string)+"-client", token, 3600, "/", domain, secure, httpOnly)
|
||||
}
|
||||
|
||||
// GetCookie gets the cookie from the request
|
||||
func GetCookie(gc *gin.Context) (string, error) {
|
||||
cookie, err := gc.Request.Cookie(constants.EnvData.COOKIE_NAME)
|
||||
cookie, err := gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string))
|
||||
if err != nil {
|
||||
cookie, err = gc.Request.Cookie(constants.EnvData.COOKIE_NAME + "-client")
|
||||
cookie, err = gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string) + "-client")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -33,31 +39,33 @@ func GetCookie(gc *gin.Context) (string, error) {
|
||||
return cookie.Value, nil
|
||||
}
|
||||
|
||||
// DeleteCookie sets the cookie value as empty to make it expired
|
||||
func DeleteCookie(gc *gin.Context) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
|
||||
host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
|
||||
domain := GetDomainName(constants.EnvData.AUTHORIZER_URL)
|
||||
host, _ := GetHostParts(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string))
|
||||
domain := GetDomainName(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string))
|
||||
if domain != "localhost" {
|
||||
domain = "." + domain
|
||||
}
|
||||
|
||||
gc.SetSameSite(http.SameSiteNoneMode)
|
||||
gc.SetCookie(constants.EnvData.COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(constants.EnvData.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string), "", -1, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyCookieName).(string)+"-client", "", -1, "/", domain, secure, httpOnly)
|
||||
}
|
||||
|
||||
// SetAdminCookie sets the admin cookie in the response
|
||||
func SetAdminCookie(gc *gin.Context, token string) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
|
||||
host, _ := GetHostParts(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string))
|
||||
|
||||
gc.SetCookie(constants.EnvData.ADMIN_COOKIE_NAME, token, 3600, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminCookieName).(string), token, 3600, "/", host, secure, httpOnly)
|
||||
}
|
||||
|
||||
func GetAdminCookie(gc *gin.Context) (string, error) {
|
||||
cookie, err := gc.Request.Cookie(constants.EnvData.ADMIN_COOKIE_NAME)
|
||||
cookie, err := gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminCookieName).(string))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -67,7 +75,7 @@ func GetAdminCookie(gc *gin.Context) (string, error) {
|
||||
func DeleteAdminCookie(gc *gin.Context) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
host, _ := GetHostParts(constants.EnvData.AUTHORIZER_URL)
|
||||
host, _ := GetHostParts(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string))
|
||||
|
||||
gc.SetCookie(constants.EnvData.ADMIN_COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminCookieName).(string), "", -1, "/", host, secure, httpOnly)
|
||||
}
|
||||
|
@@ -1,16 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func CreateSession(userId string, c *gin.Context) {
|
||||
sessionData := db.Session{
|
||||
UserID: userId,
|
||||
UserAgent: GetUserAgent(c.Request),
|
||||
IP: GetIP(c.Request),
|
||||
}
|
||||
|
||||
db.Mgr.AddSession(sessionData)
|
||||
}
|
@@ -5,15 +5,20 @@ import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// EncryptB64 encrypts data into base64 string
|
||||
func EncryptB64(text string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(text))
|
||||
}
|
||||
|
||||
// DecryptB64 decrypts from base64 string to readable string
|
||||
func DecryptB64(s string) (string, error) {
|
||||
data, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
@@ -22,8 +27,9 @@ func DecryptB64(s string) (string, error) {
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// EncryptAES encrypts data using AES algorithm
|
||||
func EncryptAES(text []byte) ([]byte, error) {
|
||||
key := []byte(constants.EnvData.ENCRYPTION_KEY)
|
||||
key := []byte(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyEncryptionKey).(string))
|
||||
c, err := aes.NewCipher(key)
|
||||
var res []byte
|
||||
if err != nil {
|
||||
@@ -55,8 +61,9 @@ func EncryptAES(text []byte) ([]byte, error) {
|
||||
return gcm.Seal(nonce, nonce, text, nil), nil
|
||||
}
|
||||
|
||||
// DecryptAES decrypts data using AES algorithm
|
||||
func DecryptAES(ciphertext []byte) ([]byte, error) {
|
||||
key := []byte(constants.EnvData.ENCRYPTION_KEY)
|
||||
key := []byte(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyEncryptionKey).(string))
|
||||
c, err := aes.NewCipher(key)
|
||||
var res []byte
|
||||
if err != nil {
|
||||
@@ -81,3 +88,39 @@ func DecryptAES(ciphertext []byte) ([]byte, error) {
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// EncryptEnvData is used to encrypt the env data
|
||||
func EncryptEnvData(data map[string]interface{}) ([]byte, error) {
|
||||
jsonBytes, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
envStoreObj := envstore.EnvInMemoryStoreObj.GetEnvStoreClone()
|
||||
|
||||
err = json.Unmarshal(jsonBytes, &envStoreObj)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
configData, err := json.Marshal(envStoreObj)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
encryptedConfig, err := EncryptAES(configData)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return encryptedConfig, nil
|
||||
}
|
||||
|
||||
// EncryptPassword is used for encrypting password
|
||||
func EncryptPassword(password string) (string, error) {
|
||||
pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(pw), nil
|
||||
}
|
||||
|
@@ -1,231 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/email"
|
||||
)
|
||||
|
||||
// SendVerificationMail to send verification email
|
||||
func SendVerificationMail(toEmail, token string) error {
|
||||
// The receiver needs to be in slice as the receive supports multiple receiver
|
||||
Receiver := []string{toEmail}
|
||||
|
||||
Subject := "Please verify your email"
|
||||
message := `
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta content="telephone=no" name="format-detection">
|
||||
<title></title>
|
||||
<!--[if (mso 16)]>
|
||||
<style type="text/css">
|
||||
a {}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!--[if gte mso 9]><style>sup { font-size: 100%% !important; }</style><![endif]-->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG></o:AllowPNG>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body style="font-family: sans-serif;">
|
||||
<div class="es-wrapper-color">
|
||||
<!--[if gte mso 9]>
|
||||
<v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
|
||||
<v:fill type="tile" color="#ffffff"></v:fill>
|
||||
</v:background>
|
||||
<![endif]-->
|
||||
<table class="es-wrapper" width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-email-paddings" valign="top">
|
||||
<table class="es-content esd-footer-popover" cellspacing="0" cellpadding="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-stripe" align="center">
|
||||
<table class="es-content-body" style="border-left:1px solid transparent;border-right:1px solid transparent;border-top:1px solid transparent;border-bottom:1px solid transparent;padding:20px 0px;" width="600" cellspacing="0" cellpadding="0" bgcolor="#ffffff" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-structure es-p20t es-p40b es-p40r es-p40l" esd-custom-block-id="8537" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-container-frame" width="518" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-block-image es-m-txt-c es-p5b" style="font-size:0;padding:10px" align="center"><a target="_blank" clicktracking="off"><img src="{{.OrgLogo}}" alt="icon" style="display: block;" title="icon" width="30"></a></td>
|
||||
</tr>
|
||||
|
||||
<tr style="background: rgb(249,250,251);padding: 10px;margin-bottom:10px;border-radius:5px;">
|
||||
<td class="esd-block-text es-m-txt-c es-p15t" align="center" style="padding:10px;padding-bottom:30px;">
|
||||
<p>Hey there 👋</p>
|
||||
<p>We have received request to verify email for <b>{{.OrgName}}</b>. If this is correct, please confirm your email address by clicking the button below.</p> <br/>
|
||||
<a
|
||||
clicktracking="off" href="{{.AuthUrl}}" class="es-button" target="_blank" style="text-decoration: none;padding:10px 15px;background-color: rgba(59,130,246,1);color: #fff;font-size: 1em;border-radius:5px;">Confirm Email</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
data := make(map[string]interface{}, 3)
|
||||
data["OrgLogo"] = constants.EnvData.ORGANIZATION_LOGO
|
||||
data["OrgName"] = constants.EnvData.ORGANIZATION_NAME
|
||||
data["AuthUrl"] = constants.EnvData.AUTHORIZER_URL + "/verify_email?token=" + token
|
||||
message = AddEmailTemplate(message, data, "verify_email.tmpl")
|
||||
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||
|
||||
return email.SendMail(Receiver, Subject, message)
|
||||
}
|
||||
|
||||
// SendForgotPasswordMail to send verification email
|
||||
func SendForgotPasswordMail(toEmail, token, host string) error {
|
||||
if constants.EnvData.RESET_PASSWORD_URL == "" {
|
||||
constants.EnvData.RESET_PASSWORD_URL = constants.EnvData.AUTHORIZER_URL + "/app/reset-password"
|
||||
}
|
||||
|
||||
// The receiver needs to be in slice as the receive supports multiple receiver
|
||||
Receiver := []string{toEmail}
|
||||
|
||||
Subject := "Reset Password"
|
||||
|
||||
message := `
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta content="telephone=no" name="format-detection">
|
||||
<title></title>
|
||||
<!--[if (mso 16)]>
|
||||
<style type="text/css">
|
||||
a {}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!--[if gte mso 9]><style>sup { font-size: 100%% !important; }</style><![endif]-->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG></o:AllowPNG>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body style="font-family: sans-serif;">
|
||||
<div class="es-wrapper-color">
|
||||
<!--[if gte mso 9]>
|
||||
<v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
|
||||
<v:fill type="tile" color="#ffffff"></v:fill>
|
||||
</v:background>
|
||||
<![endif]-->
|
||||
<table class="es-wrapper" width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-email-paddings" valign="top">
|
||||
<table class="es-content esd-footer-popover" cellspacing="0" cellpadding="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-stripe" align="center">
|
||||
<table class="es-content-body" style="border-left:1px solid transparent;border-right:1px solid transparent;border-top:1px solid transparent;border-bottom:1px solid transparent;padding:20px 0px;" width="600" cellspacing="0" cellpadding="0" bgcolor="#ffffff" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-structure es-p20t es-p40b es-p40r es-p40l" esd-custom-block-id="8537" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-container-frame" width="518" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-block-image es-m-txt-c es-p5b" style="font-size:0;padding:10px" align="center"><a target="_blank" clicktracking="off"><img src="{{.OrgLogo}}" alt="icon" style="display: block;" title="icon" width="30"></a></td>
|
||||
</tr>
|
||||
|
||||
<tr style="background: rgb(249,250,251);padding: 10px;margin-bottom:10px;border-radius:5px;">
|
||||
<td class="esd-block-text es-m-txt-c es-p15t" align="center" style="padding:10px;padding-bottom:30px;">
|
||||
<p>Hey there 👋</p>
|
||||
<p>We have received a request to reset password for email: <b>{{.ToEmail}}</b>. If this is correct, please reset the password clicking the button below.</p> <br/>
|
||||
<a clicktracking="off" href="{{.AuthUrl}}" class="es-button" target="_blank" style="text-decoration: none;padding:10px 15px;background-color: rgba(59,130,246,1);color: #fff;font-size: 1em;border-radius:5px;">Reset Password</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
data := make(map[string]interface{}, 3)
|
||||
data["OrgLogo"] = constants.EnvData.ORGANIZATION_LOGO
|
||||
data["ToEmail"] = constants.EnvData.ORGANIZATION_NAME
|
||||
data["AuthUrl"] = constants.EnvData.RESET_PASSWORD_URL + "?token=" + token
|
||||
message = AddEmailTemplate(message, data, "reset_password_email.tmpl")
|
||||
|
||||
return email.SendMail(Receiver, Subject, message)
|
||||
}
|
||||
|
||||
func AddEmailTemplate(a string, b map[string]interface{}, templateName string) string {
|
||||
tmpl, err := template.New(templateName).Parse(a)
|
||||
if err != nil {
|
||||
output, _ := json.Marshal(b)
|
||||
return string(output)
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
err = tmpl.Execute(buf, b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s := buf.String()
|
||||
return s
|
||||
}
|
@@ -3,7 +3,7 @@ package utils
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
)
|
||||
|
||||
func EncryptConfig(data map[string]interface{}) ([]byte, error) {
|
||||
@@ -12,12 +12,14 @@ func EncryptConfig(data map[string]interface{}) ([]byte, error) {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(jsonBytes, &constants.EnvData)
|
||||
envData := envstore.EnvInMemoryStoreObj.GetEnvStoreClone()
|
||||
|
||||
err = json.Unmarshal(jsonBytes, &envData)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
configData, err := json.Marshal(constants.EnvData)
|
||||
configData, err := json.Marshal(envData)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
@@ -7,6 +7,9 @@ import (
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
)
|
||||
|
||||
// TODO move this to provider
|
||||
// rename it to AsAPIUser
|
||||
|
||||
func GetResponseUserData(user db.User) *model.User {
|
||||
isEmailVerified := user.EmailVerifiedAt != nil
|
||||
isPhoneVerified := user.PhoneNumberVerifiedAt != nil
|
||||
|
@@ -7,6 +7,9 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// TODO renamae GinContextKey -> GinContext
|
||||
|
||||
// GinContext to get gin context from context
|
||||
func GinContextFromContext(ctx context.Context) (*gin.Context, error) {
|
||||
ginContext := ctx.Value("GinContextKey")
|
||||
if ginContext == nil {
|
||||
|
@@ -1,12 +0,0 @@
|
||||
package utils
|
||||
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
|
||||
func HashPassword(password string) (string, error) {
|
||||
pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(pw), nil
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
package utils
|
||||
|
||||
// any jobs that we want to run at start of server can be executed here
|
||||
|
||||
func InitServer() {
|
||||
}
|
@@ -2,19 +2,19 @@ package utils
|
||||
|
||||
import (
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
)
|
||||
|
||||
// GetMeta helps in getting the meta data about the deployment
|
||||
// version,
|
||||
// GetMeta helps in getting the meta data about the deployment from EnvData
|
||||
func GetMetaInfo() model.Meta {
|
||||
return model.Meta{
|
||||
Version: constants.VERSION,
|
||||
IsGoogleLoginEnabled: constants.GOOGLE_CLIENT_ID != "" && constants.GOOGLE_CLIENT_SECRET != "",
|
||||
IsGithubLoginEnabled: constants.GITHUB_CLIENT_ID != "" && constants.GITHUB_CLIENT_SECRET != "",
|
||||
IsFacebookLoginEnabled: constants.FACEBOOK_CLIENT_ID != "" && constants.FACEBOOK_CLIENT_SECRET != "",
|
||||
IsBasicAuthenticationEnabled: !constants.DISABLE_BASIC_AUTHENTICATION,
|
||||
IsEmailVerificationEnabled: !constants.DISABLE_EMAIL_VERIFICATION,
|
||||
IsMagicLinkLoginEnabled: !constants.DISABLE_MAGIC_LINK_LOGIN,
|
||||
Version: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyVersion).(string),
|
||||
IsGoogleLoginEnabled: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGoogleClientID).(string) != "" && envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGoogleClientSecret).(string) != "",
|
||||
IsGithubLoginEnabled: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGithubClientID).(string) != "" && envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyGithubClientSecret).(string) != "",
|
||||
IsFacebookLoginEnabled: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyFacebookClientID).(string) != "" && envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyFacebookClientSecret).(string) != "",
|
||||
IsBasicAuthenticationEnabled: !envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableBasicAuthentication).(bool),
|
||||
IsEmailVerificationEnabled: !envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableEmailVerification).(bool),
|
||||
IsMagicLinkLoginEnabled: !envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyDisableMagicLinkLogin).(bool),
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package utils
|
||||
|
||||
import "net/http"
|
||||
|
||||
// GetIP helps in getting the IP address from the request
|
||||
func GetIP(r *http.Request) string {
|
||||
IPAddress := r.Header.Get("X-Real-Ip")
|
||||
if IPAddress == "" {
|
||||
@@ -14,6 +15,7 @@ func GetIP(r *http.Request) string {
|
||||
return IPAddress
|
||||
}
|
||||
|
||||
// GetUserAgent helps in getting the user agent from the request
|
||||
func GetUserAgent(r *http.Request) string {
|
||||
return r.UserAgent()
|
||||
}
|
||||
|
@@ -6,17 +6,20 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// IsValidEmail validates email
|
||||
func IsValidEmail(email string) bool {
|
||||
_, err := mail.ParseAddress(email)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsValidOrigin validates origin based on ALLOWED_ORIGINS
|
||||
func IsValidOrigin(url string) bool {
|
||||
if len(constants.EnvData.ALLOWED_ORIGINS) == 1 && constants.EnvData.ALLOWED_ORIGINS[0] == "*" {
|
||||
allowedOrigins := envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAllowedOrigins).([]string)
|
||||
if len(allowedOrigins) == 1 && allowedOrigins[0] == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -24,7 +27,7 @@ func IsValidOrigin(url string) bool {
|
||||
hostName, port := GetHostParts(url)
|
||||
currentOrigin := hostName + ":" + port
|
||||
|
||||
for _, origin := range constants.EnvData.ALLOWED_ORIGINS {
|
||||
for _, origin := range allowedOrigins {
|
||||
replacedString := origin
|
||||
// if has regex whitelisted domains
|
||||
if strings.Contains(origin, "*") {
|
||||
@@ -49,6 +52,7 @@ func IsValidOrigin(url string) bool {
|
||||
return hasValidURL
|
||||
}
|
||||
|
||||
// IsSuperAdmin checks if user is super admin
|
||||
func IsSuperAdmin(gc *gin.Context) bool {
|
||||
token, err := GetAdminAuthToken(gc)
|
||||
if err != nil {
|
||||
@@ -57,12 +61,13 @@ func IsSuperAdmin(gc *gin.Context) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return secret == constants.EnvData.ADMIN_SECRET
|
||||
return secret == envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAdminSecret).(string)
|
||||
}
|
||||
|
||||
return token != ""
|
||||
}
|
||||
|
||||
// IsValidRoles validates roles
|
||||
func IsValidRoles(userRoles []string, roles []string) bool {
|
||||
valid := true
|
||||
for _, role := range roles {
|
||||
@@ -75,13 +80,17 @@ func IsValidRoles(userRoles []string, roles []string) bool {
|
||||
return valid
|
||||
}
|
||||
|
||||
// IsValidVerificationIdentifier validates verification identifier that is used to identify
|
||||
// the type of verification request
|
||||
func IsValidVerificationIdentifier(identifier string) bool {
|
||||
if identifier != enum.BasicAuthSignup.String() && identifier != enum.ForgotPassword.String() && identifier != enum.UpdateEmail.String() {
|
||||
if identifier != constants.VerificationTypeBasicAuthSignup && identifier != constants.VerificationTypeForgotPassword && identifier != constants.VerificationTypeUpdateEmail {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsStringArrayEqual validates if string array are equal.
|
||||
// This does check if the order is same
|
||||
func IsStringArrayEqual(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
|
@@ -4,39 +4,46 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
// TODO see if we can move this to different service
|
||||
|
||||
// UserInfo is the user info that is stored in the JWT of verification request
|
||||
type UserInfo struct {
|
||||
Email string `json:"email"`
|
||||
Host string `json:"host"`
|
||||
RedirectURL string `json:"redirect_url"`
|
||||
}
|
||||
|
||||
// CustomClaim is the custom claim that is stored in the JWT of verification request
|
||||
type CustomClaim struct {
|
||||
*jwt.StandardClaims
|
||||
TokenType string `json:"token_type"`
|
||||
UserInfo
|
||||
}
|
||||
|
||||
// CreateVerificationToken creates a verification JWT token
|
||||
func CreateVerificationToken(email string, tokenType string) (string, error) {
|
||||
t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE))
|
||||
t := jwt.New(jwt.GetSigningMethod(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyJwtType).(string)))
|
||||
|
||||
t.Claims = &CustomClaim{
|
||||
&jwt.StandardClaims{
|
||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||
},
|
||||
tokenType,
|
||||
UserInfo{Email: email, Host: constants.EnvData.AUTHORIZER_URL, RedirectURL: constants.EnvData.APP_URL},
|
||||
UserInfo{Email: email, Host: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAuthorizerURL).(string), RedirectURL: envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyAppURL).(string)},
|
||||
}
|
||||
|
||||
return t.SignedString([]byte(constants.EnvData.JWT_SECRET))
|
||||
return t.SignedString([]byte(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyJwtSecret).(string)))
|
||||
}
|
||||
|
||||
// VerifyVerificationToken verifies the verification JWT token
|
||||
func VerifyVerificationToken(token string) (*CustomClaim, error) {
|
||||
claims := &CustomClaim{}
|
||||
_, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(constants.EnvData.JWT_SECRET), nil
|
||||
return []byte(envstore.EnvInMemoryStoreObj.GetEnvVariable(constants.EnvKeyJwtSecret).(string)), nil
|
||||
})
|
||||
if err != nil {
|
||||
return claims, err
|
||||
|
Reference in New Issue
Block a user