Compare commits

...

15 Commits

Author SHA1 Message Date
Lakhan Samani
072cd46809 fix: docker build version arg 2021-10-10 01:15:47 +05:30
Lakhan Samani
17676fa13b fix: docker build version arg 2021-10-10 00:28:44 +05:30
Lakhan Samani
8b510ed556 fix: env parsing 2021-10-09 23:49:20 +05:30
Lakhan Samani
3ac0b44446 fix: remove unused env and fix typo 2021-10-09 22:29:10 +05:30
Lakhan Samani
b1dd6f2c3b chore: update app dependencies 2021-10-09 18:27:38 +05:30
Lakhan Samani
f5ea94f63c Merge branch 'main' of https://github.com/authorizerdev/authorizer 2021-10-09 10:06:15 +05:30
Lakhan Samani
653befc737 chore: update app dependencies (#56)
* fix: role validation for given token

* chore: update app dependencies
2021-10-09 10:04:59 +05:30
Lakhan Samani
6fed439ec2 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2021-10-09 10:01:47 +05:30
Lakhan Samani
5bd6fa5bc9 fix: role validation for given token (#55) 2021-10-09 09:40:45 +05:30
Lakhan Samani
75709e9f48 fix: role validation for given token 2021-10-09 09:39:50 +05:30
Lakhan Samani
b1b7f47f4c fix: roles model 2021-10-04 13:58:03 +05:30
Lakhan Samani
e429a1f860 fix: github workflow build script 2021-10-04 13:12:51 +05:30
Lakhan Samani
6819597a79 fix: mac build script 2021-10-04 13:11:26 +05:30
Lakhan Samani
b34b385be5 fix: email template 2021-10-04 12:19:27 +05:30
Lakhan Samani
5acf59d16e feat: add responsive email template + support for org env
Resolves #52
2021-10-04 03:17:50 +05:30
18 changed files with 324 additions and 146 deletions

View File

@@ -41,12 +41,12 @@ jobs:
make clean && \ make clean && \
CGO_ENABLED=1 GOOS=windows CC=/usr/bin/x86_64-w64-mingw32-gcc make && \ CGO_ENABLED=1 GOOS=windows CC=/usr/bin/x86_64-w64-mingw32-gcc make && \
mv build/server build/server.exe && \ mv build/server build/server.exe && \
zip -vr authorizer-${VERSION}-windows-amd64.zip .env app build templates zip -vr authorizer-${VERSION}-windows-amd64.zip .env app/build build templates
- name: Package files for linux - name: Package files for linux
run: | run: |
make clean && \ make clean && \
CGO_ENABLED=1 make && \ CGO_ENABLED=1 make && \
tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz .env app build templates tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz .env app/build build templates
- name: Upload assets - name: Upload assets
run: | run: |
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} && \ github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} && \
@@ -70,3 +70,5 @@ jobs:
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
build-args: |
VERSION=${VERSION}

View File

@@ -3,9 +3,10 @@ WORKDIR /app
COPY server server COPY server server
COPY Makefile . COPY Makefile .
ARG VERSION=0.1.0-beta.0 ARG VERSION="latest"
ENV VERSION="${VERSION}" ENV VERSION="$VERSION"
RUN echo "$VERSION"
RUN apk add build-base &&\ RUN apk add build-base &&\
make clean && make && \ make clean && make && \
chmod 777 build/server chmod 777 build/server

View File

@@ -14,3 +14,9 @@ For the first version we will only support setting roles master list via env
- [x] Return roles in users list for super admin - [x] Return roles in users list for super admin
- [x] Add roles to the JWT token generation - [x] Add roles to the JWT token generation
- [x] Validate token should also validate the role, if roles to validate again is present in request - [x] Validate token should also validate the role, if roles to validate again is present in request
# Misc
- [x] Fix email template
- [x] Add support for organization name in .env
- [x] Add support for organization logo in .env

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

30
app/package-lock.json generated
View File

