resolve conflict with base branch

This commit is contained in:
manoj 2022-11-29 17:19:06 +05:30
commit fc319d7934
30 changed files with 441 additions and 78 deletions

View File

@ -11,3 +11,4 @@ SMTP_PORT=2525
SMTP_USERNAME=test SMTP_USERNAME=test
SMTP_PASSWORD=test SMTP_PASSWORD=test
SENDER_EMAIL="info@authorizer.dev" SENDER_EMAIL="info@authorizer.dev"
AWS_REGION=ap-south-1

View File

@ -60,7 +60,12 @@ export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
{view === VIEW_TYPES.FORGOT_PASSWORD && ( {view === VIEW_TYPES.FORGOT_PASSWORD && (
<Fragment> <Fragment>
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1> <h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
<AuthorizerForgotPassword urlProps={urlProps} /> <AuthorizerForgotPassword
urlProps={{
...urlProps,
redirect_uri: `${window.location.origin}/app/reset-password`,
}}
/>
<Footer> <Footer>
<Link <Link
to="#" to="#"

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useState } from 'react';
import { import {
Button, Button,
Center, Center,
@ -20,13 +20,14 @@ import { useClient } from 'urql';
import { FaSave } from 'react-icons/fa'; import { FaSave } from 'react-icons/fa';
import InputField from './InputField'; import InputField from './InputField';
import { import {
ArrayInputType,
DateInputType, DateInputType,
MultiSelectInputType,
SelectInputType, SelectInputType,
TextInputType, TextInputType,
} from '../constants'; } from '../constants';
import { getObjectDiff } from '../utils'; import { getObjectDiff } from '../utils';
import { UpdateUser } from '../graphql/mutation'; import { UpdateUser } from '../graphql/mutation';
import { GetAvailableRolesQuery } from '../graphql/queries';
const GenderTypes = { const GenderTypes = {
Undisclosed: null, Undisclosed: null,
@ -57,8 +58,9 @@ const EditUserModal = ({
}) => { }) => {
const client = useClient(); const client = useClient();
const toast = useToast(); const toast = useToast();
const [availableRoles, setAvailableRoles] = useState<string[]>([]);
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const [userData, setUserData] = React.useState<userDataTypes>({ const [userData, setUserData] = useState<userDataTypes>({
id: '', id: '',
email: '', email: '',
given_name: '', given_name: '',
@ -73,7 +75,17 @@ const EditUserModal = ({
}); });
React.useEffect(() => { React.useEffect(() => {
setUserData(user); setUserData(user);
fetchAvailableRoles();
}, []); }, []);
const fetchAvailableRoles = async () => {
const res = await client.query(GetAvailableRolesQuery).toPromise();
if (res.data?._env?.ROLES && res.data?._env?.PROTECTED_ROLES) {
setAvailableRoles([
...res.data._env.ROLES,
...res.data._env.PROTECTED_ROLES,
]);
}
};
const saveHandler = async () => { const saveHandler = async () => {
const diff = getObjectDiff(user, userData); const diff = getObjectDiff(user, userData);
const updatedUserData = diff.reduce( const updatedUserData = diff.reduce(
@ -221,7 +233,8 @@ const EditUserModal = ({
<InputField <InputField
variables={userData} variables={userData}
setVariables={setUserData} setVariables={setUserData}
inputType={ArrayInputType.USER_ROLES} availableRoles={availableRoles}
inputType={MultiSelectInputType.USER_ROLES}
/> />
</Center> </Center>
</Flex> </Flex>

View File

@ -48,6 +48,26 @@ const EmailConfigurations = ({
/> />
</Center> </Center>
</Flex> </Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">SMTP Local Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_LOCAL_NAME}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}> <Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex <Flex
w={isNotSmallerScreen ? '30%' : '40%'} w={isNotSmallerScreen ? '30%' : '40%'}

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useState } from 'react';
import { import {
Box, Box,
Flex, Flex,
@ -13,6 +13,12 @@ import {
Textarea, Textarea,
Switch, Switch,
Text, Text,
MenuButton,
MenuList,
MenuItemOption,
MenuOptionGroup,
Button,
Menu,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
FaRegClone, FaRegClone,
@ -20,6 +26,7 @@ import {
FaRegEyeSlash, FaRegEyeSlash,
FaPlus, FaPlus,
FaTimes, FaTimes,
FaAngleDown,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { import {
ArrayInputOperations, ArrayInputOperations,
@ -30,6 +37,7 @@ import {
TextAreaInputType, TextAreaInputType,
SwitchInputType, SwitchInputType,
DateInputType, DateInputType,
MultiSelectInputType,
} from '../constants'; } from '../constants';
import { copyTextToClipboard } from '../utils'; import { copyTextToClipboard } from '../utils';
@ -39,13 +47,16 @@ const InputField = ({
setVariables, setVariables,
fieldVisibility, fieldVisibility,
setFieldVisibility, setFieldVisibility,
availableRoles,
...downshiftProps ...downshiftProps
}: any) => { }: any) => {
const props = { const props = {
size: 'sm', size: 'sm',
...downshiftProps, ...downshiftProps,
}; };
const [inputFieldVisibility, setInputFieldVisibility] = React.useState< const [availableUserRoles, setAvailableUserRoles] =
useState<string[]>(availableRoles);
const [inputFieldVisibility, setInputFieldVisibility] = useState<
Record<string, boolean> Record<string, boolean>
>({ >({
ROLES: false, ROLES: false,
@ -54,7 +65,7 @@ const InputField = ({
ALLOWED_ORIGINS: false, ALLOWED_ORIGINS: false,
roles: false, roles: false,
}); });
const [inputData, setInputData] = React.useState<Record<string, string>>({ const [inputData, setInputData] = useState<Record<string, string>>({
ROLES: '', ROLES: '',
DEFAULT_ROLES: '', DEFAULT_ROLES: '',
PROTECTED_ROLES: '', PROTECTED_ROLES: '',
@ -116,7 +127,7 @@ const InputField = ({
<InputGroup size="sm"> <InputGroup size="sm">
<Input <Input
{...props} {...props}
value={variables[inputType] ?? ''} value={variables[inputType] || ''}
onChange={( onChange={(
event: Event & { event: Event & {
target: HTMLInputElement; target: HTMLInputElement;
@ -221,7 +232,7 @@ const InputField = ({
size="xs" size="xs"
minW="150px" minW="150px"
placeholder="add a new value" placeholder="add a new value"
value={inputData[inputType] ?? ''} value={inputData[inputType] || ''}
onChange={(e: any) => { onChange={(e: any) => {
setInputData({ ...inputData, [inputType]: e.target.value }); setInputData({ ...inputData, [inputType]: e.target.value });
}} }}
@ -278,6 +289,87 @@ const InputField = ({
</Select> </Select>
); );
} }
if (Object.values(MultiSelectInputType).includes(inputType)) {
return (
<Flex w="100%" style={{ position: 'relative' }}>
<Flex
border="1px solid #e2e8f0"
w="100%"
borderRadius="var(--chakra-radii-sm)"
p="1% 0 0 2.5%"
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
overflowY="hidden"
justifyContent="space-between"
alignItems="center"
>
<Flex justifyContent="start" alignItems="center" w="100%" wrap="wrap">
{variables[inputType].map((role: string, index: number) => (
<Box key={index} margin="0.5%" role="group">
<Tag
size="sm"
variant="outline"
colorScheme="gray"
minW="fit-content"
>
<TagLabel cursor="default">{role}</TagLabel>
<TagRightIcon
boxSize="12px"
as={FaTimes}
display="none"
cursor="pointer"
_groupHover={{ display: 'block' }}
onClick={() =>
updateInputHandler(
inputType,
ArrayInputOperations.REMOVE,
role,
)
}
/>
</Tag>
</Box>
))}
</Flex>
<Menu matchWidth={true}>
<MenuButton px="10px" py="7.5px">
<FaAngleDown />
</MenuButton>
<MenuList
position="absolute"
top="0"
right="0"
zIndex="10"
maxH="150"
overflowX="scroll"
>
<MenuOptionGroup
title={undefined}
value={variables[inputType]}
type="checkbox"
onChange={(values: string[] | string) => {
setVariables({
...variables,
[inputType]: values,
});
}}
>
{availableUserRoles.map((role) => {
return (
<MenuItemOption
key={`multiselect-menu-${role}`}
value={role}
>
{role}
</MenuItemOption>
);
})}
</MenuOptionGroup>
</MenuList>
</Menu>
</Flex>
</Flex>
);
}
if (Object.values(TextAreaInputType).includes(inputType)) { if (Object.values(TextAreaInputType).includes(inputType)) {
return ( return (
<Textarea <Textarea

View File

@ -15,6 +15,7 @@ export const TextInputType = {
SMTP_HOST: 'SMTP_HOST', SMTP_HOST: 'SMTP_HOST',
SMTP_PORT: 'SMTP_PORT', SMTP_PORT: 'SMTP_PORT',
SMTP_USERNAME: 'SMTP_USERNAME', SMTP_USERNAME: 'SMTP_USERNAME',
SMTP_LOCAL_NAME: 'SMTP_LOCAL_NAME',
SENDER_EMAIL: 'SENDER_EMAIL', SENDER_EMAIL: 'SENDER_EMAIL',
ORGANIZATION_NAME: 'ORGANIZATION_NAME', ORGANIZATION_NAME: 'ORGANIZATION_NAME',
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO', ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
@ -48,7 +49,6 @@ export const ArrayInputType = {
DEFAULT_ROLES: 'DEFAULT_ROLES', DEFAULT_ROLES: 'DEFAULT_ROLES',
PROTECTED_ROLES: 'PROTECTED_ROLES', PROTECTED_ROLES: 'PROTECTED_ROLES',
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS', ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
USER_ROLES: 'roles',
}; };
export const SelectInputType = { export const SelectInputType = {
@ -56,6 +56,10 @@ export const SelectInputType = {
GENDER: 'gender', GENDER: 'gender',
}; };
export const MultiSelectInputType = {
USER_ROLES: 'roles',
};
export const TextAreaInputType = { export const TextAreaInputType = {
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT', CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY', JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY',
@ -129,6 +133,7 @@ export interface envVarTypes {
SMTP_PORT: string; SMTP_PORT: string;
SMTP_USERNAME: string; SMTP_USERNAME: string;
SMTP_PASSWORD: string; SMTP_PASSWORD: string;
SMTP_LOCAL_NAME: string;
SENDER_EMAIL: string; SENDER_EMAIL: string;
ALLOWED_ORIGINS: [string] | []; ALLOWED_ORIGINS: [string] | [];
ORGANIZATION_NAME: string; ORGANIZATION_NAME: string;

View File

@ -45,6 +45,7 @@ export const EnvVariablesQuery = `
SMTP_PORT SMTP_PORT
SMTP_USERNAME SMTP_USERNAME
SMTP_PASSWORD SMTP_PASSWORD
SMTP_LOCAL_NAME
SENDER_EMAIL SENDER_EMAIL
ALLOWED_ORIGINS ALLOWED_ORIGINS
ORGANIZATION_NAME ORGANIZATION_NAME
@ -169,3 +170,12 @@ export const WebhookLogsQuery = `
} }
} }
`; `;
export const GetAvailableRolesQuery = `
query {
_env {
ROLES
PROTECTED_ROLES
}
}
`;

View File

@ -65,6 +65,7 @@ const Environment = () => {
SMTP_PORT: '', SMTP_PORT: '',
SMTP_USERNAME: '', SMTP_USERNAME: '',
SMTP_PASSWORD: '', SMTP_PASSWORD: '',
SMTP_LOCAL_NAME: '',
SENDER_EMAIL: '', SENDER_EMAIL: '',
ALLOWED_ORIGINS: [], ALLOWED_ORIGINS: [],
ORGANIZATION_NAME: '', ORGANIZATION_NAME: '',

View File

@ -22,11 +22,11 @@ const (
// EnvKeyDatabaseURL key for env variable DATABASE_URL // EnvKeyDatabaseURL key for env variable DATABASE_URL
EnvKeyDatabaseURL = "DATABASE_URL" EnvKeyDatabaseURL = "DATABASE_URL"
// EnvAwsRegion key for env variable AWS REGION // EnvAwsRegion key for env variable AWS REGION
EnvAwsRegion = "REGION" EnvAwsRegion = "AWS_REGION"
// EnvAwsAccessKey key for env variable AWS_ACCESS_KEY // EnvAwsAccessKeyID key for env variable AWS_ACCESS_KEY_ID
EnvAwsAccessKey = "AWS_ACCESS_KEY" EnvAwsAccessKeyID = "AWS_ACCESS_KEY_ID"
// EnvAwsAccessKey key for env variable AWS_SECRET_KEY // EnvAwsAccessKey key for env variable AWS_SECRET_ACCESS_KEY
EnvAwsSecretKey = "AWS_SECRET_KEY" EnvAwsSecretAccessKey = "AWS_SECRET_ACCESS_KEY"
// EnvAwsAccessKey key for env variable AWS_SECRET_KEY // EnvAwsAccessKey key for env variable AWS_SECRET_KEY
EnvCouchbaseScope = "COUCHBASE_SCOPE" EnvCouchbaseScope = "COUCHBASE_SCOPE"
// EnvAwsAccessKey key for env variable AWS_SECRET_KEY // EnvAwsAccessKey key for env variable AWS_SECRET_KEY
@ -55,6 +55,8 @@ const (
EnvKeySmtpUsername = "SMTP_USERNAME" EnvKeySmtpUsername = "SMTP_USERNAME"
// EnvKeySmtpPassword key for env variable SMTP_PASSWORD // EnvKeySmtpPassword key for env variable SMTP_PASSWORD
EnvKeySmtpPassword = "SMTP_PASSWORD" EnvKeySmtpPassword = "SMTP_PASSWORD"
// EnvKeySmtpLocalName key for env variable SMTP_LOCAL_NAME
EnvKeySmtpLocalName = "SMTP_LOCAL_NAME"
// EnvKeySenderEmail key for env variable SENDER_EMAIL // EnvKeySenderEmail key for env variable SENDER_EMAIL
EnvKeySenderEmail = "SENDER_EMAIL" EnvKeySenderEmail = "SENDER_EMAIL"
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED // EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED

View File

@ -1,16 +1,15 @@
package dynamodb package dynamodb
import ( import (
"os"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/guregu/dynamo" "github.com/guregu/dynamo"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/memorystore"
) )
type provider struct { type provider struct {
@ -20,28 +19,29 @@ type provider struct {
// NewProvider returns a new Dynamo provider // NewProvider returns a new Dynamo provider
func NewProvider() (*provider, error) { func NewProvider() (*provider, error) {
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
awsRegion := os.Getenv(constants.EnvAwsRegion) awsRegion := memorystore.RequiredEnvStoreObj.GetRequiredEnv().AwsRegion
accessKey := os.Getenv(constants.EnvAwsAccessKey) awsAccessKeyID := memorystore.RequiredEnvStoreObj.GetRequiredEnv().AwsAccessKeyID
secretKey := os.Getenv(constants.EnvAwsSecretKey) awsSecretAccessKey := memorystore.RequiredEnvStoreObj.GetRequiredEnv().AwsSecretAccessKey
config := aws.Config{ config := aws.Config{
MaxRetries: aws.Int(3), MaxRetries: aws.Int(3),
CredentialsChainVerboseErrors: aws.Bool(true), // for full error logs CredentialsChainVerboseErrors: aws.Bool(true), // for full error logs
} }
if awsRegion != "" { if awsRegion != "" {
config.Region = aws.String(awsRegion) config.Region = aws.String(awsRegion)
} }
// custom accessKey, secretkey took first priority, if not then fetch config from aws credentials // custom awsAccessKeyID, awsSecretAccessKey took first priority, if not then fetch config from aws credentials
if accessKey != "" && secretKey != "" { if awsAccessKeyID != "" && awsSecretAccessKey != "" {
config.Credentials = credentials.NewStaticCredentials(accessKey, secretKey, "") config.Credentials = credentials.NewStaticCredentials(awsAccessKeyID, awsSecretAccessKey, "")
} else if dbURL != "" { } else if dbURL != "" {
// static config in case of testing or local-setup // static config in case of testing or local-setup
config.Credentials = credentials.NewStaticCredentials("key", "key", "") config.Credentials = credentials.NewStaticCredentials("key", "key", "")
config.Endpoint = aws.String(dbURL) config.Endpoint = aws.String(dbURL)
} else { } else {
log.Info("REGION, AWS_ACCESS_KEY and AWS_SECRET_KEY not found in .env, trying to load default profile from aws credentials") log.Debugf("%s or %s or %s not found. Trying to load default credentials from aws config", constants.EnvAwsRegion, constants.EnvAwsAccessKeyID, constants.EnvAwsSecretAccessKey)
} }
session := session.Must(session.NewSession(&config)) session := session.Must(session.NewSession(&config))

View File

@ -180,7 +180,7 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
for _, user := range allUsers { for _, user := range allUsers {
err = UpdateByHashKey(userCollection, "id", user.ID, data) err = UpdateByHashKey(userCollection, "id", user.ID, data)
if err != nil { if err == nil {
res = res + 1 res = res + 1
} }
} }

View File

@ -1,6 +1,7 @@
package sql package sql
import ( import (
"fmt"
"log" "log"
"os" "os"
"time" "time"
@ -21,6 +22,16 @@ type provider struct {
db *gorm.DB db *gorm.DB
} }
const (
phoneNumberIndexName = "UQ_phone_number"
phoneNumberColumnName = "phone_number"
)
type indexInfo struct {
IndexName string `json:"index_name"`
ColumnName string `json:"column_name"`
}
// NewProvider returns a new SQL provider // NewProvider returns a new SQL provider
func NewProvider() (*provider, error) { func NewProvider() (*provider, error) {
var sqlDB *gorm.DB var sqlDB *gorm.DB
@ -65,6 +76,32 @@ func NewProvider() (*provider, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// unique constraint on phone number does not work with multiple null values for sqlserver
// for more information check https://stackoverflow.com/a/767702
if dbType == constants.DbTypeSqlserver {
var indexInfos []indexInfo
// remove index on phone number if present with different name
res := sqlDB.Raw("SELECT i.name AS index_name, i.type_desc AS index_algorithm, CASE i.is_unique WHEN 1 THEN 'TRUE' ELSE 'FALSE' END AS is_unique, ac.Name AS column_name FROM sys.tables AS t INNER JOIN sys.indexes AS i ON t.object_id = i.object_id INNER JOIN sys.index_columns AS ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id INNER JOIN sys.all_columns AS ac ON ic.object_id = ac.object_id AND ic.column_id = ac.column_id WHERE t.name = 'authorizer_users' AND SCHEMA_NAME(t.schema_id) = 'dbo';").Scan(&indexInfos)
if res.Error != nil {
return nil, res.Error
}
for _, val := range indexInfos {
if val.ColumnName == phoneNumberColumnName && val.IndexName != phoneNumberIndexName {
// drop index & create new
if res := sqlDB.Exec(fmt.Sprintf(`ALTER TABLE authorizer_users DROP CONSTRAINT "%s";`, val.IndexName)); res.Error != nil {
return nil, res.Error
}
// create index
if res := sqlDB.Exec(fmt.Sprintf("CREATE UNIQUE NONCLUSTERED INDEX %s ON authorizer_users(phone_number) WHERE phone_number IS NOT NULL;", phoneNumberIndexName)); res.Error != nil {
return nil, res.Error
}
}
}
}
return &provider{ return &provider{
db: sqlDB, db: sqlDB,
}, nil }, nil

View File

@ -5,6 +5,7 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"strconv" "strconv"
"strings"
"text/template" "text/template"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -126,6 +127,12 @@ func SendEmail(to []string, event string, data map[string]interface{}) error {
return err return err
} }
smtpLocalName, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeySmtpLocalName)
if err != nil {
log.Debugf("Error while getting smtp localname from env variable: %v", err)
smtpLocalName = ""
}
isProd, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsProd) isProd, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsProd)
if err != nil { if err != nil {
log.Errorf("Error while getting env variable: %v", err) log.Errorf("Error while getting env variable: %v", err)
@ -141,6 +148,11 @@ func SendEmail(to []string, event string, data map[string]interface{}) error {
if !isProd { if !isProd {
d.TLSConfig = &tls.Config{InsecureSkipVerify: true} d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
} }
if strings.TrimSpace(smtpLocalName) != "" {
d.LocalName = smtpLocalName
}
if err := d.DialAndSend(m); err != nil { if err := d.DialAndSend(m); err != nil {
log.Debug("SMTP Failed: ", err) log.Debug("SMTP Failed: ", err)
return err return err

28
server/env/env.go vendored
View File

@ -55,6 +55,7 @@ func InitAllEnv() error {
osSmtpPort := os.Getenv(constants.EnvKeySmtpPort) osSmtpPort := os.Getenv(constants.EnvKeySmtpPort)
osSmtpUsername := os.Getenv(constants.EnvKeySmtpUsername) osSmtpUsername := os.Getenv(constants.EnvKeySmtpUsername)
osSmtpPassword := os.Getenv(constants.EnvKeySmtpPassword) osSmtpPassword := os.Getenv(constants.EnvKeySmtpPassword)
osSmtpLocalName := os.Getenv(constants.EnvKeySmtpLocalName)
osSenderEmail := os.Getenv(constants.EnvKeySenderEmail) osSenderEmail := os.Getenv(constants.EnvKeySenderEmail)
osJwtType := os.Getenv(constants.EnvKeyJwtType) osJwtType := os.Getenv(constants.EnvKeyJwtType)
osJwtSecret := os.Getenv(constants.EnvKeyJwtSecret) osJwtSecret := os.Getenv(constants.EnvKeyJwtSecret)
@ -78,8 +79,8 @@ func InitAllEnv() error {
osOrganizationName := os.Getenv(constants.EnvKeyOrganizationName) osOrganizationName := os.Getenv(constants.EnvKeyOrganizationName)
osOrganizationLogo := os.Getenv(constants.EnvKeyOrganizationLogo) osOrganizationLogo := os.Getenv(constants.EnvKeyOrganizationLogo)
osAwsRegion := os.Getenv(constants.EnvAwsRegion) osAwsRegion := os.Getenv(constants.EnvAwsRegion)
osAwsAccessKey := os.Getenv(constants.EnvAwsAccessKey) osAwsAccessKey := os.Getenv(constants.EnvAwsAccessKeyID)
osAwsSecretKey := os.Getenv(constants.EnvAwsSecretKey) osAwsSecretKey := os.Getenv(constants.EnvAwsSecretAccessKey)
// os bool vars // os bool vars
osAppCookieSecure := os.Getenv(constants.EnvKeyAppCookieSecure) osAppCookieSecure := os.Getenv(constants.EnvKeyAppCookieSecure)
@ -129,18 +130,18 @@ func InitAllEnv() error {
envData[constants.EnvAwsRegion] = osAwsRegion envData[constants.EnvAwsRegion] = osAwsRegion
} }
if val, ok := envData[constants.EnvAwsAccessKey]; !ok || val == "" { if val, ok := envData[constants.EnvAwsAccessKeyID]; !ok || val == "" {
envData[constants.EnvAwsAccessKey] = osAwsAccessKey envData[constants.EnvAwsAccessKeyID] = osAwsAccessKey
} }
if osAwsAccessKey != "" && envData[constants.EnvAwsAccessKey] != osAwsRegion { if osAwsAccessKey != "" && envData[constants.EnvAwsAccessKeyID] != osAwsRegion {
envData[constants.EnvAwsAccessKey] = osAwsAccessKey envData[constants.EnvAwsAccessKeyID] = osAwsAccessKey
} }
if val, ok := envData[constants.EnvAwsSecretKey]; !ok || val == "" { if val, ok := envData[constants.EnvAwsSecretAccessKey]; !ok || val == "" {
envData[constants.EnvAwsSecretKey] = osAwsSecretKey envData[constants.EnvAwsSecretAccessKey] = osAwsSecretKey
} }
if osAwsSecretKey != "" && envData[constants.EnvAwsSecretKey] != osAwsRegion { if osAwsSecretKey != "" && envData[constants.EnvAwsSecretAccessKey] != osAwsRegion {
envData[constants.EnvAwsSecretKey] = osAwsSecretKey envData[constants.EnvAwsSecretAccessKey] = osAwsSecretKey
} }
if val, ok := envData[constants.EnvKeyAppURL]; !ok || val == "" { if val, ok := envData[constants.EnvKeyAppURL]; !ok || val == "" {
@ -205,6 +206,13 @@ func InitAllEnv() error {
envData[constants.EnvKeySmtpUsername] = osSmtpUsername envData[constants.EnvKeySmtpUsername] = osSmtpUsername
} }
if val, ok := envData[constants.EnvKeySmtpLocalName]; !ok || val == "" {
envData[constants.EnvKeySmtpLocalName] = osSmtpLocalName
}
if osSmtpLocalName != "" && envData[constants.EnvKeySmtpLocalName] != osSmtpLocalName {
envData[constants.EnvKeySmtpLocalName] = osSmtpLocalName
}
if val, ok := envData[constants.EnvKeySmtpPassword]; !ok || val == "" { if val, ok := envData[constants.EnvKeySmtpPassword]; !ok || val == "" {
envData[constants.EnvKeySmtpPassword] = osSmtpPassword envData[constants.EnvKeySmtpPassword] = osSmtpPassword
} }

View File

@ -128,7 +128,6 @@ github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gocql/gocql v1.2.0 h1:TZhsCd7fRuye4VyHr3WCvWwIQaZUmjsqnSIXK9FcVCE= github.com/gocql/gocql v1.2.0 h1:TZhsCd7fRuye4VyHr3WCvWwIQaZUmjsqnSIXK9FcVCE=
github.com/gocql/gocql v1.2.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/gocql/gocql v1.2.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@ -265,6 +264,7 @@ github.com/jinzhu/now v1.1.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
@ -501,6 +501,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@ -569,6 +571,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -118,6 +118,7 @@ type ComplexityRoot struct {
ResetPasswordURL func(childComplexity int) int ResetPasswordURL func(childComplexity int) int
Roles func(childComplexity int) int Roles func(childComplexity int) int
SMTPHost func(childComplexity int) int SMTPHost func(childComplexity int) int
SMTPLocalName func(childComplexity int) int
SMTPPassword func(childComplexity int) int SMTPPassword func(childComplexity int) int
SMTPPort func(childComplexity int) int SMTPPort func(childComplexity int) int
SMTPUsername func(childComplexity int) int SMTPUsername func(childComplexity int) int
@ -245,6 +246,7 @@ type ComplexityRoot struct {
} }
ValidateJWTTokenResponse struct { ValidateJWTTokenResponse struct {
Claims func(childComplexity int) int
IsValid func(childComplexity int) int IsValid func(childComplexity int) int
} }
@ -805,6 +807,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Env.SMTPHost(childComplexity), true return e.complexity.Env.SMTPHost(childComplexity), true
case "Env.SMTP_LOCAL_NAME":
if e.complexity.Env.SMTPLocalName == nil {
break
}
return e.complexity.Env.SMTPLocalName(childComplexity), true
case "Env.SMTP_PASSWORD": case "Env.SMTP_PASSWORD":
if e.complexity.Env.SMTPPassword == nil { if e.complexity.Env.SMTPPassword == nil {
break break
@ -1638,6 +1647,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Users.Users(childComplexity), true return e.complexity.Users.Users(childComplexity), true
case "ValidateJWTTokenResponse.claims":
if e.complexity.ValidateJWTTokenResponse.Claims == nil {
break
}
return e.complexity.ValidateJWTTokenResponse.Claims(childComplexity), true
case "ValidateJWTTokenResponse.is_valid": case "ValidateJWTTokenResponse.is_valid":
if e.complexity.ValidateJWTTokenResponse.IsValid == nil { if e.complexity.ValidateJWTTokenResponse.IsValid == nil {
break break
@ -2059,6 +2075,7 @@ type Env {
SMTP_PORT: String SMTP_PORT: String
SMTP_USERNAME: String SMTP_USERNAME: String
SMTP_PASSWORD: String SMTP_PASSWORD: String
SMTP_LOCAL_NAME: String
SENDER_EMAIL: String SENDER_EMAIL: String
JWT_TYPE: String JWT_TYPE: String
JWT_SECRET: String JWT_SECRET: String
@ -2101,6 +2118,7 @@ type Env {
type ValidateJWTTokenResponse { type ValidateJWTTokenResponse {
is_valid: Boolean! is_valid: Boolean!
claims: Map
} }
type GenerateJWTKeysResponse { type GenerateJWTKeysResponse {
@ -2168,6 +2186,7 @@ input UpdateEnvInput {
SMTP_PORT: String SMTP_PORT: String
SMTP_USERNAME: String SMTP_USERNAME: String
SMTP_PASSWORD: String SMTP_PASSWORD: String
SMTP_LOCAL_NAME: String
SENDER_EMAIL: String SENDER_EMAIL: String
JWT_TYPE: String JWT_TYPE: String
JWT_SECRET: String JWT_SECRET: String
@ -4442,6 +4461,47 @@ func (ec *executionContext) fieldContext_Env_SMTP_PASSWORD(ctx context.Context,
return fc, nil return fc, nil
} }
func (ec *executionContext) _Env_SMTP_LOCAL_NAME(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Env_SMTP_LOCAL_NAME(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.SMTPLocalName, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Env_SMTP_LOCAL_NAME(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Env",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Env_SENDER_EMAIL(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) { func (ec *executionContext) _Env_SENDER_EMAIL(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Env_SENDER_EMAIL(ctx, field) fc, err := ec.fieldContext_Env_SENDER_EMAIL(ctx, field)
if err != nil { if err != nil {
@ -9085,6 +9145,8 @@ func (ec *executionContext) fieldContext_Query_validate_jwt_token(ctx context.Co
switch field.Name { switch field.Name {
case "is_valid": case "is_valid":
return ec.fieldContext_ValidateJWTTokenResponse_is_valid(ctx, field) return ec.fieldContext_ValidateJWTTokenResponse_is_valid(ctx, field)
case "claims":
return ec.fieldContext_ValidateJWTTokenResponse_claims(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type ValidateJWTTokenResponse", field.Name) return nil, fmt.Errorf("no field named %q was found under type ValidateJWTTokenResponse", field.Name)
}, },
@ -9344,6 +9406,8 @@ func (ec *executionContext) fieldContext_Query__env(ctx context.Context, field g
return ec.fieldContext_Env_SMTP_USERNAME(ctx, field) return ec.fieldContext_Env_SMTP_USERNAME(ctx, field)
case "SMTP_PASSWORD": case "SMTP_PASSWORD":
return ec.fieldContext_Env_SMTP_PASSWORD(ctx, field) return ec.fieldContext_Env_SMTP_PASSWORD(ctx, field)
case "SMTP_LOCAL_NAME":
return ec.fieldContext_Env_SMTP_LOCAL_NAME(ctx, field)
case "SENDER_EMAIL": case "SENDER_EMAIL":
return ec.fieldContext_Env_SENDER_EMAIL(ctx, field) return ec.fieldContext_Env_SENDER_EMAIL(ctx, field)
case "JWT_TYPE": case "JWT_TYPE":
@ -10912,6 +10976,47 @@ func (ec *executionContext) fieldContext_ValidateJWTTokenResponse_is_valid(ctx c
return fc, nil return fc, nil
} }
func (ec *executionContext) _ValidateJWTTokenResponse_claims(ctx context.Context, field graphql.CollectedField, obj *model.ValidateJWTTokenResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ValidateJWTTokenResponse_claims(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Claims, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(map[string]interface{})
fc.Result = res
return ec.marshalOMap2map(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ValidateJWTTokenResponse_claims(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ValidateJWTTokenResponse",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Map does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _VerificationRequest_id(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) { func (ec *executionContext) _VerificationRequest_id(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_VerificationRequest_id(ctx, field) fc, err := ec.fieldContext_VerificationRequest_id(ctx, field)
if err != nil { if err != nil {
@ -14970,7 +15075,7 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"ACCESS_TOKEN_EXPIRY_TIME", "ADMIN_SECRET", "CUSTOM_ACCESS_TOKEN_SCRIPT", "OLD_ADMIN_SECRET", "SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD", "SENDER_EMAIL", "JWT_TYPE", "JWT_SECRET", "JWT_PRIVATE_KEY", "JWT_PUBLIC_KEY", "ALLOWED_ORIGINS", "APP_URL", "RESET_PASSWORD_URL", "APP_COOKIE_SECURE", "ADMIN_COOKIE_SECURE", "DISABLE_EMAIL_VERIFICATION", "DISABLE_BASIC_AUTHENTICATION", "DISABLE_MAGIC_LINK_LOGIN", "DISABLE_LOGIN_PAGE", "DISABLE_SIGN_UP", "DISABLE_REDIS_FOR_ENV", "DISABLE_STRONG_PASSWORD", "DISABLE_MULTI_FACTOR_AUTHENTICATION", "ENFORCE_MULTI_FACTOR_AUTHENTICATION", "ROLES", "PROTECTED_ROLES", "DEFAULT_ROLES", "JWT_ROLE_CLAIM", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", "FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET", "LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET", "APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "ORGANIZATION_NAME", "ORGANIZATION_LOGO"} fieldsInOrder := [...]string{"ACCESS_TOKEN_EXPIRY_TIME", "ADMIN_SECRET", "CUSTOM_ACCESS_TOKEN_SCRIPT", "OLD_ADMIN_SECRET", "SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD", "SMTP_LOCAL_NAME", "SENDER_EMAIL", "JWT_TYPE", "JWT_SECRET", "JWT_PRIVATE_KEY", "JWT_PUBLIC_KEY", "ALLOWED_ORIGINS", "APP_URL", "RESET_PASSWORD_URL", "APP_COOKIE_SECURE", "ADMIN_COOKIE_SECURE", "DISABLE_EMAIL_VERIFICATION", "DISABLE_BASIC_AUTHENTICATION", "DISABLE_MAGIC_LINK_LOGIN", "DISABLE_LOGIN_PAGE", "DISABLE_SIGN_UP", "DISABLE_REDIS_FOR_ENV", "DISABLE_STRONG_PASSWORD", "DISABLE_MULTI_FACTOR_AUTHENTICATION", "ENFORCE_MULTI_FACTOR_AUTHENTICATION", "ROLES", "PROTECTED_ROLES", "DEFAULT_ROLES", "JWT_ROLE_CLAIM", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", "FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET", "LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET", "APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "ORGANIZATION_NAME", "ORGANIZATION_LOGO"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@ -15041,6 +15146,14 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
if err != nil { if err != nil {
return it, err return it, err
} }
case "SMTP_LOCAL_NAME":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("SMTP_LOCAL_NAME"))
it.SMTPLocalName, err = ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
case "SENDER_EMAIL": case "SENDER_EMAIL":
var err error var err error
@ -16026,6 +16139,10 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj
out.Values[i] = ec._Env_SMTP_PASSWORD(ctx, field, obj) out.Values[i] = ec._Env_SMTP_PASSWORD(ctx, field, obj)
case "SMTP_LOCAL_NAME":
out.Values[i] = ec._Env_SMTP_LOCAL_NAME(ctx, field, obj)
case "SENDER_EMAIL": case "SENDER_EMAIL":
out.Values[i] = ec._Env_SENDER_EMAIL(ctx, field, obj) out.Values[i] = ec._Env_SENDER_EMAIL(ctx, field, obj)
@ -17288,6 +17405,10 @@ func (ec *executionContext) _ValidateJWTTokenResponse(ctx context.Context, sel a
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "claims":
out.Values[i] = ec._ValidateJWTTokenResponse_claims(ctx, field, obj)
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }

View File

@ -74,6 +74,7 @@ type Env struct {
SMTPPort *string `json:"SMTP_PORT"` SMTPPort *string `json:"SMTP_PORT"`
SMTPUsername *string `json:"SMTP_USERNAME"` SMTPUsername *string `json:"SMTP_USERNAME"`
SMTPPassword *string `json:"SMTP_PASSWORD"` SMTPPassword *string `json:"SMTP_PASSWORD"`
SMTPLocalName *string `json:"SMTP_LOCAL_NAME"`
SenderEmail *string `json:"SENDER_EMAIL"` SenderEmail *string `json:"SENDER_EMAIL"`
JwtType *string `json:"JWT_TYPE"` JwtType *string `json:"JWT_TYPE"`
JwtSecret *string `json:"JWT_SECRET"` JwtSecret *string `json:"JWT_SECRET"`
@ -271,6 +272,7 @@ type UpdateEnvInput struct {
SMTPPort *string `json:"SMTP_PORT"` SMTPPort *string `json:"SMTP_PORT"`
SMTPUsername *string `json:"SMTP_USERNAME"` SMTPUsername *string `json:"SMTP_USERNAME"`
SMTPPassword *string `json:"SMTP_PASSWORD"` SMTPPassword *string `json:"SMTP_PASSWORD"`
SMTPLocalName *string `json:"SMTP_LOCAL_NAME"`
SenderEmail *string `json:"SENDER_EMAIL"` SenderEmail *string `json:"SENDER_EMAIL"`
JwtType *string `json:"JWT_TYPE"` JwtType *string `json:"JWT_TYPE"`
JwtSecret *string `json:"JWT_SECRET"` JwtSecret *string `json:"JWT_SECRET"`
@ -384,7 +386,8 @@ type ValidateJWTTokenInput struct {
} }
type ValidateJWTTokenResponse struct { type ValidateJWTTokenResponse struct {
IsValid bool `json:"is_valid"` IsValid bool `json:"is_valid"`
Claims map[string]interface{} `json:"claims"`
} }
type VerificationRequest struct { type VerificationRequest struct {

View File

@ -110,6 +110,7 @@ type Env {
SMTP_PORT: String SMTP_PORT: String
SMTP_USERNAME: String SMTP_USERNAME: String
SMTP_PASSWORD: String SMTP_PASSWORD: String
SMTP_LOCAL_NAME: String
SENDER_EMAIL: String SENDER_EMAIL: String
JWT_TYPE: String JWT_TYPE: String
JWT_SECRET: String JWT_SECRET: String
@ -152,6 +153,7 @@ type Env {
type ValidateJWTTokenResponse { type ValidateJWTTokenResponse {
is_valid: Boolean! is_valid: Boolean!
claims: Map
} }
type GenerateJWTKeysResponse { type GenerateJWTKeysResponse {
@ -219,6 +221,7 @@ input UpdateEnvInput {
SMTP_PORT: String SMTP_PORT: String
SMTP_USERNAME: String SMTP_USERNAME: String
SMTP_PASSWORD: String SMTP_PASSWORD: String
SMTP_LOCAL_NAME: String
SENDER_EMAIL: String SENDER_EMAIL: String
JWT_TYPE: String JWT_TYPE: String
JWT_SECRET: String JWT_SECRET: String

View File

@ -30,7 +30,7 @@ func AppHandler() gin.HandlerFunc {
return return
} }
redirect_uri := strings.TrimSpace(c.Query("redirect_uri")) redirectURI := strings.TrimSpace(c.Query("redirect_uri"))
state := strings.TrimSpace(c.Query("state")) state := strings.TrimSpace(c.Query("state"))
scopeString := strings.TrimSpace(c.Query("scope")) scopeString := strings.TrimSpace(c.Query("scope"))
@ -41,11 +41,11 @@ func AppHandler() gin.HandlerFunc {
scope = strings.Split(scopeString, " ") scope = strings.Split(scopeString, " ")
} }
if redirect_uri == "" { if redirectURI == "" {
redirect_uri = hostname + "/app" redirectURI = hostname + "/app"
} else { } else {
// validate redirect url with allowed origins // validate redirect url with allowed origins
if !validators.IsValidOrigin(redirect_uri) { if !validators.IsValidOrigin(redirectURI) {
log.Debug("Invalid redirect_uri") log.Debug("Invalid redirect_uri")
c.JSON(400, gin.H{"error": "invalid redirect url"}) c.JSON(400, gin.H{"error": "invalid redirect url"})
return return
@ -75,7 +75,7 @@ func AppHandler() gin.HandlerFunc {
c.HTML(http.StatusOK, "app.tmpl", gin.H{ c.HTML(http.StatusOK, "app.tmpl", gin.H{
"data": map[string]interface{}{ "data": map[string]interface{}{
"authorizerURL": hostname, "authorizerURL": hostname,
"redirectURL": redirect_uri, "redirectURL": redirectURI,
"scope": scope, "scope": scope,
"state": state, "state": state,
"organizationName": orgName, "organizationName": orgName,

View File

@ -17,14 +17,14 @@ func OpenIDConfigurationHandler() gin.HandlerFunc {
c.JSON(200, gin.H{ c.JSON(200, gin.H{
"issuer": issuer, "issuer": issuer,
"authorization_endpoint": issuer + "/authorize", "authorization_endpoint": issuer + "/authorize",
"token_endpoint": issuer + "/token", "token_endpoint": issuer + "/oauth/token",
"userinfo_endpoint": issuer + "/userinfo", "userinfo_endpoint": issuer + "/userinfo",
"jwks_uri": issuer + "/.well-known/jwks.json", "jwks_uri": issuer + "/.well-known/jwks.json",
"response_types_supported": []string{"code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token"}, "response_types_supported": []string{"code", "token", "id_token"},
"scopes_supported": []string{"openid", "email", "profile", "email_verified", "given_name", "family_name", "nick_name", "picture"}, "scopes_supported": []string{"openid", "email", "profile", "email_verified", "given_name", "family_name", "nick_name", "picture"},
"response_modes_supported": []string{"query", "fragment", "form_post"}, "response_modes_supported": []string{"query", "fragment", "form_post", "web_message"},
"id_token_signing_alg_values_supported": []string{jwtType}, "id_token_signing_alg_values_supported": []string{jwtType},
"claims_supported": []string{"aud", "exp", "iss", "iat", "sub", "given_name", "family_name", "middle_name", "nickname", "preferred_username", "picture", "email", "email_verified", "roles", "gender", "birthdate", "phone_number", "phone_number_verified"}, "claims_supported": []string{"aud", "exp", "iss", "iat", "sub", "given_name", "family_name", "middle_name", "nickname", "preferred_username", "picture", "email", "email_verified", "roles", "role", "gender", "birthdate", "phone_number", "phone_number_verified", "nonce", "updated_at", "created_at", "revoked_timestamp", "login_method", "signup_methods", "token_type"},
}) })
} }
} }

View File

@ -15,12 +15,15 @@ import (
"github.com/authorizerdev/authorizer/server/routes" "github.com/authorizerdev/authorizer/server/routes"
) )
// VERSION is used to define the version of authorizer from build tags
var VERSION string var VERSION string
// LogUTCFormatter hels in setting UTC time format for the logs
type LogUTCFormatter struct { type LogUTCFormatter struct {
log.Formatter log.Formatter
} }
// Format helps fomratting time to UTC
func (u LogUTCFormatter) Format(e *log.Entry) ([]byte, error) { func (u LogUTCFormatter) Format(e *log.Entry) ([]byte, error) {
e.Time = e.Time.UTC() e.Time = e.Time.UTC()
return u.Formatter.Format(e) return u.Formatter.Format(e)

View File

@ -57,7 +57,7 @@ func InitMemStore() error {
} }
redisURL := requiredEnvs.RedisURL redisURL := requiredEnvs.RedisURL
if redisURL != "" && !requiredEnvs.disableRedisForEnv { if redisURL != "" && !requiredEnvs.DisableRedisForEnv {
log.Info("Initializing Redis memory store") log.Info("Initializing Redis memory store")
Provider, err = redis.NewRedisProvider(redisURL) Provider, err = redis.NewRedisProvider(redisURL)
if err != nil { if err != nil {

View File

@ -27,7 +27,11 @@ type RequiredEnv struct {
DatabaseCertKey string `json:"DATABASE_CERT_KEY"` DatabaseCertKey string `json:"DATABASE_CERT_KEY"`
DatabaseCACert string `json:"DATABASE_CA_CERT"` DatabaseCACert string `json:"DATABASE_CA_CERT"`
RedisURL string `json:"REDIS_URL"` RedisURL string `json:"REDIS_URL"`
disableRedisForEnv bool `json:"DISABLE_REDIS_FOR_ENV"` DisableRedisForEnv bool `json:"DISABLE_REDIS_FOR_ENV"`
// AWS Related Envs
AwsRegion string `json:"AWS_REGION"`
AwsAccessKeyID string `json:"AWS_ACCESS_KEY_ID"`
AwsSecretAccessKey string `json:"AWS_SECRET_ACCESS_KEY"`
} }
// RequiredEnvObj is a simple in-memory store for sessions. // RequiredEnvObj is a simple in-memory store for sessions.
@ -53,7 +57,8 @@ func (r *RequiredEnvStore) SetRequiredEnv(requiredEnv RequiredEnv) {
var RequiredEnvStoreObj *RequiredEnvStore var RequiredEnvStoreObj *RequiredEnvStore
// InitRequiredEnv to initialize EnvData and through error if required env are not present // InitRequiredEnv to initialize EnvData and throw error if required env are not present
// This includes env that only configurable via env vars and not the ui
func InitRequiredEnv() error { func InitRequiredEnv() error {
envPath := os.Getenv(constants.EnvKeyEnvPath) envPath := os.Getenv(constants.EnvKeyEnvPath)
@ -85,6 +90,9 @@ func InitRequiredEnv() error {
dbCACert := os.Getenv(constants.EnvKeyDatabaseCACert) dbCACert := os.Getenv(constants.EnvKeyDatabaseCACert)
redisURL := os.Getenv(constants.EnvKeyRedisURL) redisURL := os.Getenv(constants.EnvKeyRedisURL)
disableRedisForEnv := os.Getenv(constants.EnvKeyDisableRedisForEnv) == "true" disableRedisForEnv := os.Getenv(constants.EnvKeyDisableRedisForEnv) == "true"
awsRegion := os.Getenv(constants.EnvAwsRegion)
awsAccessKeyID := os.Getenv(constants.EnvAwsAccessKeyID)
awsSecretAccessKey := os.Getenv(constants.EnvAwsSecretAccessKey)
if strings.TrimSpace(redisURL) == "" { if strings.TrimSpace(redisURL) == "" {
if cli.ARG_REDIS_URL != nil && *cli.ARG_REDIS_URL != "" { if cli.ARG_REDIS_URL != nil && *cli.ARG_REDIS_URL != "" {
@ -139,7 +147,10 @@ func InitRequiredEnv() error {
DatabaseCertKey: dbCertKey, DatabaseCertKey: dbCertKey,
DatabaseCACert: dbCACert, DatabaseCACert: dbCACert,
RedisURL: redisURL, RedisURL: redisURL,
disableRedisForEnv: disableRedisForEnv, DisableRedisForEnv: disableRedisForEnv,
AwsRegion: awsRegion,
AwsAccessKeyID: awsAccessKeyID,
AwsSecretAccessKey: awsSecretAccessKey,
} }
RequiredEnvStoreObj = &RequiredEnvStore{ RequiredEnvStoreObj = &RequiredEnvStore{

View File

@ -89,6 +89,9 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
if val, ok := store[constants.EnvKeySenderEmail]; ok { if val, ok := store[constants.EnvKeySenderEmail]; ok {
res.SenderEmail = refs.NewStringRef(val.(string)) res.SenderEmail = refs.NewStringRef(val.(string))
} }
if val, ok := store[constants.EnvKeySmtpLocalName]; ok {
res.SMTPLocalName = refs.NewStringRef(val.(string))
}
if val, ok := store[constants.EnvKeyJwtType]; ok { if val, ok := store[constants.EnvKeyJwtType]; ok {
res.JwtType = refs.NewStringRef(val.(string)) res.JwtType = refs.NewStringRef(val.(string))
} }

View File

@ -62,12 +62,21 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
log.Debug("Failed to generate nonce: ", err) log.Debug("Failed to generate nonce: ", err)
return res, err return res, err
} }
redirectURL := parsers.GetAppURL(gc)
redirectURI := ""
// give higher preference to params redirect uri
if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" { if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" {
redirectURL = refs.StringValue(params.RedirectURI) redirectURI = refs.StringValue(params.RedirectURI)
} else {
redirectURI, err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
if err != nil {
log.Debug("ResetPasswordURL not found using default app url: ", err)
redirectURI = hostname + "/app/reset-password"
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, redirectURI)
}
} }
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL) verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURI)
if err != nil { if err != nil {
log.Debug("Failed to create verification token", err) log.Debug("Failed to create verification token", err)
return res, err return res, err
@ -78,7 +87,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
Email: params.Email, Email: params.Email,
Nonce: nonceHash, Nonce: nonceHash,
RedirectURI: redirectURL, RedirectURI: redirectURI,
}) })
if err != nil { if err != nil {
log.Debug("Failed to add verification request", err) log.Debug("Failed to add verification request", err)
@ -89,7 +98,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
go email.SendEmail([]string{params.Email}, constants.VerificationTypeForgotPassword, map[string]interface{}{ go email.SendEmail([]string{params.Email}, constants.VerificationTypeForgotPassword, map[string]interface{}{
"user": user.ToMap(), "user": user.ToMap(),
"organization": utils.GetOrganization(), "organization": utils.GetOrganization(),
"verification_url": utils.GetForgotPasswordURL(verificationToken, hostname), "verification_url": utils.GetForgotPasswordURL(verificationToken, redirectURI),
}) })
res = &model.Response{ res = &model.Response{

View File

@ -93,5 +93,6 @@ func ValidateJwtTokenResolver(ctx context.Context, params model.ValidateJWTToken
} }
return &model.ValidateJWTTokenResponse{ return &model.ValidateJWTTokenResponse{
IsValid: true, IsValid: true,
Claims: claims,
}, nil }, nil
} }

View File

@ -20,7 +20,7 @@ func TestResolvers(t *testing.T) {
constants.DbTypeArangodb: "http://localhost:8529", constants.DbTypeArangodb: "http://localhost:8529",
constants.DbTypeMongodb: "mongodb://localhost:27017", constants.DbTypeMongodb: "mongodb://localhost:27017",
constants.DbTypeScyllaDB: "127.0.0.1:9042", constants.DbTypeScyllaDB: "127.0.0.1:9042",
constants.DbTypeDynamoDB: "http://127.0.0.1:8000", constants.DbTypeDynamoDB: "http://0.0.0.0:8000",
constants.DbTypeCouchbaseDB: "couchbase://127.0.0.1", constants.DbTypeCouchbaseDB: "couchbase://127.0.0.1",
} }
@ -54,25 +54,33 @@ func TestResolvers(t *testing.T) {
os.Setenv(constants.EnvKeyDatabaseURL, dbURL) os.Setenv(constants.EnvKeyDatabaseURL, dbURL)
os.Setenv(constants.EnvKeyDatabaseType, dbType) os.Setenv(constants.EnvKeyDatabaseType, dbType)
os.Setenv(constants.EnvKeyDatabaseName, testDb) os.Setenv(constants.EnvKeyDatabaseName, testDb)
if dbType == constants.DbTypeDynamoDB {
memorystore.Provider.UpdateEnvVariable(constants.EnvAwsRegion, "ap-south-1")
os.Setenv(constants.EnvAwsRegion, "ap-south-1")
}
memorystore.InitRequiredEnv() memorystore.InitRequiredEnv()
err := db.InitDB() err := db.InitDB()
if err != nil { if err != nil {
t.Errorf("Error initializing database: %s", err.Error()) t.Logf("Error initializing database: %s", err.Error())
} }
// clean the persisted config for test to use fresh config // clean the persisted config for test to use fresh config
envData, err := db.Provider.GetEnv(ctx) envData, err := db.Provider.GetEnv(ctx)
if err == nil { if err == nil && envData.ID != "" {
envData.EnvData = "" envData.EnvData = ""
_, err = db.Provider.UpdateEnv(ctx, envData) _, err = db.Provider.UpdateEnv(ctx, envData)
if err != nil { if err != nil {
t.Errorf("Error updating env: %s", err.Error()) t.Logf("Error updating env: %s", err.Error())
} }
} else if err != nil {
t.Logf("Error getting env: %s", err.Error())
} }
err = env.PersistEnv() err = env.PersistEnv()
if err != nil { if err != nil {
t.Errorf("Error persisting env: %s", err.Error()) t.Logf("Error persisting env: %s", err.Error())
} }
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEnv, "test") memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEnv, "test")

View File

@ -84,7 +84,7 @@ func testSetup() TestSetup {
testData := TestData{ testData := TestData{
Email: fmt.Sprintf("%d_authorizer_tester@yopmail.com", time.Now().Unix()), Email: fmt.Sprintf("%d_authorizer_tester@yopmail.com", time.Now().Unix()),
Password: "Test@123", Password: "Test@123",
WebhookEndpoint: "https://62cbc6738042b16aa7c22df2.mockapi.io/api/v1/webhook", WebhookEndpoint: "https://62f93101e05644803533cf36.mockapi.io/authorizer/webhook",
TestWebhookEventTypes: []string{constants.UserAccessEnabledWebhookEvent, constants.UserAccessRevokedWebhookEvent, constants.UserCreatedWebhookEvent, constants.UserDeletedWebhookEvent, constants.UserLoginWebhookEvent, constants.UserSignUpWebhookEvent}, TestWebhookEventTypes: []string{constants.UserAccessEnabledWebhookEvent, constants.UserAccessRevokedWebhookEvent, constants.UserCreatedWebhookEvent, constants.UserDeletedWebhookEvent, constants.UserLoginWebhookEvent, constants.UserSignUpWebhookEvent},
TestEmailTemplateEventTypes: []string{constants.VerificationTypeBasicAuthSignup, constants.VerificationTypeForgotPassword, constants.VerificationTypeMagicLinkLogin, constants.VerificationTypeUpdateEmail}, TestEmailTemplateEventTypes: []string{constants.VerificationTypeBasicAuthSignup, constants.VerificationTypeForgotPassword, constants.VerificationTypeMagicLinkLogin, constants.VerificationTypeUpdateEmail},
} }

View File

@ -93,5 +93,6 @@ func validateJwtTokenTest(t *testing.T, s TestSetup) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, res.IsValid) assert.True(t, res.IsValid)
assert.Equal(t, user.Email, res.Claims["email"])
}) })
} }

View File

@ -81,17 +81,8 @@ func GetOrganization() map[string]interface{} {
} }
// GetForgotPasswordURL to get url for given token and hostname // GetForgotPasswordURL to get url for given token and hostname
func GetForgotPasswordURL(token, hostname string) string { func GetForgotPasswordURL(token, redirectURI string) string {
resetPasswordUrl, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL) verificationURL := redirectURI + "?token=" + token
if err != nil {
return ""
}
if resetPasswordUrl == "" {
if err := memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, hostname+"/app/reset-password"); err != nil {
return ""
}
}
verificationURL := resetPasswordUrl + "?token=" + token
return verificationURL return verificationURL
} }