@@ -8,7 +8,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "^0.1.0-beta.16", "@authorizerdev/authorizer-react": "^0.1.0-beta.18",
"@types/react": "^17.0.15", "@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17", "esbuild": "^0.12.17",
@@ -22,9 +22,9 @@
} }
}, },
"node_modules/@authorizerdev/authorizer-js": { "node_modules/@authorizerdev/authorizer-js": {
"version": "0.1.0-beta.16", "version": "0.1.0-beta.19",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.16.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.19.tgz",
"integrity": "sha512-RyjWhVbLYvmkcAT+2ddpRhrt7P5DBVKtGKjHWRugSqenTW7XFQMlhQqx5Z09DeqLw3Lsq0VVZ5h8tWcExHvwEw==", "integrity": "sha512-//uYjklwQfQKqLJHMAyjdrzh2nz6DycB3lEgl6bTXxmSbrz+l1kQyxB3y8wP/W30IrBQz8bZb+1sau+LD/FU7g==",
"dependencies": { "dependencies": {
"node-fetch": "^2.6.1" "node-fetch": "^2.6.1"
}, },
@@ -33,11 +33,11 @@
} }
}, },
"node_modules/@authorizerdev/authorizer-react": { "node_modules/@authorizerdev/authorizer-react": {
"version": "0.1.0-beta.16", "version": "0.1.0-beta.18",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.16.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.18.tgz",
"integrity": "sha512-b00LH0gtfMh/opFaGDF+EkxDOpNkCj/TNVjyW2wbiOFdC4HgLhc+UbPgL5/rDDol4XQsToL3SmwxmSxB2lWWuQ==", "integrity": "sha512-lRWWlS9akZwwINRW1NatsbMob06NXht3HXNTUTlu1s8m1YjxmFRE/AL6UIplzAYTpR6eDWMxEEaS0qAVxovUcg==",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-js": "^0.1.0-beta.16", "@authorizerdev/authorizer-js": "^0.1.0-beta.19",
"final-form": "^4.20.2", "final-form": "^4.20.2",
"react-final-form": "^6.5.3", "react-final-form": "^6.5.3",
"styled-components": "^5.3.0" "styled-components": "^5.3.0"
@@ -797,19 +797,19 @@
}, },
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-js": { "@authorizerdev/authorizer-js": {
"version": "0.1.0-beta.16", "version": "0.1.0-beta.19",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.16.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.19.tgz",
"integrity": "sha512-RyjWhVbLYvmkcAT+2ddpRhrt7P5DBVKtGKjHWRugSqenTW7XFQMlhQqx5Z09DeqLw3Lsq0VVZ5h8tWcExHvwEw==", "integrity": "sha512-//uYjklwQfQKqLJHMAyjdrzh2nz6DycB3lEgl6bTXxmSbrz+l1kQyxB3y8wP/W30IrBQz8bZb+1sau+LD/FU7g==",
"requires": { "requires": {
"node-fetch": "^2.6.1" "node-fetch": "^2.6.1"
} }
}, },
"@authorizerdev/authorizer-react": { "@authorizerdev/authorizer-react": {
"version": "0.1.0-beta.16", "version": "0.1.0-beta.18",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.16.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.18.tgz",
"integrity": "sha512-b00LH0gtfMh/opFaGDF+EkxDOpNkCj/TNVjyW2wbiOFdC4HgLhc+UbPgL5/rDDol4XQsToL3SmwxmSxB2lWWuQ==", "integrity": "sha512-lRWWlS9akZwwINRW1NatsbMob06NXht3HXNTUTlu1s8m1YjxmFRE/AL6UIplzAYTpR6eDWMxEEaS0qAVxovUcg==",
"requires": { "requires": {
"@authorizerdev/authorizer-js": "^0.1.0-beta.16", "@authorizerdev/authorizer-js": "^0.1.0-beta.19",
"final-form": "^4.20.2", "final-form": "^4.20.2",
"react-final-form": "^6.5.3", "react-final-form": "^6.5.3",
"styled-components": "^5.3.0" "styled-components": "^5.3.0"

View File

@@ -10,7 +10,7 @@
"author": "Lakhan Samani", "author": "Lakhan Samani",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "^0.1.0-beta.16", "@authorizerdev/authorizer-react": "^0.1.0-beta.18",
"@types/react": "^17.0.15", "@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17", "esbuild": "^0.12.17",

View File

@@ -4,30 +4,52 @@ import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
import Root from './Root'; import Root from './Root';
export default function App() { export default function App() {
// @ts-ignore // @ts-ignore
const globalState: Record<string, string> = window['__authorizer__']; const globalState: Record<string, string> = window['__authorizer__'];
return ( return (
<div style={{ display: 'flex', justifyContent: 'center' }}> <div
<div style={{
style={{ display: 'flex',
width: 400, justifyContent: 'center',
margin: `10px auto`, flexDirection: 'column',
border: `1px solid #D1D5DB`, }}
padding: `25px 20px`, >
borderRadius: 5, <div
}} style={{
> display: 'flex',
<BrowserRouter> justifyContent: 'center',
<AuthorizerProvider marginTop: 20,
config={{ flexDirection: 'column',
authorizerURL: globalState.authorizerURL, alignItems: 'center',
redirectURL: globalState.redirectURL, }}
}} >
> <img
<Root /> src={`${globalState.organizationLogo}`}
</AuthorizerProvider> alt="logo"
</BrowserRouter> style={{ height: 60, width: 60, objectFit: 'cover' }}
</div> />
</div> <h1>{globalState.organizationName}</h1>
); </div>
<div
style={{
width: 400,
margin: `10px auto`,
border: `1px solid #D1D5DB`,
padding: `25px 20px`,
borderRadius: 5,
}}
>
<BrowserRouter>
<AuthorizerProvider
config={{
authorizerURL: globalState.authorizerURL,
redirectURL: globalState.redirectURL,
}}
>
<Root />
</AuthorizerProvider>
</BrowserRouter>
</div>
</div>
);
} }

View File

@@ -10,9 +10,9 @@ export default function Root() {
useEffect(() => { useEffect(() => {
if (token) { if (token) {
const url = new URL(config.redirectURL); const url = new URL(config.redirectURL || '/app');
if (url.origin !== window.location.origin) { if (url.origin !== window.location.origin) {
window.location.href = config.redirectURL; window.location.href = config.redirectURL || '/app';
} }
} }
return () => {}; return () => {};

View File

@@ -2,9 +2,9 @@ import React, { Fragment } from 'react';
import { Authorizer } from '@authorizerdev/authorizer-react'; import { Authorizer } from '@authorizerdev/authorizer-react';
export default function Login() { export default function Login() {
return ( return (
<Fragment> <Fragment>
<Authorizer /> <Authorizer />
</Fragment> </Fragment>
); );
} }

View File

@@ -1,7 +1,7 @@
VERSION="$1" VERSION="$1"
make clean && CGO_ENABLED=1 VERSION=${VERSION} make make clean && CGO_ENABLED=1 VERSION=${VERSION} make
FILE_NAME=authorizer-${VERSION}-darwin-amd64.tar.gz FILE_NAME=authorizer-${VERSION}-darwin-amd64.tar.gz
tar cvfz ${FILE_NAME} .env app build templates tar cvfz ${FILE_NAME} .env app/build build templates
AUTH="Authorization: token $GITHUB_TOKEN" AUTH="Authorization: token $GITHUB_TOKEN"
RELASE_INFO=$(curl -sH "$AUTH" https://api.github.com/repos/authorizerdev/authorizer/releases/tags/${VERSION}) RELASE_INFO=$(curl -sH "$AUTH" https://api.github.com/repos/authorizerdev/authorizer/releases/tags/${VERSION})
echo $RELASE_INFO echo $RELASE_INFO

View File

@@ -13,7 +13,6 @@ var (
JWT_TYPE = "" JWT_TYPE = ""
JWT_SECRET = "" JWT_SECRET = ""
ALLOWED_ORIGINS = []string{} ALLOWED_ORIGINS = []string{}
ALLOWED_CALLBACK_URLS = []string{}
AUTHORIZER_URL = "" AUTHORIZER_URL = ""
PORT = "8080" PORT = "8080"
REDIS_URL = "" REDIS_URL = ""
@@ -37,4 +36,8 @@ var (
FACEBOOK_CLIENT_SECRET = "" FACEBOOK_CLIENT_SECRET = ""
TWITTER_CLIENT_ID = "" TWITTER_CLIENT_ID = ""
TWITTER_CLIENT_SECRET = "" TWITTER_CLIENT_SECRET = ""
// Org envs
ORGANIZATION_NAME = "Authorizer"
ORGANIZATION_LOGO = "https://authorizer.dev/images/logo.png"
) )

View File

@@ -23,8 +23,7 @@ func (r *Role) BeforeCreate(tx *gorm.DB) (err error) {
func (mgr *manager) SaveRoles(roles []Role) error { func (mgr *manager) SaveRoles(roles []Role) error {
res := mgr.db.Clauses( res := mgr.db.Clauses(
clause.OnConflict{ clause.OnConflict{
OnConstraint: "authorizer_roles_role_key", DoNothing: true,
DoNothing: true,
}).Create(&roles) }).Create(&roles)
if res.Error != nil { if res.Error != nil {
log.Println(`Error saving roles`) log.Println(`Error saving roles`)

View File

@@ -11,43 +11,33 @@ import (
) )
// build variables // build variables
var Version string var (
Version string
// ParseArgs -> to parse the cli flag and get db url. This is useful with heroku button ARG_DB_URL *string
func ParseArgs() { ARG_DB_TYPE *string
dbURL := flag.String("database_url", "", "Database connection string") ARG_AUTHORIZER_URL *string
dbType := flag.String("databse_type", "", "Database type, possible values are postgres,mysql,sqlite") ARG_ENV_FILE *string
authorizerURL := flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com") )
flag.Parse()
if *dbURL != "" {
constants.DATABASE_URL = *dbURL
}
if *dbType != "" {
constants.DATABASE_TYPE = *dbType
}
if *authorizerURL != "" {
constants.AUTHORIZER_URL = *authorizerURL
}
}
// InitEnv -> to initialize env and through error if required env are not present // InitEnv -> to initialize env and through error if required env are not present
func InitEnv() { func InitEnv() {
envPath := `.env` envPath := `.env`
envFile := flag.String("env_file", "", "Env file path") ARG_DB_URL = flag.String("database_url", "", "Database connection string")
ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite")
ARG_AUTHORIZER_URL = flag.String("authorizer_url", "", "URL for authorizer instance, eg: https://xyz.herokuapp.com")
ARG_ENV_FILE = flag.String("env_file", "", "Env file path")
flag.Parse() flag.Parse()
if *envFile != "" { if *ARG_ENV_FILE != "" {
envPath = *envFile envPath = *ARG_ENV_FILE
} }
err := godotenv.Load(envPath) err := godotenv.Load(envPath)
if err != nil { if err != nil {
log.Println("Error loading .env file") log.Println("Error loading .env file")
} }
constants.VERSION = Version constants.VERSION = Version
constants.ADMIN_SECRET = os.Getenv("ADMIN_SECRET") constants.ADMIN_SECRET = os.Getenv("ADMIN_SECRET")
constants.ENV = os.Getenv("ENV") constants.ENV = os.Getenv("ENV")
constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE") constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
@@ -104,20 +94,18 @@ func InitEnv() {
} }
constants.ALLOWED_ORIGINS = allowedOrigins constants.ALLOWED_ORIGINS = allowedOrigins
allowedCallbackSplit := strings.Split(os.Getenv("ALLOWED_CALLBACK_URLS"), ",") if *ARG_AUTHORIZER_URL != "" {
allowedCallbacks := []string{} constants.AUTHORIZER_URL = *ARG_AUTHORIZER_URL
for _, val := range allowedCallbackSplit { }
trimVal := strings.TrimSpace(val)
if trimVal != "" { if *ARG_DB_URL != "" {
allowedCallbacks = append(allowedCallbacks, trimVal) constants.DATABASE_URL = *ARG_DB_URL
} }
if *ARG_DB_TYPE != "" {
constants.DATABASE_TYPE = *ARG_DB_TYPE
} }
if len(allowedCallbackSplit) == 0 {
allowedCallbackSplit = []string{"*"}
}
constants.ALLOWED_CALLBACK_URLS = allowedCallbackSplit
ParseArgs()
if constants.DATABASE_URL == "" { if constants.DATABASE_URL == "" {
panic("Database url is required") panic("Database url is required")
} }
@@ -174,4 +162,12 @@ func InitEnv() {
if constants.JWT_ROLE_CLAIM == "" { if constants.JWT_ROLE_CLAIM == "" {
constants.JWT_ROLE_CLAIM = "role" constants.JWT_ROLE_CLAIM = "role"
} }
if os.Getenv("ORGANIZATION_NAME") != "" {
constants.ORGANIZATION_NAME = os.Getenv("ORGANIZATION_NAME")
}
if os.Getenv("ORGANIZATION_LOGO") != "" {
constants.ORGANIZATION_LOGO = os.Getenv("ORGANIZATION_LOGO")
}
} }

View File

@@ -75,8 +75,10 @@ func AppHandler() gin.HandlerFunc {
} }
c.HTML(http.StatusOK, "app.tmpl", gin.H{ c.HTML(http.StatusOK, "app.tmpl", gin.H{
"data": map[string]string{ "data": map[string]string{
"authorizerURL": stateObj.AuthorizerURL, "authorizerURL": stateObj.AuthorizerURL,
"redirectURL": stateObj.RedirectURL, "redirectURL": stateObj.RedirectURL,
"organizationName": constants.ORGANIZATION_NAME,
"organizationLogo": constants.ORGANIZATION_LOGO,
}, },
}) })
} }

View File

@@ -30,7 +30,6 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
} }
user, err := db.Mgr.GetUserByID(params.ID) user, err := db.Mgr.GetUserByID(params.ID)
if err != nil { if err != nil {
return res, fmt.Errorf(`User not found`) return res, fmt.Errorf(`User not found`)
} }
@@ -114,9 +113,8 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
return res, err return res, err
} }
userIdStr := fmt.Sprintf("%v", user.ID)
res = &model.User{ res = &model.User{
ID: userIdStr, ID: params.ID,
Email: user.Email, Email: user.Email,
Image: &user.Image, Image: &user.Image,
FirstName: &user.FirstName, FirstName: &user.FirstName,

View File

@@ -36,8 +36,8 @@ func Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
return res, err return res, err
} }
if role != nil && role != &claimRole { if role != nil && *role != claimRole {
return res, fmt.Errorf(`unauthorized. invalid role for a given token`) return res, fmt.Errorf(`unauthorized`)
} }
userIdStr := fmt.Sprintf("%v", user.ID) userIdStr := fmt.Sprintf("%v", user.ID)

View File

@@ -16,17 +16,91 @@ func SendVerificationMail(toEmail, token string) error {
Subject := "Please verify your email" Subject := "Please verify your email"
message := fmt.Sprintf(` message := fmt.Sprintf(`
<!DOCTYPE HTML PULBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta http-equiv="content-type" content="text/html"; charset=ISO-8859-1"> <head>
</head> <meta charset="UTF-8">
<body> <meta content="width=device-width, initial-scale=1" name="viewport">
<h1>Please verify your email by clicking on the link below </h1><br/> <meta name="x-apple-disable-message-reformatting">
<a href="%s">Click here to verify</a> <meta http-equiv="X-UA-Compatible" content="IE=edge">
</body> <meta content="telephone=no" name="format-detection">
</html> <title></title>
`, constants.AUTHORIZER_URL+"/verify_email"+"?token="+token) <!--[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"><img src="%s" 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 received a request to sign-up for <b>%s</b>. If this is correct, please confirm your email address by clicking the button below.</p> <br/>
<a href="%s" 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>
`, constants.ORGANIZATION_LOGO, constants.ORGANIZATION_NAME, constants.AUTHORIZER_URL+"/verify_email"+"?token="+token)
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message) bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
return sender.SendMail(Receiver, Subject, bodyMessage) return sender.SendMail(Receiver, Subject, bodyMessage)
@@ -46,17 +120,92 @@ func SendForgotPasswordMail(toEmail, token, host string) error {
Subject := "Reset Password" Subject := "Reset Password"
message := fmt.Sprintf(` message := fmt.Sprintf(`
<!DOCTYPE HTML PULBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta http-equiv="content-type" content="text/html"; charset=ISO-8859-1"> <head>
</head> <meta charset="UTF-8">
<body> <meta content="width=device-width, initial-scale=1" name="viewport">
<h1>Please use the link below to reset password </h1><br/> <meta name="x-apple-disable-message-reformatting">
<a href="%s">Reset Password</a> <meta http-equiv="X-UA-Compatible" content="IE=edge">
</body> <meta content="telephone=no" name="format-detection">
</html> <title></title>
`, constants.RESET_PASSWORD_URL+"?token="+token) <!--[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"><img src="%s" 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 received a request to reset password for email: <b>%s</b>. If this is correct, please reset the password clicking the button below.</p> <br/>
<a href="%s" 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>
`, constants.ORGANIZATION_LOGO, toEmail, constants.RESET_PASSWORD_URL+"?token="+token)
bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message) bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
return sender.SendMail(Receiver, Subject, bodyMessage) return sender.SendMail(Receiver, Subject, bodyMessage)