Compare commits
14 Commits
feat/plain
...
1.1.21.bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2b52932e98 | ||
![]() |
c716638725 | ||
![]() |
252cd1fa2d | ||
![]() |
7c2693b086 | ||
![]() |
eaa10ec5bc | ||
![]() |
253128ca0c | ||
![]() |
cddfe1e088 | ||
![]() |
8e655bcb5b | ||
![]() |
9a411e673c | ||
![]() |
346c8e5a47 | ||
![]() |
3cd99fe5f6 | ||
![]() |
2bd92d6028 | ||
![]() |
ff805e3ef2 | ||
![]() |
0115128ee7 |
@@ -6,5 +6,4 @@ SMTP_HOST=smtp.mailtrap.io
|
|||||||
SMTP_PORT=2525
|
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
|
|
8
Makefile
8
Makefile
@@ -23,20 +23,14 @@ test-arangodb:
|
|||||||
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
|
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
|
||||||
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
|
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
|
||||||
docker rm -vf authorizer_arangodb
|
docker rm -vf authorizer_arangodb
|
||||||
test-dynamodb:
|
|
||||||
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
|
|
||||||
cd server && go clean --testcache && TEST_DBS="dynamodb" go test -p 1 -v ./test
|
|
||||||
docker rm -vf dynamodb-local-test
|
|
||||||
test-all-db:
|
test-all-db:
|
||||||
rm -rf server/test/test.db && rm -rf test.db
|
rm -rf server/test/test.db && rm -rf test.db
|
||||||
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
|
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
|
||||||
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
|
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
|
||||||
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
|
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
|
||||||
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
|
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb" go test -p 1 -v ./test
|
||||||
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb,dynamodb" go test -p 1 -v ./test
|
|
||||||
docker rm -vf authorizer_scylla_db
|
docker rm -vf authorizer_scylla_db
|
||||||
docker rm -vf authorizer_mongodb_db
|
docker rm -vf authorizer_mongodb_db
|
||||||
docker rm -vf authorizer_arangodb
|
docker rm -vf authorizer_arangodb
|
||||||
docker rm -vf dynamodb-local-test
|
|
||||||
generate:
|
generate:
|
||||||
cd server && go run github.com/99designs/gqlgen generate && go mod tidy
|
cd server && go run github.com/99designs/gqlgen generate && go mod tidy
|
||||||
|
@@ -60,12 +60,7 @@ 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
|
<AuthorizerForgotPassword urlProps={urlProps} />
|
||||||
urlProps={{
|
|
||||||
...urlProps,
|
|
||||||
redirect_uri: `${window.location.origin}/app/reset-password`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Footer>
|
<Footer>
|
||||||
<Link
|
<Link
|
||||||
to="#"
|
to="#"
|
||||||
|
1079
dashboard/package-lock.json
generated
1079
dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
@@ -20,14 +20,13 @@ 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,
|
||||||
@@ -58,9 +57,8 @@ 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] = useState<userDataTypes>({
|
const [userData, setUserData] = React.useState<userDataTypes>({
|
||||||
id: '',
|
id: '',
|
||||||
email: '',
|
email: '',
|
||||||
given_name: '',
|
given_name: '',
|
||||||
@@ -75,17 +73,7 @@ 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(
|
||||||
@@ -233,8 +221,7 @@ const EditUserModal = ({
|
|||||||
<InputField
|
<InputField
|
||||||
variables={userData}
|
variables={userData}
|
||||||
setVariables={setUserData}
|
setVariables={setUserData}
|
||||||
availableRoles={availableRoles}
|
inputType={ArrayInputType.USER_ROLES}
|
||||||
inputType={MultiSelectInputType.USER_ROLES}
|
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@@ -48,26 +48,6 @@ 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%'}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
@@ -13,12 +13,6 @@ 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,
|
||||||
@@ -26,7 +20,6 @@ import {
|
|||||||
FaRegEyeSlash,
|
FaRegEyeSlash,
|
||||||
FaPlus,
|
FaPlus,
|
||||||
FaTimes,
|
FaTimes,
|
||||||
FaAngleDown,
|
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import {
|
import {
|
||||||
ArrayInputOperations,
|
ArrayInputOperations,
|
||||||
@@ -37,7 +30,6 @@ import {
|
|||||||
TextAreaInputType,
|
TextAreaInputType,
|
||||||
SwitchInputType,
|
SwitchInputType,
|
||||||
DateInputType,
|
DateInputType,
|
||||||
MultiSelectInputType,
|
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { copyTextToClipboard } from '../utils';
|
import { copyTextToClipboard } from '../utils';
|
||||||
|
|
||||||
@@ -47,16 +39,13 @@ const InputField = ({
|
|||||||
setVariables,
|
setVariables,
|
||||||
fieldVisibility,
|
fieldVisibility,
|
||||||
setFieldVisibility,
|
setFieldVisibility,
|
||||||
availableRoles,
|
|
||||||
...downshiftProps
|
...downshiftProps
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const props = {
|
const props = {
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
...downshiftProps,
|
...downshiftProps,
|
||||||
};
|
};
|
||||||
const [availableUserRoles, setAvailableUserRoles] =
|
const [inputFieldVisibility, setInputFieldVisibility] = React.useState<
|
||||||
useState<string[]>(availableRoles);
|
|
||||||
const [inputFieldVisibility, setInputFieldVisibility] = useState<
|
|
||||||
Record<string, boolean>
|
Record<string, boolean>
|
||||||
>({
|
>({
|
||||||
ROLES: false,
|
ROLES: false,
|
||||||
@@ -65,7 +54,7 @@ const InputField = ({
|
|||||||
ALLOWED_ORIGINS: false,
|
ALLOWED_ORIGINS: false,
|
||||||
roles: false,
|
roles: false,
|
||||||
});
|
});
|
||||||
const [inputData, setInputData] = useState<Record<string, string>>({
|
const [inputData, setInputData] = React.useState<Record<string, string>>({
|
||||||
ROLES: '',
|
ROLES: '',
|
||||||
DEFAULT_ROLES: '',
|
DEFAULT_ROLES: '',
|
||||||
PROTECTED_ROLES: '',
|
PROTECTED_ROLES: '',
|
||||||
@@ -127,7 +116,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;
|
||||||
@@ -232,7 +221,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 });
|
||||||
}}
|
}}
|
||||||
@@ -289,87 +278,6 @@ 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
|
||||||
|
@@ -29,10 +29,6 @@ import {
|
|||||||
Tbody,
|
Tbody,
|
||||||
Td,
|
Td,
|
||||||
Code,
|
Code,
|
||||||
Radio,
|
|
||||||
RadioGroup,
|
|
||||||
Stack,
|
|
||||||
Textarea,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa';
|
import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa';
|
||||||
import { useClient } from 'urql';
|
import { useClient } from 'urql';
|
||||||
@@ -42,7 +38,6 @@ import {
|
|||||||
EmailTemplateInputDataFields,
|
EmailTemplateInputDataFields,
|
||||||
emailTemplateEventNames,
|
emailTemplateEventNames,
|
||||||
emailTemplateVariables,
|
emailTemplateVariables,
|
||||||
EmailTemplateEditors,
|
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { capitalizeFirstLetter } from '../utils';
|
import { capitalizeFirstLetter } from '../utils';
|
||||||
import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation';
|
import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation';
|
||||||
@@ -71,8 +66,6 @@ interface templateVariableDataTypes {
|
|||||||
interface emailTemplateDataType {
|
interface emailTemplateDataType {
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]: string;
|
[EmailTemplateInputDataFields.EVENT_NAME]: string;
|
||||||
[EmailTemplateInputDataFields.SUBJECT]: string;
|
[EmailTemplateInputDataFields.SUBJECT]: string;
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: string;
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface validatorDataType {
|
interface validatorDataType {
|
||||||
@@ -82,8 +75,6 @@ interface validatorDataType {
|
|||||||
const initTemplateData: emailTemplateDataType = {
|
const initTemplateData: emailTemplateDataType = {
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]: emailTemplateEventNames.Signup,
|
[EmailTemplateInputDataFields.EVENT_NAME]: emailTemplateEventNames.Signup,
|
||||||
[EmailTemplateInputDataFields.SUBJECT]: '',
|
[EmailTemplateInputDataFields.SUBJECT]: '',
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const initTemplateValidatorData: validatorDataType = {
|
const initTemplateValidatorData: validatorDataType = {
|
||||||
@@ -100,9 +91,6 @@ const UpdateEmailTemplate = ({
|
|||||||
const emailEditorRef = useRef(null);
|
const emailEditorRef = useRef(null);
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [editor, setEditor] = useState<string>(
|
|
||||||
EmailTemplateEditors.PLAIN_HTML_EDITOR,
|
|
||||||
);
|
|
||||||
const [templateVariables, setTemplateVariables] = useState<
|
const [templateVariables, setTemplateVariables] = useState<
|
||||||
templateVariableDataTypes[]
|
templateVariableDataTypes[]
|
||||||
>([]);
|
>([]);
|
||||||
@@ -119,11 +107,9 @@ const UpdateEmailTemplate = ({
|
|||||||
if (selectedTemplate) {
|
if (selectedTemplate) {
|
||||||
const { design } = selectedTemplate;
|
const { design } = selectedTemplate;
|
||||||
try {
|
try {
|
||||||
if (design) {
|
const designData = JSON.parse(design);
|
||||||
const designData = JSON.parse(design);
|
// @ts-ignore
|
||||||
// @ts-ignore
|
emailEditorRef.current.editor.loadDesign(designData);
|
||||||
emailEditorRef.current.editor.loadDesign(designData);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
onClose();
|
onClose();
|
||||||
@@ -150,85 +136,70 @@ const UpdateEmailTemplate = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateTemplate = async (params: emailTemplateDataType) => {
|
|
||||||
let res: any = {};
|
|
||||||
if (
|
|
||||||
view === UpdateModalViews.Edit &&
|
|
||||||
selectedTemplate?.[EmailTemplateInputDataFields.ID]
|
|
||||||
) {
|
|
||||||
res = await client
|
|
||||||
.mutation(EditEmailTemplate, {
|
|
||||||
params: {
|
|
||||||
...params,
|
|
||||||
id: selectedTemplate[EmailTemplateInputDataFields.ID],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.toPromise();
|
|
||||||
} else {
|
|
||||||
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
|
|
||||||
}
|
|
||||||
setLoading(false);
|
|
||||||
if (res.error) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(res.error.message),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'error',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
} else if (
|
|
||||||
res.data?._add_email_template ||
|
|
||||||
res.data?._update_email_template
|
|
||||||
) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(
|
|
||||||
res.data?._add_email_template?.message ||
|
|
||||||
res.data?._update_email_template?.message,
|
|
||||||
),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'success',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
setTemplateData({
|
|
||||||
...initTemplateData,
|
|
||||||
});
|
|
||||||
setValidator({ ...initTemplateValidatorData });
|
|
||||||
fetchEmailTemplatesData();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveData = async () => {
|
const saveData = async () => {
|
||||||
if (!validateData()) return;
|
if (!validateData()) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let params: emailTemplateDataType = {
|
// @ts-ignore
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]:
|
return await emailEditorRef.current.editor.exportHtml(async (data) => {
|
||||||
templateData[EmailTemplateInputDataFields.EVENT_NAME],
|
const { design, html } = data;
|
||||||
[EmailTemplateInputDataFields.SUBJECT]:
|
if (!html || !design) {
|
||||||
templateData[EmailTemplateInputDataFields.SUBJECT],
|
setLoading(false);
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]:
|
return;
|
||||||
templateData[EmailTemplateInputDataFields.TEMPLATE],
|
}
|
||||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
const params = {
|
||||||
};
|
[EmailTemplateInputDataFields.EVENT_NAME]:
|
||||||
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
templateData[EmailTemplateInputDataFields.EVENT_NAME],
|
||||||
// @ts-ignore
|
[EmailTemplateInputDataFields.SUBJECT]:
|
||||||
await emailEditorRef.current.editor.exportHtml(async (data) => {
|
templateData[EmailTemplateInputDataFields.SUBJECT],
|
||||||
const { design, html } = data;
|
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
|
||||||
if (!html || !design) {
|
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
|
||||||
setLoading(false);
|
};
|
||||||
return;
|
let res: any = {};
|
||||||
}
|
if (
|
||||||
params = {
|
view === UpdateModalViews.Edit &&
|
||||||
...params,
|
selectedTemplate?.[EmailTemplateInputDataFields.ID]
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
|
) {
|
||||||
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
|
res = await client
|
||||||
};
|
.mutation(EditEmailTemplate, {
|
||||||
await updateTemplate(params);
|
params: {
|
||||||
});
|
...params,
|
||||||
} else {
|
id: selectedTemplate[EmailTemplateInputDataFields.ID],
|
||||||
await updateTemplate(params);
|
},
|
||||||
}
|
})
|
||||||
view === UpdateModalViews.ADD && onClose();
|
.toPromise();
|
||||||
|
} else {
|
||||||
|
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
if (res.error) {
|
||||||
|
toast({
|
||||||
|
title: capitalizeFirstLetter(res.error.message),
|
||||||
|
isClosable: true,
|
||||||
|
status: 'error',
|
||||||
|
position: 'bottom-right',
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
res.data?._add_email_template ||
|
||||||
|
res.data?._update_email_template
|
||||||
|
) {
|
||||||
|
toast({
|
||||||
|
title: capitalizeFirstLetter(
|
||||||
|
res.data?._add_email_template?.message ||
|
||||||
|
res.data?._update_email_template?.message,
|
||||||
|
),
|
||||||
|
isClosable: true,
|
||||||
|
status: 'success',
|
||||||
|
position: 'bottom-right',
|
||||||
|
});
|
||||||
|
setTemplateData({
|
||||||
|
...initTemplateData,
|
||||||
|
});
|
||||||
|
setValidator({ ...initTemplateValidatorData });
|
||||||
|
fetchEmailTemplatesData();
|
||||||
|
}
|
||||||
|
view === UpdateModalViews.ADD && onClose();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetData = () => {
|
const resetData = () => {
|
||||||
if (selectedTemplate) {
|
if (selectedTemplate) {
|
||||||
setTemplateData(selectedTemplate);
|
setTemplateData(selectedTemplate);
|
||||||
@@ -236,8 +207,6 @@ const UpdateEmailTemplate = ({
|
|||||||
setTemplateData({ ...initTemplateData });
|
setTemplateData({ ...initTemplateData });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// set template data if edit modal is open
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
isOpen &&
|
isOpen &&
|
||||||
@@ -245,12 +214,10 @@ const UpdateEmailTemplate = ({
|
|||||||
selectedTemplate &&
|
selectedTemplate &&
|
||||||
Object.keys(selectedTemplate || {}).length
|
Object.keys(selectedTemplate || {}).length
|
||||||
) {
|
) {
|
||||||
const { id, created_at, ...rest } = selectedTemplate;
|
const { id, created_at, template, design, ...rest } = selectedTemplate;
|
||||||
setTemplateData(rest);
|
setTemplateData(rest);
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
// set template variables
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updatedTemplateVariables = Object.entries(
|
const updatedTemplateVariables = Object.entries(
|
||||||
emailTemplateVariables,
|
emailTemplateVariables,
|
||||||
@@ -277,51 +244,6 @@ const UpdateEmailTemplate = ({
|
|||||||
setTemplateVariables(updatedTemplateVariables);
|
setTemplateVariables(updatedTemplateVariables);
|
||||||
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
|
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
|
||||||
|
|
||||||
// change editor
|
|
||||||
useEffect(() => {
|
|
||||||
if (isOpen && selectedTemplate) {
|
|
||||||
const { design } = selectedTemplate;
|
|
||||||
if (design) {
|
|
||||||
setEditor(EmailTemplateEditors.UNLAYER_EDITOR);
|
|
||||||
} else {
|
|
||||||
setEditor(EmailTemplateEditors.PLAIN_HTML_EDITOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [isOpen, selectedTemplate]);
|
|
||||||
|
|
||||||
// reset fields when editor is changed
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedTemplate?.design) {
|
|
||||||
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
|
||||||
setTemplateData({
|
|
||||||
...templateData,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate.template,
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: selectedTemplate.design,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setTemplateData({
|
|
||||||
...templateData,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (selectedTemplate?.template) {
|
|
||||||
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
|
||||||
setTemplateData({
|
|
||||||
...templateData,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setTemplateData({
|
|
||||||
...templateData,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate?.template,
|
|
||||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [editor]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{view === UpdateModalViews.ADD ? (
|
{view === UpdateModalViews.ADD ? (
|
||||||
@@ -492,22 +414,7 @@ const UpdateEmailTemplate = ({
|
|||||||
alignItems="center"
|
alignItems="center"
|
||||||
marginBottom="2%"
|
marginBottom="2%"
|
||||||
>
|
>
|
||||||
<Flex flex="1">Template Body</Flex>
|
Template Body
|
||||||
<Flex flex="3">
|
|
||||||
<RadioGroup
|
|
||||||
onChange={(value) => setEditor(value)}
|
|
||||||
value={editor}
|
|
||||||
>
|
|
||||||
<Stack direction="row" spacing="50px">
|
|
||||||
<Radio value={EmailTemplateEditors.PLAIN_HTML_EDITOR}>
|
|
||||||
Plain HTML
|
|
||||||
</Radio>
|
|
||||||
<Radio value={EmailTemplateEditors.UNLAYER_EDITOR}>
|
|
||||||
Unlayer Editor
|
|
||||||
</Radio>
|
|
||||||
</Stack>
|
|
||||||
</RadioGroup>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex
|
<Flex
|
||||||
width="100%"
|
width="100%"
|
||||||
@@ -516,22 +423,7 @@ const UpdateEmailTemplate = ({
|
|||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="gray.200"
|
borderColor="gray.200"
|
||||||
>
|
>
|
||||||
{editor === EmailTemplateEditors.UNLAYER_EDITOR ? (
|
<EmailEditor ref={emailEditorRef} onReady={onReady} />
|
||||||
<EmailEditor ref={emailEditorRef} onReady={onReady} />
|
|
||||||
) : (
|
|
||||||
<Textarea
|
|
||||||
value={templateData.template}
|
|
||||||
onChange={(e) => {
|
|
||||||
setTemplateData({
|
|
||||||
...templateData,
|
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: e.target.value,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
placeholder="Template HTML"
|
|
||||||
border="0"
|
|
||||||
height="500px"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
@@ -15,7 +15,6 @@ 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',
|
||||||
@@ -49,6 +48,7 @@ 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,10 +56,6 @@ 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',
|
||||||
@@ -133,7 +129,6 @@ 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;
|
||||||
@@ -337,8 +332,3 @@ export const webhookPayloadExample: string = `{
|
|||||||
},
|
},
|
||||||
"auth_recipe":"google"
|
"auth_recipe":"google"
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
export enum EmailTemplateEditors {
|
|
||||||
UNLAYER_EDITOR = 'unlayer_editor',
|
|
||||||
PLAIN_HTML_EDITOR = 'plain_html_editor',
|
|
||||||
}
|
|
||||||
|
@@ -45,7 +45,6 @@ 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
|
||||||
@@ -170,12 +169,3 @@ export const WebhookLogsQuery = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GetAvailableRolesQuery = `
|
|
||||||
query {
|
|
||||||
_env {
|
|
||||||
ROLES
|
|
||||||
PROTECTED_ROLES
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
@@ -65,7 +65,6 @@ 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: '',
|
||||||
|
@@ -25,6 +25,4 @@ const (
|
|||||||
DbTypeCockroachDB = "cockroachdb"
|
DbTypeCockroachDB = "cockroachdb"
|
||||||
// DbTypePlanetScaleDB is the planetscale database type
|
// DbTypePlanetScaleDB is the planetscale database type
|
||||||
DbTypePlanetScaleDB = "planetscale"
|
DbTypePlanetScaleDB = "planetscale"
|
||||||
// DbTypeDynamoDB is the Dynamo database type
|
|
||||||
DbTypeDynamoDB = "dynamodb"
|
|
||||||
)
|
)
|
||||||
|
@@ -21,12 +21,6 @@ const (
|
|||||||
EnvKeyDatabaseType = "DATABASE_TYPE"
|
EnvKeyDatabaseType = "DATABASE_TYPE"
|
||||||
// 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 = "AWS_REGION"
|
|
||||||
// EnvAwsAccessKeyID key for env variable AWS_ACCESS_KEY_ID
|
|
||||||
EnvAwsAccessKeyID = "AWS_ACCESS_KEY_ID"
|
|
||||||
// EnvAwsAccessKey key for env variable AWS_SECRET_ACCESS_KEY
|
|
||||||
EnvAwsSecretAccessKey = "AWS_SECRET_ACCESS_KEY"
|
|
||||||
// EnvKeyDatabaseName key for env variable DATABASE_NAME
|
// EnvKeyDatabaseName key for env variable DATABASE_NAME
|
||||||
EnvKeyDatabaseName = "DATABASE_NAME"
|
EnvKeyDatabaseName = "DATABASE_NAME"
|
||||||
// EnvKeyDatabaseUsername key for env variable DATABASE_USERNAME
|
// EnvKeyDatabaseUsername key for env variable DATABASE_USERNAME
|
||||||
@@ -51,8 +45,6 @@ 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
|
||||||
|
19
server/constants/oauth2.go
Normal file
19
server/constants/oauth2.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
const (
|
||||||
|
// - query: for Authorization Code grant. 302 Found triggers redirect.
|
||||||
|
ResponseModeQuery = "query"
|
||||||
|
// - fragment: for Implicit grant. 302 Found triggers redirect.
|
||||||
|
ResponseModeFragment = "fragment"
|
||||||
|
// - form_post: 200 OK with response parameters embedded in an HTML form as hidden parameters.
|
||||||
|
ResponseModeFormPost = "form_post"
|
||||||
|
// - web_message: For Silent Authentication. Uses HTML5 web messaging.
|
||||||
|
ResponseModeWebMessage = "web_message"
|
||||||
|
|
||||||
|
// For the Authorization Code grant, use response_type=code to include the authorization code.
|
||||||
|
ResponseTypeCode = "code"
|
||||||
|
// For the Implicit grant, use response_type=token to include an access token.
|
||||||
|
ResponseTypeToken = "token"
|
||||||
|
// For the Implicit grant of id_token, use response_type=id_token to include an identifier token.
|
||||||
|
ResponseTypeIDToken = "id_token"
|
||||||
|
)
|
@@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/db/providers"
|
"github.com/authorizerdev/authorizer/server/db/providers"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/arangodb"
|
"github.com/authorizerdev/authorizer/server/db/providers/arangodb"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/cassandradb"
|
"github.com/authorizerdev/authorizer/server/db/providers/cassandradb"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/dynamodb"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/mongodb"
|
"github.com/authorizerdev/authorizer/server/db/providers/mongodb"
|
||||||
"github.com/authorizerdev/authorizer/server/db/providers/sql"
|
"github.com/authorizerdev/authorizer/server/db/providers/sql"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
@@ -21,11 +20,10 @@ func InitDB() error {
|
|||||||
|
|
||||||
envs := memorystore.RequiredEnvStoreObj.GetRequiredEnv()
|
envs := memorystore.RequiredEnvStoreObj.GetRequiredEnv()
|
||||||
|
|
||||||
isSQL := envs.DatabaseType != constants.DbTypeArangodb && envs.DatabaseType != constants.DbTypeMongodb && envs.DatabaseType != constants.DbTypeCassandraDB && envs.DatabaseType != constants.DbTypeScyllaDB && envs.DatabaseType != constants.DbTypeDynamoDB
|
isSQL := envs.DatabaseType != constants.DbTypeArangodb && envs.DatabaseType != constants.DbTypeMongodb && envs.DatabaseType != constants.DbTypeCassandraDB && envs.DatabaseType != constants.DbTypeScyllaDB
|
||||||
isArangoDB := envs.DatabaseType == constants.DbTypeArangodb
|
isArangoDB := envs.DatabaseType == constants.DbTypeArangodb
|
||||||
isMongoDB := envs.DatabaseType == constants.DbTypeMongodb
|
isMongoDB := envs.DatabaseType == constants.DbTypeMongodb
|
||||||
isCassandra := envs.DatabaseType == constants.DbTypeCassandraDB || envs.DatabaseType == constants.DbTypeScyllaDB
|
isCassandra := envs.DatabaseType == constants.DbTypeCassandraDB || envs.DatabaseType == constants.DbTypeScyllaDB
|
||||||
isDynamoDB := envs.DatabaseType == constants.DbTypeDynamoDB
|
|
||||||
|
|
||||||
if isSQL {
|
if isSQL {
|
||||||
log.Info("Initializing SQL Driver for: ", envs.DatabaseType)
|
log.Info("Initializing SQL Driver for: ", envs.DatabaseType)
|
||||||
@@ -63,14 +61,5 @@ func InitDB() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDynamoDB {
|
|
||||||
log.Info("Initializing DynamoDB Driver for: ", envs.DatabaseType)
|
|
||||||
Provider, err = dynamodb.NewProvider()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to initialize DynamoDB driver: ", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -9,14 +9,14 @@ import (
|
|||||||
|
|
||||||
// EmailTemplate model for database
|
// EmailTemplate model for database
|
||||||
type EmailTemplate struct {
|
type EmailTemplate struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"`
|
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
|
||||||
Subject string `json:"subject" bson:"subject" cql:"subject" dynamo:"subject"`
|
Subject string `json:"subject" bson:"subject" cql:"subject"`
|
||||||
Template string `json:"template" bson:"template" cql:"template" dynamo:"template"`
|
Template string `json:"template" bson:"template" cql:"template"`
|
||||||
Design string `json:"design" bson:"design" cql:"design" dynamo:"design"`
|
Design string `json:"design" bson:"design" cql:"design"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsAPIEmailTemplate to return email template as graphql response object
|
// AsAPIEmailTemplate to return email template as graphql response object
|
||||||
|
@@ -4,10 +4,10 @@ package models
|
|||||||
|
|
||||||
// Env model for db
|
// Env model for db
|
||||||
type Env struct {
|
type Env struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
EnvData string `json:"env" bson:"env" cql:"env" dynamo:"env"`
|
EnvData string `json:"env" bson:"env" cql:"env"`
|
||||||
Hash string `json:"hash" bson:"hash" cql:"hash" dynamo:"hash"`
|
Hash string `json:"hash" bson:"hash" cql:"hash"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
}
|
}
|
||||||
|
@@ -2,15 +2,11 @@ package models
|
|||||||
|
|
||||||
// OTP model for database
|
// OTP model for database
|
||||||
type OTP struct {
|
type OTP struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
Email string `gorm:"unique" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
|
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
|
||||||
Otp string `json:"otp" bson:"otp" cql:"otp" dynamo:"otp"`
|
Otp string `json:"otp" bson:"otp" cql:"otp"`
|
||||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
|
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
}
|
|
||||||
|
|
||||||
type Paging struct {
|
|
||||||
ID string `json:"id,omitempty" dynamo:"id,hash"`
|
|
||||||
}
|
}
|
||||||
|
@@ -4,11 +4,11 @@ package models
|
|||||||
|
|
||||||
// Session model for db
|
// Session model for db
|
||||||
type Session struct {
|
type Session struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" index:"user_id,hash"`
|
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id"`
|
||||||
UserAgent string `json:"user_agent" bson:"user_agent" cql:"user_agent" dynamo:"user_agent"`
|
UserAgent string `json:"user_agent" bson:"user_agent" cql:"user_agent"`
|
||||||
IP string `json:"ip" bson:"ip" cql:"ip" dynamo:"ip"`
|
IP string `json:"ip" bson:"ip" cql:"ip"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
}
|
}
|
||||||
|
@@ -12,27 +12,27 @@ import (
|
|||||||
|
|
||||||
// User model for db
|
// User model for db
|
||||||
type User struct {
|
type User struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
|
|
||||||
Email string `gorm:"unique" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
|
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
|
||||||
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at" dynamo:"email_verified_at"`
|
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at"`
|
||||||
Password *string `json:"password" bson:"password" cql:"password" dynamo:"password"`
|
Password *string `json:"password" bson:"password" cql:"password"`
|
||||||
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods" dynamo:"signup_methods"`
|
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
|
||||||
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name" dynamo:"given_name"`
|
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
|
||||||
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name" dynamo:"family_name"`
|
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
|
||||||
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name" dynamo:"middle_name"`
|
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name"`
|
||||||
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname" dynamo:"nickname"`
|
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname"`
|
||||||
Gender *string `json:"gender" bson:"gender" cql:"gender" dynamo:"gender"`
|
Gender *string `json:"gender" bson:"gender" cql:"gender"`
|
||||||
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate" dynamo:"birthdate"`
|
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
|
||||||
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
|
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number"`
|
||||||
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at" dynamo:"phone_number_verified_at"`
|
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"`
|
||||||
Picture *string `json:"picture" bson:"picture" cql:"picture" dynamo:"picture"`
|
Picture *string `json:"picture" bson:"picture" cql:"picture"`
|
||||||
Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"`
|
Roles string `json:"roles" bson:"roles" cql:"roles"`
|
||||||
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp" dynamo:"revoked_timestamp"`
|
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"`
|
||||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled" dynamo:"is_multi_factor_auth_enabled"`
|
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) AsAPIUser() *model.User {
|
func (user *User) AsAPIUser() *model.User {
|
||||||
|
@@ -11,16 +11,16 @@ import (
|
|||||||
|
|
||||||
// VerificationRequest model for db
|
// VerificationRequest model for db
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
Token string `json:"token" bson:"token" cql:"jwt_token" dynamo:"token" index:"token,hash"`
|
Token string `json:"token" bson:"token" cql:"jwt_token"` // token is reserved keyword in cassandra
|
||||||
Identifier string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(64)" json:"identifier" bson:"identifier" cql:"identifier" dynamo:"identifier"`
|
Identifier string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(64)" json:"identifier" bson:"identifier" cql:"identifier"`
|
||||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
|
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"`
|
||||||
Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email" dynamo:"email"`
|
Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email"`
|
||||||
Nonce string `json:"nonce" bson:"nonce" cql:"nonce" dynamo:"nonce"`
|
Nonce string `json:"nonce" bson:"nonce" cql:"nonce"`
|
||||||
RedirectURI string `json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri" dynamo:"redirect_uri"`
|
RedirectURI string `json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
||||||
|
@@ -12,14 +12,14 @@ import (
|
|||||||
|
|
||||||
// Webhook model for db
|
// Webhook model for db
|
||||||
type Webhook struct {
|
type Webhook struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"`
|
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
|
||||||
EndPoint string `json:"endpoint" bson:"endpoint" cql:"endpoint" dynamo:"endpoint"`
|
EndPoint string `json:"endpoint" bson:"endpoint" cql:"endpoint"`
|
||||||
Headers string `json:"headers" bson:"headers" cql:"headers" dynamo:"headers"`
|
Headers string `json:"headers" bson:"headers" cql:"headers"`
|
||||||
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled" dynamo:"enabled"`
|
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsAPIWebhook to return webhook as graphql response object
|
// AsAPIWebhook to return webhook as graphql response object
|
||||||
|
@@ -11,14 +11,14 @@ import (
|
|||||||
|
|
||||||
// WebhookLog model for db
|
// WebhookLog model for db
|
||||||
type WebhookLog struct {
|
type WebhookLog struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||||
HttpStatus int64 `json:"http_status" bson:"http_status" cql:"http_status" dynamo:"http_status"`
|
HttpStatus int64 `json:"http_status" bson:"http_status" cql:"http_status"`
|
||||||
Response string `json:"response" bson:"response" cql:"response" dynamo:"response"`
|
Response string `json:"response" bson:"response" cql:"response"`
|
||||||
Request string `json:"request" bson:"request" cql:"request" dynamo:"request"`
|
Request string `json:"request" bson:"request" cql:"request"`
|
||||||
WebhookID string `gorm:"type:char(36)" json:"webhook_id" bson:"webhook_id" cql:"webhook_id" dynamo:"webhook_id" index:"webhook_id,hash"`
|
WebhookID string `gorm:"type:char(36)" json:"webhook_id" bson:"webhook_id" cql:"webhook_id"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsAPIWebhookLog to return webhook log as graphql response object
|
// AsAPIWebhookLog to return webhook log as graphql response object
|
||||||
|
@@ -1,121 +0,0 @@
|
|||||||
package dynamodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/guregu/dynamo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddEmailTemplate to add EmailTemplate
|
|
||||||
func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
|
|
||||||
collection := p.db.Table(models.Collections.EmailTemplate)
|
|
||||||
if emailTemplate.ID == "" {
|
|
||||||
emailTemplate.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
emailTemplate.Key = emailTemplate.ID
|
|
||||||
emailTemplate.CreatedAt = time.Now().Unix()
|
|
||||||
emailTemplate.UpdatedAt = time.Now().Unix()
|
|
||||||
err := collection.Put(emailTemplate).RunWithContext(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return emailTemplate.AsAPIEmailTemplate(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
return emailTemplate.AsAPIEmailTemplate(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateEmailTemplate to update EmailTemplate
|
|
||||||
func (p *provider) UpdateEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
|
|
||||||
collection := p.db.Table(models.Collections.EmailTemplate)
|
|
||||||
emailTemplate.UpdatedAt = time.Now().Unix()
|
|
||||||
err := UpdateByHashKey(collection, "id", emailTemplate.ID, emailTemplate)
|
|
||||||
if err != nil {
|
|
||||||
return emailTemplate.AsAPIEmailTemplate(), err
|
|
||||||
}
|
|
||||||
return emailTemplate.AsAPIEmailTemplate(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEmailTemplates to list EmailTemplate
|
|
||||||
func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagination) (*model.EmailTemplates, error) {
|
|
||||||
|
|
||||||
var emailTemplate models.EmailTemplate
|
|
||||||
var iter dynamo.PagingIter
|
|
||||||
var lastEval dynamo.PagingKey
|
|
||||||
var iteration int64 = 0
|
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.EmailTemplate)
|
|
||||||
emailTemplates := []*model.EmailTemplate{}
|
|
||||||
paginationClone := pagination
|
|
||||||
scanner := collection.Scan()
|
|
||||||
count, err := scanner.Count()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for (paginationClone.Offset + paginationClone.Limit) > iteration {
|
|
||||||
iter = scanner.StartFrom(lastEval).Limit(paginationClone.Limit).Iter()
|
|
||||||
for iter.NextWithContext(ctx, &emailTemplate) {
|
|
||||||
if paginationClone.Offset == iteration {
|
|
||||||
emailTemplates = append(emailTemplates, emailTemplate.AsAPIEmailTemplate())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastEval = iter.LastEvaluatedKey()
|
|
||||||
iteration += paginationClone.Limit
|
|
||||||
}
|
|
||||||
|
|
||||||
paginationClone.Total = count
|
|
||||||
|
|
||||||
return &model.EmailTemplates{
|
|
||||||
Pagination: &paginationClone,
|
|
||||||
EmailTemplates: emailTemplates,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEmailTemplateByID to get EmailTemplate by id
|
|
||||||
func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) {
|
|
||||||
collection := p.db.Table(models.Collections.EmailTemplate)
|
|
||||||
var emailTemplate models.EmailTemplate
|
|
||||||
err := collection.Get("id", emailTemplateID).OneWithContext(ctx, &emailTemplate)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return emailTemplate.AsAPIEmailTemplate(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEmailTemplateByEventName to get EmailTemplate by event_name
|
|
||||||
func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) {
|
|
||||||
collection := p.db.Table(models.Collections.EmailTemplate)
|
|
||||||
var emailTemplates []models.EmailTemplate
|
|
||||||
var emailTemplate models.EmailTemplate
|
|
||||||
|
|
||||||
err := collection.Scan().Index("event_name").Filter("'event_name' = ?", eventName).Limit(1).AllWithContext(ctx, &emailTemplates)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(emailTemplates) > 0 {
|
|
||||||
emailTemplate = emailTemplates[0]
|
|
||||||
return emailTemplate.AsAPIEmailTemplate(), nil
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("no record found")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteEmailTemplate to delete EmailTemplate
|
|
||||||
func (p *provider) DeleteEmailTemplate(ctx context.Context, emailTemplate *model.EmailTemplate) error {
|
|
||||||
collection := p.db.Table(models.Collections.EmailTemplate)
|
|
||||||
err := collection.Delete("id", emailTemplate.ID).RunWithContext(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,72 +0,0 @@
|
|||||||
package dynamodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddEnv to save environment information in database
|
|
||||||
func (p *provider) AddEnv(ctx context.Context, env models.Env) (models.Env, error) {
|
|
||||||
collection := p.db.Table(models.Collections.Env)
|
|
||||||
|
|
||||||
if env.ID == "" {
|
|
||||||
env.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
env.Key = env.ID
|
|
||||||
|
|
||||||
env.CreatedAt = time.Now().Unix()
|
|
||||||
env.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
err := collection.Put(env).RunWithContext(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return env, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return env, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateEnv to update environment information in database
|
|
||||||
func (p *provider) UpdateEnv(ctx context.Context, env models.Env) (models.Env, error) {
|
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.Env)
|
|
||||||
env.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
err := UpdateByHashKey(collection, "id", env.ID, env)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return env, err
|
|
||||||
}
|
|
||||||
return env, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEnv to get environment information from database
|
|
||||||
func (p *provider) GetEnv(ctx context.Context) (models.Env, error) {
|
|
||||||
var env models.Env
|
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.Env)
|
|
||||||
// As there is no Findone supported.
|
|
||||||
iter := collection.Scan().Limit(1).Iter()
|
|
||||||
|
|
||||||
for iter.NextWithContext(ctx, &env) {
|
|
||||||
if env.ID == "" {
|
|
||||||
return env, errors.New("no documets found")
|
|
||||||
} else {
|
|
||||||
return env, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := iter.Err()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return env, fmt.Errorf("config not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return env, nil
|
|
||||||
}
|
|
@@ -1,80 +0,0 @@
|
|||||||
package dynamodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UpsertOTP to add or update otp
|
|
||||||
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
|
|
||||||
otp, _ := p.GetOTPByEmail(ctx, otpParam.Email)
|
|
||||||
shouldCreate := false
|
|
||||||
if otp == nil {
|
|
||||||
id := uuid.NewString()
|
|
||||||
otp = &models.OTP{
|
|
||||||
ID: id,
|
|
||||||
Key: id,
|
|
||||||
Otp: otpParam.Otp,
|
|
||||||
Email: otpParam.Email,
|
|
||||||
ExpiresAt: otpParam.ExpiresAt,
|
|
||||||
CreatedAt: time.Now().Unix(),
|
|
||||||
}
|
|
||||||
shouldCreate = true
|
|
||||||
} else {
|
|
||||||
otp.Otp = otpParam.Otp
|
|
||||||
otp.ExpiresAt = otpParam.ExpiresAt
|
|
||||||
}
|
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.OTP)
|
|
||||||
otp.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if shouldCreate {
|
|
||||||
err = collection.Put(otp).RunWithContext(ctx)
|
|
||||||
} else {
|
|
||||||
err = UpdateByHashKey(collection, "id", otp.ID, otp)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return otp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOTPByEmail to get otp for a given email address
|
|
||||||
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
|
|
||||||
var otps []models.OTP
|
|
||||||
var otp models.OTP
|
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.OTP)
|
|
||||||
|
|
||||||
err := collection.Scan().Index("email").Filter("'email' = ?", emailAddress).Limit(1).AllWithContext(ctx, &otps)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(otps) > 0 {
|
|
||||||
otp = otps[0]
|
|
||||||
return &otp, nil
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("no docuemnt found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteOTP to delete otp
|
|
||||||
func (p *provider) DeleteOTP(ctx context.Context, otp *models.OTP) error {
|
|
||||||
collection := p.db.Table(models.Collections.OTP)
|
|
||||||
|
|
||||||
if otp.ID != "" {
|
|
||||||
err := collection.Delete("id", otp.ID).RunWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,61 +0,0 @@
|
|||||||
package dynamodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
"github.com/guregu/dynamo"
|
|
||||||
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 {
|
|
||||||
db *dynamo.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewProvider returns a new Dynamo provider
|
|
||||||
func NewProvider() (*provider, error) {
|
|
||||||
dbURL := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseURL
|
|
||||||
awsRegion := memorystore.RequiredEnvStoreObj.GetRequiredEnv().AwsRegion
|
|
||||||
awsAccessKeyID := memorystore.RequiredEnvStoreObj.GetRequiredEnv().AwsAccessKeyID
|
|
||||||
awsSecretAccessKey := memorystore.RequiredEnvStoreObj.GetRequiredEnv().AwsSecretAccessKey
|
|
||||||
|
|
||||||
config := aws.Config{
|
|
||||||
MaxRetries: aws.Int(3),
|
|
||||||
CredentialsChainVerboseErrors: aws.Bool(true), // for full error logs
|
|
||||||
}
|
|
||||||
|
|
||||||
if awsRegion != "" {
|
|
||||||
config.Region = aws.String(awsRegion)
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom awsAccessKeyID, awsSecretAccessKey took first priority, if not then fetch config from aws credentials
|
|
||||||
if awsAccessKeyID != "" && awsSecretAccessKey != "" {
|
|
||||||
config.Credentials = credentials.NewStaticCredentials(awsAccessKeyID, awsSecretAccessKey, "")
|
|
||||||
} else if dbURL != "" {
|
|
||||||
// static config in case of testing or local-setup
|
|
||||||
config.Credentials = credentials.NewStaticCredentials("key", "key", "")
|
|
||||||
config.Endpoint = aws.String(dbURL)
|
|
||||||
} else {
|
|
||||||
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))
|
|
||||||
db := dynamo.New(session)
|
|
||||||
|
|
||||||
db.CreateTable(models.Collections.User, models.User{}).Wait()
|
|
||||||
db.CreateTable(models.Collections.Session, models.Session{}).Wait()
|
|
||||||
db.CreateTable(models.Collections.EmailTemplate, models.EmailTemplate{}).Wait()
|
|
||||||
db.CreateTable(models.Collections.Env, models.Env{}).Wait()
|
|
||||||
db.CreateTable(models.Collections.OTP, models.OTP{}).Wait()
|
|
||||||
db.CreateTable(models.Collections.VerificationRequest, models.VerificationRequest{}).Wait()
|
|
||||||
db.CreateTable(models.Collections.Webhook, models.Webhook{}).Wait()
|
|
||||||
db.CreateTable(models.Collections.WebhookLog, models.WebhookLog{}).Wait()
|
|
||||||
|
|
||||||
return &provider{
|
|
||||||
db: db,
|
|
||||||
}, nil
|
|
||||||
}
|
|
@@ -1,28 +0,0 @@
|
|||||||
package dynamodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddSession to save session information in database
|
|
||||||
func (p *provider) AddSession(ctx context.Context, session models.Session) error {
|
|
||||||
collection := p.db.Table(models.Collections.Session)
|
|
||||||
|
|
||||||
if session.ID == "" {
|
|
||||||
session.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
session.CreatedAt = time.Now().Unix()
|
|
||||||
session.UpdatedAt = time.Now().Unix()
|
|
||||||
err := collection.Put(session).RunWithContext(ctx)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteSession to delete session information from database
|
|
||||||
func (p *provider) DeleteSession(ctx context.Context, userId string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,46 +0,0 @@
|
|||||||
package dynamodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
|
|
||||||
"github.com/guregu/dynamo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// As updpate all item not supported so set manually via Set and SetNullable for empty field
|
|
||||||
func UpdateByHashKey(table dynamo.Table, hashKey string, hashValue string, item interface{}) error {
|
|
||||||
existingValue, err := dynamo.MarshalItem(item)
|
|
||||||
var i interface{}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nullableValue, err := dynamodbattribute.MarshalMap(item)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
u := table.Update(hashKey, hashValue)
|
|
||||||
for k, v := range existingValue {
|
|
||||||
if k == hashKey {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
u = u.Set(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range nullableValue {
|
|
||||||
if k == hashKey {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dynamodbattribute.Unmarshal(v, &i)
|
|
||||||
if i == nil {
|
|
||||||
u = u.SetNullable(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = u.Run()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,195 +0,0 @@
|
|||||||
package dynamodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/guregu/dynamo"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddUser to save user information in database
|
|
||||||
func (p *provider) AddUser(ctx context.Context, user models.User) (models.User, error) {
|
|
||||||
collection := p.db.Table(models.Collections.User)
|
|
||||||
|
|
||||||
if user.ID == "" {
|
|
||||||
user.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.Roles == "" {
|
|
||||||
defaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
|
||||||
if err != nil {
|
|
||||||
return user, err
|
|
||||||
}
|
|
||||||
user.Roles = defaultRoles
|
|
||||||
}
|
|
||||||
|
|
||||||
user.CreatedAt = time.Now().Unix()
|
|
||||||
user.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
err := collection.Put(user).RunWithContext(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return user, err
|
|
||||||
}
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateUser to update user information in database
|
|
||||||
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
|
|
||||||
collection := p.db.Table(models.Collections.User)
|
|
||||||
|
|
||||||
if user.ID != "" {
|
|
||||||
|
|
||||||
user.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
err := UpdateByHashKey(collection, "id", user.ID, user)
|
|
||||||
if err != nil {
|
|
||||||
return user, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return user, err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteUser to delete user information from database
|
|
||||||
func (p *provider) DeleteUser(ctx context.Context, user models.User) error {
|
|
||||||
collection := p.db.Table(models.Collections.User)
|
|
||||||
sessionCollection := p.db.Table(models.Collections.Session)
|
|
||||||
|
|
||||||
if user.ID != "" {
|
|
||||||
err := collection.Delete("id", user.ID).Run()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = sessionCollection.Batch("id").Write().Delete(dynamo.Keys{"user_id", user.ID}).RunWithContext(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListUsers to get list of users from database
|
|
||||||
func (p *provider) ListUsers(ctx context.Context, pagination model.Pagination) (*model.Users, error) {
|
|
||||||
var user models.User
|
|
||||||
var lastEval dynamo.PagingKey
|
|
||||||
var iter dynamo.PagingIter
|
|
||||||
var iteration int64 = 0
|
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.User)
|
|
||||||
users := []*model.User{}
|
|
||||||
|
|
||||||
paginationClone := pagination
|
|
||||||
scanner := collection.Scan()
|
|
||||||
count, err := scanner.Count()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for (paginationClone.Offset + paginationClone.Limit) > iteration {
|
|
||||||
iter = scanner.StartFrom(lastEval).Limit(paginationClone.Limit).Iter()
|
|
||||||
for iter.NextWithContext(ctx, &user) {
|
|
||||||
if paginationClone.Offset == iteration {
|
|
||||||
users = append(users, user.AsAPIUser())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastEval = iter.LastEvaluatedKey()
|
|
||||||
iteration += paginationClone.Limit
|
|
||||||
}
|
|
||||||
|
|
||||||
err = iter.Err()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
paginationClone.Total = count
|
|
||||||
|
|
||||||
return &model.Users{
|
|
||||||
Pagination: &paginationClone,
|
|
||||||
Users: users,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserByEmail to get user information from database using email address
|
|
||||||
func (p *provider) GetUserByEmail(ctx context.Context, email string) (models.User, error) {
|
|
||||||
var users []models.User
|
|
||||||
var user models.User
|
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.User)
|
|
||||||
err := collection.Scan().Index("email").Filter("'email' = ?", email).AllWithContext(ctx, &users)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(users) > 0 {
|
|
||||||
user = users[0]
|
|
||||||
return user, nil
|
|
||||||
} else {
|
|
||||||
return user, errors.New("no record found")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserByID to get user information from database using user ID
|
|
||||||
func (p *provider) GetUserByID(ctx context.Context, id string) (models.User, error) {
|
|
||||||
collection := p.db.Table(models.Collections.User)
|
|
||||||
var user models.User
|
|
||||||
err := collection.Get("id", id).OneWithContext(ctx, &user)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if user.Email == "" {
|
|
||||||
return user, errors.New("no documets found")
|
|
||||||
} else {
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateUsers to update multiple users, with parameters of user IDs slice
|
|
||||||
// If ids set to nil / empty all the users will be updated
|
|
||||||
func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error {
|
|
||||||
// set updated_at time for all users
|
|
||||||
userCollection := p.db.Table(models.Collections.User)
|
|
||||||
var allUsers []models.User
|
|
||||||
var res int64 = 0
|
|
||||||
var err error
|
|
||||||
if len(ids) > 0 {
|
|
||||||
for _, v := range ids {
|
|
||||||
err = UpdateByHashKey(userCollection, "id", v, data)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// as there is no facility to update all doc - https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SQLtoNoSQL.UpdateData.html
|
|
||||||
userCollection.Scan().All(&allUsers)
|
|
||||||
|
|
||||||
for _, user := range allUsers {
|
|
||||||
err = UpdateByHashKey(userCollection, "id", user.ID, data)
|
|
||||||
if err == nil {
|
|
||||||
res = res + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
log.Info("Updated users: ", res)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,116 +0,0 @@
|
|||||||
package dynamodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/guregu/dynamo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddVerification to save verification request in database
|
|
||||||
func (p *provider) AddVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) (models.VerificationRequest, error) {
|
|
||||||
collection := p.db.Table(models.Collections.VerificationRequest)
|
|
||||||
|
|
||||||
if verificationRequest.ID == "" {
|
|
||||||
verificationRequest.ID = uuid.New().String()
|
|
||||||
verificationRequest.CreatedAt = time.Now().Unix()
|
|
||||||
verificationRequest.UpdatedAt = time.Now().Unix()
|
|
||||||
err := collection.Put(verificationRequest).RunWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return verificationRequest, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return verificationRequest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVerificationRequestByToken to get verification request from database using token
|
|
||||||
func (p *provider) GetVerificationRequestByToken(ctx context.Context, token string) (models.VerificationRequest, error) {
|
|
||||||
collection := p.db.Table(models.Collections.VerificationRequest)
|
|
||||||
var verificationRequest models.VerificationRequest
|
|
||||||
|
|
||||||
iter := collection.Scan().Filter("'token' = ?", token).Iter()
|
|
||||||
for iter.NextWithContext(ctx, &verificationRequest) {
|
|
||||||
return verificationRequest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := iter.Err()
|
|
||||||
if err != nil {
|
|
||||||
return verificationRequest, err
|
|
||||||
}
|
|
||||||
return verificationRequest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVerificationRequestByEmail to get verification request by email from database
|
|
||||||
func (p *provider) GetVerificationRequestByEmail(ctx context.Context, email string, identifier string) (models.VerificationRequest, error) {
|
|
||||||
var verificationRequest models.VerificationRequest
|
|
||||||
collection := p.db.Table(models.Collections.VerificationRequest)
|
|
||||||
iter := collection.Scan().Filter("'email' = ?", email).Filter("'identifier' = ?", identifier).Iter()
|
|
||||||
for iter.NextWithContext(ctx, &verificationRequest) {
|
|
||||||
return verificationRequest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := iter.Err()
|
|
||||||
if err != nil {
|
|
||||||
return verificationRequest, err
|
|
||||||
}
|
|
||||||
return verificationRequest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListVerificationRequests to get list of verification requests from database
|
|
||||||
func (p *provider) ListVerificationRequests(ctx context.Context, pagination model.Pagination) (*model.VerificationRequests, error) {
|
|
||||||
verificationRequests := []*model.VerificationRequest{}
|
|
||||||
var verificationRequest models.VerificationRequest
|
|
||||||
var lastEval dynamo.PagingKey
|
|
||||||
var iter dynamo.PagingIter
|
|
||||||
var iteration int64 = 0
|
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.VerificationRequest)
|
|
||||||
paginationClone := pagination
|
|
||||||
|
|
||||||
scanner := collection.Scan()
|
|
||||||
count, err := scanner.Count()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for (paginationClone.Offset + paginationClone.Limit) > iteration {
|
|
||||||
iter = scanner.StartFrom(lastEval).Limit(paginationClone.Limit).Iter()
|
|
||||||
for iter.NextWithContext(ctx, &verificationRequest) {
|
|
||||||
if paginationClone.Offset == iteration {
|
|
||||||
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = iter.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
lastEval = iter.LastEvaluatedKey()
|
|
||||||
iteration += paginationClone.Limit
|
|
||||||
}
|
|
||||||
|
|
||||||
paginationClone.Total = count
|
|
||||||
|
|
||||||
return &model.VerificationRequests{
|
|
||||||
VerificationRequests: verificationRequests,
|
|
||||||
Pagination: &paginationClone,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteVerificationRequest to delete verification request from database
|
|
||||||
func (p *provider) DeleteVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) error {
|
|
||||||
collection := p.db.Table(models.Collections.VerificationRequest)
|
|
||||||
|
|
||||||
if verificationRequest.ID != "" {
|
|
||||||
err := collection.Delete("id", verificationRequest.ID).RunWithContext(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,148 +0,0 @@
|
|||||||
package dynamodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/guregu/dynamo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddWebhook to add webhook
|
|
||||||
func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
|
|
||||||
collection := p.db.Table(models.Collections.Webhook)
|
|
||||||
|
|
||||||
if webhook.ID == "" {
|
|
||||||
webhook.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
webhook.Key = webhook.ID
|
|
||||||
webhook.CreatedAt = time.Now().Unix()
|
|
||||||
webhook.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
err := collection.Put(webhook).RunWithContext(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return webhook.AsAPIWebhook(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateWebhook to update webhook
|
|
||||||
func (p *provider) UpdateWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
|
|
||||||
collection := p.db.Table(models.Collections.Webhook)
|
|
||||||
|
|
||||||
webhook.UpdatedAt = time.Now().Unix()
|
|
||||||
err := UpdateByHashKey(collection, "id", webhook.ID, webhook)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return webhook.AsAPIWebhook(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListWebhooks to list webhook
|
|
||||||
func (p *provider) ListWebhook(ctx context.Context, pagination model.Pagination) (*model.Webhooks, error) {
|
|
||||||
webhooks := []*model.Webhook{}
|
|
||||||
var webhook models.Webhook
|
|
||||||
var lastEval dynamo.PagingKey
|
|
||||||
var iter dynamo.PagingIter
|
|
||||||
var iteration int64 = 0
|
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.Webhook)
|
|
||||||
paginationClone := pagination
|
|
||||||
scanner := collection.Scan()
|
|
||||||
count, err := scanner.Count()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for (paginationClone.Offset + paginationClone.Limit) > iteration {
|
|
||||||
iter = scanner.StartFrom(lastEval).Limit(paginationClone.Limit).Iter()
|
|
||||||
for iter.NextWithContext(ctx, &webhook) {
|
|
||||||
if paginationClone.Offset == iteration {
|
|
||||||
webhooks = append(webhooks, webhook.AsAPIWebhook())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = iter.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
lastEval = iter.LastEvaluatedKey()
|
|
||||||
iteration += paginationClone.Limit
|
|
||||||
}
|
|
||||||
|
|
||||||
paginationClone.Total = count
|
|
||||||
|
|
||||||
return &model.Webhooks{
|
|
||||||
Pagination: &paginationClone,
|
|
||||||
Webhooks: webhooks,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWebhookByID to get webhook by id
|
|
||||||
func (p *provider) GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error) {
|
|
||||||
collection := p.db.Table(models.Collections.Webhook)
|
|
||||||
var webhook models.Webhook
|
|
||||||
|
|
||||||
err := collection.Get("id", webhookID).OneWithContext(ctx, &webhook)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if webhook.ID == "" {
|
|
||||||
return webhook.AsAPIWebhook(), errors.New("no documets found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return webhook.AsAPIWebhook(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWebhookByEventName to get webhook by event_name
|
|
||||||
func (p *provider) GetWebhookByEventName(ctx context.Context, eventName string) (*model.Webhook, error) {
|
|
||||||
var webhook models.Webhook
|
|
||||||
collection := p.db.Table(models.Collections.Webhook)
|
|
||||||
|
|
||||||
iter := collection.Scan().Index("event_name").Filter("'event_name' = ?", eventName).Iter()
|
|
||||||
|
|
||||||
for iter.NextWithContext(ctx, &webhook) {
|
|
||||||
return webhook.AsAPIWebhook(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := iter.Err()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return webhook.AsAPIWebhook(), err
|
|
||||||
}
|
|
||||||
return webhook.AsAPIWebhook(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteWebhook to delete webhook
|
|
||||||
func (p *provider) DeleteWebhook(ctx context.Context, webhook *model.Webhook) error {
|
|
||||||
// Also delete webhook logs for given webhook id
|
|
||||||
if webhook.ID != "" {
|
|
||||||
webhookCollection := p.db.Table(models.Collections.Webhook)
|
|
||||||
pagination := model.Pagination{}
|
|
||||||
webhookLogCollection := p.db.Table(models.Collections.WebhookLog)
|
|
||||||
err := webhookCollection.Delete("id", webhook.ID).RunWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
webhookLogs, errIs := p.ListWebhookLogs(ctx, pagination, webhook.ID)
|
|
||||||
|
|
||||||
for _, webhookLog := range webhookLogs.WebhookLogs {
|
|
||||||
err = webhookLogCollection.Delete("id", webhookLog.ID).RunWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if errIs != nil {
|
|
||||||
return errIs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,78 +0,0 @@
|
|||||||
package dynamodb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/guregu/dynamo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddWebhookLog to add webhook log
|
|
||||||
func (p *provider) AddWebhookLog(ctx context.Context, webhookLog models.WebhookLog) (*model.WebhookLog, error) {
|
|
||||||
collection := p.db.Table(models.Collections.WebhookLog)
|
|
||||||
|
|
||||||
if webhookLog.ID == "" {
|
|
||||||
webhookLog.ID = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
webhookLog.Key = webhookLog.ID
|
|
||||||
webhookLog.CreatedAt = time.Now().Unix()
|
|
||||||
webhookLog.UpdatedAt = time.Now().Unix()
|
|
||||||
err := collection.Put(webhookLog).RunWithContext(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return webhookLog.AsAPIWebhookLog(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListWebhookLogs to list webhook logs
|
|
||||||
func (p *provider) ListWebhookLogs(ctx context.Context, pagination model.Pagination, webhookID string) (*model.WebhookLogs, error) {
|
|
||||||
webhookLogs := []*model.WebhookLog{}
|
|
||||||
var webhookLog models.WebhookLog
|
|
||||||
var lastEval dynamo.PagingKey
|
|
||||||
var iter dynamo.PagingIter
|
|
||||||
var iteration int64 = 0
|
|
||||||
var err error
|
|
||||||
var count int64
|
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.WebhookLog)
|
|
||||||
paginationClone := pagination
|
|
||||||
scanner := collection.Scan()
|
|
||||||
|
|
||||||
if webhookID != "" {
|
|
||||||
iter = scanner.Index("webhook_id").Filter("'webhook_id' = ?", webhookID).Iter()
|
|
||||||
for iter.NextWithContext(ctx, &webhookLog) {
|
|
||||||
webhookLogs = append(webhookLogs, webhookLog.AsAPIWebhookLog())
|
|
||||||
}
|
|
||||||
err = iter.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (paginationClone.Offset + paginationClone.Limit) > iteration {
|
|
||||||
iter = scanner.StartFrom(lastEval).Limit(paginationClone.Limit).Iter()
|
|
||||||
for iter.NextWithContext(ctx, &webhookLog) {
|
|
||||||
if paginationClone.Offset == iteration {
|
|
||||||
webhookLogs = append(webhookLogs, webhookLog.AsAPIWebhookLog())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = iter.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
lastEval = iter.LastEvaluatedKey()
|
|
||||||
iteration += paginationClone.Limit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paginationClone.Total = count
|
|
||||||
// paginationClone.Cursor = iter.LastEvaluatedKey()
|
|
||||||
return &model.WebhookLogs{
|
|
||||||
Pagination: &paginationClone,
|
|
||||||
WebhookLogs: webhookLogs,
|
|
||||||
}, nil
|
|
||||||
}
|
|
@@ -1,7 +1,6 @@
|
|||||||
package sql
|
package sql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@@ -22,16 +21,6 @@ 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
|
||||||
@@ -76,32 +65,6 @@ 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
|
||||||
|
@@ -5,7 +5,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -127,12 +126,6 @@ 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)
|
||||||
@@ -148,11 +141,6 @@ 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
|
||||||
|
32
server/env/env.go
vendored
32
server/env/env.go
vendored
@@ -55,7 +55,6 @@ 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,9 +77,6 @@ func InitAllEnv() error {
|
|||||||
osResetPasswordURL := os.Getenv(constants.EnvKeyResetPasswordURL)
|
osResetPasswordURL := os.Getenv(constants.EnvKeyResetPasswordURL)
|
||||||
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)
|
|
||||||
osAwsAccessKey := os.Getenv(constants.EnvAwsAccessKeyID)
|
|
||||||
osAwsSecretKey := os.Getenv(constants.EnvAwsSecretAccessKey)
|
|
||||||
|
|
||||||
// os bool vars
|
// os bool vars
|
||||||
osAppCookieSecure := os.Getenv(constants.EnvKeyAppCookieSecure)
|
osAppCookieSecure := os.Getenv(constants.EnvKeyAppCookieSecure)
|
||||||
@@ -123,27 +119,6 @@ func InitAllEnv() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := envData[constants.EnvAwsRegion]; !ok || val == "" {
|
|
||||||
envData[constants.EnvAwsRegion] = osAwsRegion
|
|
||||||
}
|
|
||||||
if osAwsRegion != "" && envData[constants.EnvAwsRegion] != osAwsRegion {
|
|
||||||
envData[constants.EnvAwsRegion] = osAwsRegion
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, ok := envData[constants.EnvAwsAccessKeyID]; !ok || val == "" {
|
|
||||||
envData[constants.EnvAwsAccessKeyID] = osAwsAccessKey
|
|
||||||
}
|
|
||||||
if osAwsAccessKey != "" && envData[constants.EnvAwsAccessKeyID] != osAwsRegion {
|
|
||||||
envData[constants.EnvAwsAccessKeyID] = osAwsAccessKey
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, ok := envData[constants.EnvAwsSecretAccessKey]; !ok || val == "" {
|
|
||||||
envData[constants.EnvAwsSecretAccessKey] = osAwsSecretKey
|
|
||||||
}
|
|
||||||
if osAwsSecretKey != "" && envData[constants.EnvAwsSecretAccessKey] != osAwsRegion {
|
|
||||||
envData[constants.EnvAwsSecretAccessKey] = osAwsSecretKey
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, ok := envData[constants.EnvKeyAppURL]; !ok || val == "" {
|
if val, ok := envData[constants.EnvKeyAppURL]; !ok || val == "" {
|
||||||
envData[constants.EnvKeyAppURL] = osAppURL
|
envData[constants.EnvKeyAppURL] = osAppURL
|
||||||
}
|
}
|
||||||
@@ -206,13 +181,6 @@ 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
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,6 @@ go 1.16
|
|||||||
require (
|
require (
|
||||||
github.com/99designs/gqlgen v0.17.20
|
github.com/99designs/gqlgen v0.17.20
|
||||||
github.com/arangodb/go-driver v1.2.1
|
github.com/arangodb/go-driver v1.2.1
|
||||||
github.com/aws/aws-sdk-go v1.44.109
|
|
||||||
github.com/coreos/go-oidc/v3 v3.1.0
|
github.com/coreos/go-oidc/v3 v3.1.0
|
||||||
github.com/gin-gonic/gin v1.8.1
|
github.com/gin-gonic/gin v1.8.1
|
||||||
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
||||||
@@ -15,7 +14,6 @@ require (
|
|||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/guregu/dynamo v1.16.0
|
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.3.0
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||||
|
@@ -49,15 +49,10 @@ github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e h1:Xg+hGrY2
|
|||||||
github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:mq7Shfa/CaixoDxiyAAc5jZ6CVBAyPaNQCGS7mkj4Ho=
|
github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:mq7Shfa/CaixoDxiyAAc5jZ6CVBAyPaNQCGS7mkj4Ho=
|
||||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||||
github.com/aws/aws-sdk-go v1.42.47/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc=
|
|
||||||
github.com/aws/aws-sdk-go v1.44.109 h1:+Na5JPeS0kiEHoBp5Umcuuf+IDqXqD0lXnM920E31YI=
|
|
||||||
github.com/aws/aws-sdk-go v1.44.109/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
|
||||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
|
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
|
||||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||||
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
@@ -123,9 +118,8 @@ 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/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||||
@@ -191,8 +185,6 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
|
|||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/guregu/dynamo v1.16.0 h1:gmI8oi1VHwYQtq7+RPBeOiSssVLgxH/Az2t+NtDtL2c=
|
|
||||||
github.com/guregu/dynamo v1.16.0/go.mod h1:W2Gqcf3MtkrS+Q6fHPGAmRtT0Dyq+TGrqfqrUC9+R/c=
|
|
||||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
@@ -255,10 +247,6 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
|
|||||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jinzhu/now v1.1.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI=
|
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/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/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=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
@@ -492,8 +480,6 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
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-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=
|
||||||
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b h1:uKO3Js8lXGjpjdc4J3rqs0/Ex5yDKUGfk43tTYWVLas=
|
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b h1:uKO3Js8lXGjpjdc4J3rqs0/Ex5yDKUGfk43tTYWVLas=
|
||||||
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
@@ -560,7 +546,6 @@ 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-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -570,7 +555,6 @@ golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,10 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type AddEmailTemplateRequest struct {
|
type AddEmailTemplateRequest struct {
|
||||||
EventName string `json:"event_name"`
|
EventName string `json:"event_name"`
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
Template string `json:"template"`
|
Template string `json:"template"`
|
||||||
Design *string `json:"design"`
|
Design string `json:"design"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddWebhookRequest struct {
|
type AddWebhookRequest struct {
|
||||||
@@ -74,7 +74,6 @@ 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"`
|
||||||
@@ -272,7 +271,6 @@ 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"`
|
||||||
@@ -386,8 +384,7 @@ 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 {
|
||||||
|
@@ -110,7 +110,6 @@ 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
|
||||||
@@ -153,7 +152,6 @@ type Env {
|
|||||||
|
|
||||||
type ValidateJWTTokenResponse {
|
type ValidateJWTTokenResponse {
|
||||||
is_valid: Boolean!
|
is_valid: Boolean!
|
||||||
claims: Map
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GenerateJWTKeysResponse {
|
type GenerateJWTKeysResponse {
|
||||||
@@ -221,7 +219,6 @@ 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
|
||||||
@@ -430,7 +427,7 @@ input AddEmailTemplateRequest {
|
|||||||
event_name: String!
|
event_name: String!
|
||||||
subject: String!
|
subject: String!
|
||||||
template: String!
|
template: String!
|
||||||
design: String
|
design: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateEmailTemplateRequest {
|
input UpdateEmailTemplateRequest {
|
||||||
@@ -501,4 +498,4 @@ type Query {
|
|||||||
_webhooks(params: PaginatedInput): Webhooks!
|
_webhooks(params: PaginatedInput): Webhooks!
|
||||||
_webhook_logs(params: ListWebhookLogRequest): WebhookLogs!
|
_webhook_logs(params: ListWebhookLogRequest): WebhookLogs!
|
||||||
_email_templates(params: PaginatedInput): EmailTemplates!
|
_email_templates(params: PaginatedInput): EmailTemplates!
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ func AppHandler() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectURI := strings.TrimSpace(c.Query("redirect_uri"))
|
redirect_uri := 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 redirectURI == "" {
|
if redirect_uri == "" {
|
||||||
redirectURI = hostname + "/app"
|
redirect_uri = hostname + "/app"
|
||||||
} else {
|
} else {
|
||||||
// validate redirect url with allowed origins
|
// validate redirect url with allowed origins
|
||||||
if !validators.IsValidOrigin(redirectURI) {
|
if !validators.IsValidOrigin(redirect_uri) {
|
||||||
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": redirectURI,
|
"redirectURL": redirect_uri,
|
||||||
"scope": scope,
|
"scope": scope,
|
||||||
"state": state,
|
"state": state,
|
||||||
"organizationName": orgName,
|
"organizationName": orgName,
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -26,6 +27,12 @@ import (
|
|||||||
// code_challenge_method = to prevent CSRF attack [only sh256 is supported]
|
// code_challenge_method = to prevent CSRF attack [only sh256 is supported]
|
||||||
|
|
||||||
// check the flow for generating and verifying codes: https://developer.okta.com/blog/2019/08/22/okta-authjs-pkce#:~:text=PKCE%20works%20by%20having%20the,is%20called%20the%20Code%20Challenge.
|
// check the flow for generating and verifying codes: https://developer.okta.com/blog/2019/08/22/okta-authjs-pkce#:~:text=PKCE%20works%20by%20having%20the,is%20called%20the%20Code%20Challenge.
|
||||||
|
|
||||||
|
const (
|
||||||
|
authorizeWebMessageTemplate = "authorize_web_message.tmpl"
|
||||||
|
authorizeFormPostTemplate = "authorize_form_post.tmpl"
|
||||||
|
)
|
||||||
|
|
||||||
func AuthorizeHandler() gin.HandlerFunc {
|
func AuthorizeHandler() gin.HandlerFunc {
|
||||||
return func(gc *gin.Context) {
|
return func(gc *gin.Context) {
|
||||||
redirectURI := strings.TrimSpace(gc.Query("redirect_uri"))
|
redirectURI := strings.TrimSpace(gc.Query("redirect_uri"))
|
||||||
@@ -34,7 +41,6 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
codeChallenge := strings.TrimSpace(gc.Query("code_challenge"))
|
codeChallenge := strings.TrimSpace(gc.Query("code_challenge"))
|
||||||
scopeString := strings.TrimSpace(gc.Query("scope"))
|
scopeString := strings.TrimSpace(gc.Query("scope"))
|
||||||
clientID := strings.TrimSpace(gc.Query("client_id"))
|
clientID := strings.TrimSpace(gc.Query("client_id"))
|
||||||
template := "authorize.tmpl"
|
|
||||||
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
||||||
|
|
||||||
var scope []string
|
var scope []string
|
||||||
@@ -45,176 +51,94 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if responseMode == "" {
|
if responseMode == "" {
|
||||||
responseMode = "query"
|
responseMode = constants.ResponseModeQuery
|
||||||
}
|
|
||||||
|
|
||||||
if responseMode != "query" && responseMode != "web_message" {
|
|
||||||
log.Debug("Invalid response_mode: ", responseMode)
|
|
||||||
gc.JSON(400, gin.H{"error": "invalid response mode"})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if redirectURI == "" {
|
if redirectURI == "" {
|
||||||
redirectURI = "/app"
|
redirectURI = "/app"
|
||||||
}
|
}
|
||||||
|
|
||||||
isQuery := responseMode == "query"
|
|
||||||
|
|
||||||
loginURL := "/app?state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI
|
|
||||||
|
|
||||||
if clientID == "" {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
log.Debug("Failed to get client_id: ", clientID)
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "client_id is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if client, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID); client != clientID || err != nil {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
log.Debug("Invalid client_id: ", clientID)
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "invalid_client_id",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if state == "" {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
log.Debug("Failed to get state: ", state)
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "state is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if responseType == "" {
|
if responseType == "" {
|
||||||
responseType = "token"
|
responseType = "token"
|
||||||
}
|
}
|
||||||
|
|
||||||
isResponseTypeCode := responseType == "code"
|
if err := validateAuthorizeRequest(responseType, responseMode, clientID, state, codeChallenge); err != nil {
|
||||||
isResponseTypeToken := responseType == "token"
|
log.Debug("invalid authorization request: ", err)
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
if !isResponseTypeCode && !isResponseTypeToken {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
log.Debug("Invalid response_type: ", responseType)
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "response_type is invalid",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isResponseTypeCode {
|
log := log.WithFields(log.Fields{
|
||||||
if codeChallenge == "" {
|
"response_mode": responseMode,
|
||||||
if isQuery {
|
"response_type": responseType,
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
"state": state,
|
||||||
} else {
|
"code_challenge": codeChallenge,
|
||||||
log.Debug("Failed to get code_challenge: ", codeChallenge)
|
"scope": scope,
|
||||||
gc.HTML(http.StatusBadRequest, template, gin.H{
|
"redirect_uri": redirectURI,
|
||||||
"target_origin": redirectURI,
|
})
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
// used for response mode query or fragment
|
||||||
"response": map[string]string{
|
loginState := "state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI
|
||||||
"error": "code_challenge is required",
|
loginURL := "/app?" + loginState
|
||||||
},
|
|
||||||
},
|
if responseMode == constants.ResponseModeFragment {
|
||||||
})
|
loginURL = "/app#" + loginState
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if state == "" {
|
||||||
|
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]interface{}{
|
||||||
|
"error": "state_required",
|
||||||
|
"error_description": "state is required",
|
||||||
|
},
|
||||||
|
}, http.StatusOK)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseType == constants.ResponseTypeCode && codeChallenge == "" {
|
||||||
|
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]interface{}{
|
||||||
|
"error": "code_challenge_required",
|
||||||
|
"error_description": "code challenge is required",
|
||||||
|
},
|
||||||
|
}, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
loginError := map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]interface{}{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
|
}
|
||||||
sessionToken, err := cookie.GetSession(gc)
|
sessionToken, err := cookie.GetSession(gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isQuery {
|
log.Debug("GetSession failed: ", err)
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "login_required",
|
|
||||||
"error_description": "Login is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get session from cookie
|
// get session from cookie
|
||||||
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isQuery {
|
log.Debug("ValidateBrowserSession failed: ", err)
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "login_required",
|
|
||||||
"error_description": "Login is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userID := claims.Subject
|
userID := claims.Subject
|
||||||
user, err := db.Provider.GetUserByID(gc, userID)
|
user, err := db.Provider.GetUserByID(gc, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isQuery {
|
log.Debug("GetUserByID failed: ", err)
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||||
} else {
|
"type": "authorization_response",
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
"response": map[string]interface{}{
|
||||||
"target_origin": redirectURI,
|
"error": "signup_required",
|
||||||
"authorization_response": map[string]interface{}{
|
"error_description": "Sign up required",
|
||||||
"type": "authorization_response",
|
},
|
||||||
"response": map[string]string{
|
}, http.StatusOK)
|
||||||
"error": "signup_required",
|
|
||||||
"error_description": "Sign up required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,72 +147,92 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
sessionKey = claims.LoginMethod + ":" + user.ID
|
sessionKey = claims.LoginMethod + ":" + user.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// if user is logged in
|
nonce := uuid.New().String()
|
||||||
// based on the response type code, generate the response
|
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
|
||||||
if isResponseTypeCode {
|
if err != nil {
|
||||||
// rollover the session for security
|
log.Debug("CreateSessionToken failed: ", err)
|
||||||
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
|
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||||
nonce := uuid.New().String()
|
|
||||||
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
|
|
||||||
if err != nil {
|
|
||||||
if isQuery {
|
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "login_required",
|
|
||||||
"error_description": "Login is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken)
|
|
||||||
cookie.SetSession(gc, newSessionToken)
|
|
||||||
code := uuid.New().String()
|
|
||||||
memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken)
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"code": code,
|
|
||||||
"state": state,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isResponseTypeToken {
|
code := uuid.New().String()
|
||||||
// rollover the session for security
|
if err := memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken); err != nil {
|
||||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod)
|
log.Debug("SetState failed: ", err)
|
||||||
if err != nil {
|
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||||
if isQuery {
|
return
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
}
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
// rollover the session for security
|
||||||
"target_origin": redirectURI,
|
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
|
||||||
"authorization_response": map[string]interface{}{
|
if responseType == constants.ResponseTypeCode {
|
||||||
"type": "authorization_response",
|
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken); err != nil {
|
||||||
"response": map[string]string{
|
log.Debug("SetUserSession failed: ", err)
|
||||||
"error": "login_required",
|
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||||
"error_description": "Login is required",
|
return
|
||||||
},
|
}
|
||||||
},
|
|
||||||
})
|
cookie.SetSession(gc, newSessionToken)
|
||||||
}
|
|
||||||
|
// in case, response type is code and user is already logged in send the code and state
|
||||||
|
// and cookie session will already be rolled over and set
|
||||||
|
// gc.HTML(http.StatusOK, authorizeWebMessageTemplate, gin.H{
|
||||||
|
// "target_origin": redirectURI,
|
||||||
|
// "authorization_response": map[string]interface{}{
|
||||||
|
// "type": "authorization_response",
|
||||||
|
// "response": map[string]string{
|
||||||
|
// "code": code,
|
||||||
|
// "state": state,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
|
||||||
|
params := "code=" + code + "&state=" + state
|
||||||
|
if responseMode == constants.ResponseModeQuery {
|
||||||
|
if strings.Contains(redirectURI, "?") {
|
||||||
|
redirectURI = redirectURI + "&" + params
|
||||||
|
} else {
|
||||||
|
redirectURI = redirectURI + "?" + params
|
||||||
|
}
|
||||||
|
} else if responseMode == constants.ResponseModeFragment {
|
||||||
|
if strings.Contains(redirectURI, "#") {
|
||||||
|
redirectURI = redirectURI + "&" + params
|
||||||
|
} else {
|
||||||
|
redirectURI = redirectURI + "#" + params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]interface{}{
|
||||||
|
"code": code,
|
||||||
|
"state": state,
|
||||||
|
},
|
||||||
|
}, http.StatusOK)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseType == constants.ResponseTypeToken || responseType == constants.ResponseTypeIDToken {
|
||||||
|
// rollover the session for security
|
||||||
|
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("CreateAuthToken failed: ", err)
|
||||||
|
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash); err != nil {
|
||||||
|
log.Debug("SetUserSession failed: ", err)
|
||||||
|
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token); err != nil {
|
||||||
|
log.Debug("SetUserSession failed: ", err)
|
||||||
|
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
|
|
||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
|
||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
|
||||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
@@ -306,6 +250,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
"scope": scope,
|
"scope": scope,
|
||||||
"token_type": "Bearer",
|
"token_type": "Bearer",
|
||||||
"expires_in": expiresIn,
|
"expires_in": expiresIn,
|
||||||
|
"code": code,
|
||||||
}
|
}
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
@@ -314,38 +259,72 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isQuery {
|
if responseMode == constants.ResponseModeQuery {
|
||||||
if strings.Contains(redirectURI, "?") {
|
if strings.Contains(redirectURI, "?") {
|
||||||
gc.Redirect(http.StatusFound, redirectURI+"&"+params)
|
redirectURI = redirectURI + "&" + params
|
||||||
} else {
|
} else {
|
||||||
gc.Redirect(http.StatusFound, redirectURI+"?"+params)
|
redirectURI = redirectURI + "?" + params
|
||||||
|
}
|
||||||
|
} else if responseMode == constants.ResponseModeFragment {
|
||||||
|
if strings.Contains(redirectURI, "#") {
|
||||||
|
redirectURI = redirectURI + "&" + params
|
||||||
|
} else {
|
||||||
|
redirectURI = redirectURI + "#" + params
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": res,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": res,
|
||||||
|
}, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isQuery {
|
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||||
gc.Redirect(http.StatusFound, loginURL)
|
}
|
||||||
} else {
|
}
|
||||||
// by default return with error
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
func validateAuthorizeRequest(responseType, responseMode, clientID, state, codeChallenge string) error {
|
||||||
"target_origin": redirectURI,
|
if responseType != constants.ResponseTypeCode && responseType != constants.ResponseTypeToken && responseType != constants.ResponseTypeIDToken {
|
||||||
"authorization_response": map[string]interface{}{
|
return fmt.Errorf("invalid response type %s. 'code' & 'token' are valid response_type", responseMode)
|
||||||
"type": "authorization_response",
|
}
|
||||||
"response": map[string]string{
|
|
||||||
"error": "login_required",
|
if responseMode != constants.ResponseModeQuery && responseMode != constants.ResponseModeWebMessage && responseMode != constants.ResponseModeFragment && responseMode != constants.ResponseModeFormPost {
|
||||||
"error_description": "Login is required",
|
return fmt.Errorf("invalid response mode %s. 'query', 'fragment', 'form_post' and 'web_message' are valid response_mode", responseMode)
|
||||||
},
|
}
|
||||||
},
|
|
||||||
})
|
if client, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID); client != clientID || err != nil {
|
||||||
}
|
return fmt.Errorf("invalid client_id %s", clientID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleResponse(gc *gin.Context, responseMode, loginURI, redirectURI string, data map[string]interface{}, httpStatusCode int) {
|
||||||
|
isAuthenticationRequired := false
|
||||||
|
if _, ok := data["response"].(map[string]interface{})["error"]; ok {
|
||||||
|
isAuthenticationRequired = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch responseMode {
|
||||||
|
case constants.ResponseModeQuery, constants.ResponseModeFragment:
|
||||||
|
if isAuthenticationRequired {
|
||||||
|
gc.Redirect(http.StatusFound, loginURI)
|
||||||
|
} else {
|
||||||
|
gc.Redirect(http.StatusFound, redirectURI)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case constants.ResponseModeWebMessage:
|
||||||
|
gc.HTML(httpStatusCode, authorizeWebMessageTemplate, gin.H{
|
||||||
|
"target_origin": redirectURI,
|
||||||
|
"authorization_response": data,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
case constants.ResponseModeFormPost:
|
||||||
|
gc.HTML(httpStatusCode, authorizeFormPostTemplate, gin.H{
|
||||||
|
"target_origin": redirectURI,
|
||||||
|
"authorization_response": data["response"],
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 + "/oauth/token",
|
"token_endpoint": issuer + "/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"},
|
"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", "web_message"},
|
"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", "role", "gender", "birthdate", "phone_number", "phone_number_verified", "nonce", "updated_at", "created_at", "revoked_timestamp", "login_method", "signup_methods", "token_type"},
|
"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"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -154,7 +154,6 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
"error": "invalid_refresh_token",
|
"error": "invalid_refresh_token",
|
||||||
"error_description": "The refresh token is invalid",
|
"error_description": "The refresh token is invalid",
|
||||||
})
|
})
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
claims, err := token.ValidateRefreshToken(gc, refreshToken)
|
claims, err := token.ValidateRefreshToken(gc, refreshToken)
|
||||||
@@ -164,10 +163,9 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
"error": "unauthorized",
|
"error": "unauthorized",
|
||||||
"error_description": err.Error(),
|
"error_description": err.Error(),
|
||||||
})
|
})
|
||||||
return
|
|
||||||
}
|
}
|
||||||
userID = claims["sub"].(string)
|
userID = claims["sub"].(string)
|
||||||
claimLoginMethod := claims["login_method"]
|
loginMethod := claims["login_method"]
|
||||||
rolesInterface := claims["roles"].([]interface{})
|
rolesInterface := claims["roles"].([]interface{})
|
||||||
scopeInterface := claims["scope"].([]interface{})
|
scopeInterface := claims["scope"].([]interface{})
|
||||||
for _, v := range rolesInterface {
|
for _, v := range rolesInterface {
|
||||||
@@ -178,11 +176,9 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sessionKey = userID
|
sessionKey = userID
|
||||||
if claimLoginMethod != nil && claimLoginMethod != "" {
|
if loginMethod != nil && loginMethod != "" {
|
||||||
sessionKey = claimLoginMethod.(string) + ":" + sessionKey
|
sessionKey = loginMethod.(string) + ":" + sessionKey
|
||||||
loginMethod = claimLoginMethod.(string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove older refresh token and rotate it for security
|
// remove older refresh token and rotate it for security
|
||||||
go memorystore.Provider.DeleteUserSession(sessionKey, claims["nonce"].(string))
|
go memorystore.Provider.DeleteUserSession(sessionKey, claims["nonce"].(string))
|
||||||
}
|
}
|
||||||
@@ -215,7 +211,6 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
@@ -15,15 +15,12 @@ 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)
|
||||||
|
@@ -28,10 +28,6 @@ type RequiredEnv struct {
|
|||||||
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.
|
||||||
@@ -57,8 +53,7 @@ func (r *RequiredEnvStore) SetRequiredEnv(requiredEnv RequiredEnv) {
|
|||||||
|
|
||||||
var RequiredEnvStoreObj *RequiredEnvStore
|
var RequiredEnvStoreObj *RequiredEnvStore
|
||||||
|
|
||||||
// InitRequiredEnv to initialize EnvData and throw error if required env are not present
|
// InitRequiredEnv to initialize EnvData and through 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)
|
||||||
|
|
||||||
@@ -90,9 +85,6 @@ 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 != "" {
|
||||||
@@ -121,8 +113,7 @@ func InitRequiredEnv() error {
|
|||||||
dbURL = strings.TrimSpace(*cli.ARG_DB_URL)
|
dbURL = strings.TrimSpace(*cli.ARG_DB_URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// In dynamoDB these field are not always mandatory
|
if dbURL == "" && dbPort == "" && dbHost == "" && dbUsername == "" && dbPassword == "" {
|
||||||
if dbType != constants.DbTypeDynamoDB && dbURL == "" && dbPort == "" && dbHost == "" && dbUsername == "" && dbPassword == "" {
|
|
||||||
log.Debug("DATABASE_URL is not set")
|
log.Debug("DATABASE_URL is not set")
|
||||||
return errors.New("invalid database url. DATABASE_URL is required")
|
return errors.New("invalid database url. DATABASE_URL is required")
|
||||||
}
|
}
|
||||||
@@ -148,9 +139,6 @@ func InitRequiredEnv() error {
|
|||||||
DatabaseCACert: dbCACert,
|
DatabaseCACert: dbCACert,
|
||||||
RedisURL: redisURL,
|
RedisURL: redisURL,
|
||||||
DisableRedisForEnv: disableRedisForEnv,
|
DisableRedisForEnv: disableRedisForEnv,
|
||||||
AwsRegion: awsRegion,
|
|
||||||
AwsAccessKeyID: awsAccessKeyID,
|
|
||||||
AwsSecretAccessKey: awsSecretAccessKey,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RequiredEnvStoreObj = &RequiredEnvStore{
|
RequiredEnvStoreObj = &RequiredEnvStore{
|
||||||
|
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/refs"
|
|
||||||
"github.com/authorizerdev/authorizer/server/token"
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/authorizerdev/authorizer/server/validators"
|
"github.com/authorizerdev/authorizer/server/validators"
|
||||||
@@ -41,17 +40,15 @@ func AddEmailTemplateResolver(ctx context.Context, params model.AddEmailTemplate
|
|||||||
return nil, fmt.Errorf("empty template not allowed")
|
return nil, fmt.Errorf("empty template not allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
var design string
|
if strings.TrimSpace(params.Design) == "" {
|
||||||
|
return nil, fmt.Errorf("empty design not allowed")
|
||||||
if params.Design == nil || strings.TrimSpace(refs.StringValue(params.Design)) == "" {
|
|
||||||
design = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Provider.AddEmailTemplate(ctx, models.EmailTemplate{
|
_, err = db.Provider.AddEmailTemplate(ctx, models.EmailTemplate{
|
||||||
EventName: params.EventName,
|
EventName: params.EventName,
|
||||||
Template: params.Template,
|
Template: params.Template,
|
||||||
Subject: params.Subject,
|
Subject: params.Subject,
|
||||||
Design: design,
|
Design: params.Design,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to add email template: ", err)
|
log.Debug("Failed to add email template: ", err)
|
||||||
|
@@ -89,9 +89,6 @@ 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))
|
||||||
}
|
}
|
||||||
|
@@ -62,21 +62,12 @@ 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)) != "" {
|
||||||
redirectURI = refs.StringValue(params.RedirectURI)
|
redirectURL = 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, redirectURI)
|
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL)
|
||||||
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
|
||||||
@@ -87,18 +78,18 @@ 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: redirectURI,
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to add verification request", err)
|
log.Debug("Failed to add verification request", err)
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// exec it as go routine so that we can reduce the api latency
|
// execute it as go routine so that we can reduce the api latency
|
||||||
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, redirectURI),
|
"verification_url": utils.GetForgotPasswordURL(verificationToken, hostname),
|
||||||
})
|
})
|
||||||
|
|
||||||
res = &model.Response{
|
res = &model.Response{
|
||||||
|
@@ -93,6 +93,5 @@ func ValidateJwtTokenResolver(ctx context.Context, params model.ValidateJWTToken
|
|||||||
}
|
}
|
||||||
return &model.ValidateJWTTokenResponse{
|
return &model.ValidateJWTTokenResponse{
|
||||||
IsValid: true,
|
IsValid: true,
|
||||||
Claims: claims,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@@ -51,30 +51,16 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||||||
assert.Nil(t, emailTemplate)
|
assert.Nil(t, emailTemplate)
|
||||||
})
|
})
|
||||||
|
|
||||||
var design string
|
t.Run("should not add email template with empty design", func(t *testing.T) {
|
||||||
design = ""
|
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
||||||
|
EventName: s.TestInfo.TestEmailTemplateEventTypes[0],
|
||||||
for _, eventType := range s.TestInfo.TestEmailTemplateEventTypes {
|
Template: "test",
|
||||||
t.Run("should add email template with empty design for "+eventType, func(t *testing.T) {
|
Subject: "test",
|
||||||
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
Design: " ",
|
||||||
EventName: eventType,
|
|
||||||
Template: "Test email",
|
|
||||||
Subject: "Test email",
|
|
||||||
Design: &design,
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, emailTemplate)
|
|
||||||
assert.NotEmpty(t, emailTemplate.Message)
|
|
||||||
|
|
||||||
et, err := db.Provider.GetEmailTemplateByEventName(ctx, eventType)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, et.EventName, eventType)
|
|
||||||
assert.Equal(t, "Test email", et.Subject)
|
|
||||||
assert.Equal(t, "Test design", et.Design)
|
|
||||||
})
|
})
|
||||||
}
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, emailTemplate)
|
||||||
design = "Test design"
|
})
|
||||||
|
|
||||||
for _, eventType := range s.TestInfo.TestEmailTemplateEventTypes {
|
for _, eventType := range s.TestInfo.TestEmailTemplateEventTypes {
|
||||||
t.Run("should add email template for "+eventType, func(t *testing.T) {
|
t.Run("should add email template for "+eventType, func(t *testing.T) {
|
||||||
@@ -82,7 +68,7 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||||||
EventName: eventType,
|
EventName: eventType,
|
||||||
Template: "Test email",
|
Template: "Test email",
|
||||||
Subject: "Test email",
|
Subject: "Test email",
|
||||||
Design: &design,
|
Design: "Test design",
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, emailTemplate)
|
assert.NotNil(t, emailTemplate)
|
||||||
|
@@ -20,7 +20,6 @@ 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://0.0.0.0:8000",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testDBs := strings.Split(os.Getenv("TEST_DBS"), ",")
|
testDBs := strings.Split(os.Getenv("TEST_DBS"), ",")
|
||||||
@@ -52,33 +51,25 @@ 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.Logf("Error initializing database: %s", err.Error())
|
t.Errorf("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 && envData.ID != "" {
|
if err == nil {
|
||||||
envData.EnvData = ""
|
envData.EnvData = ""
|
||||||
_, err = db.Provider.UpdateEnv(ctx, envData)
|
_, err = db.Provider.UpdateEnv(ctx, envData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("Error updating env: %s", err.Error())
|
t.Errorf("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.Logf("Error persisting env: %s", err.Error())
|
t.Errorf("Error persisting env: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEnv, "test")
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEnv, "test")
|
||||||
|
@@ -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://62f93101e05644803533cf36.mockapi.io/authorizer/webhook",
|
WebhookEndpoint: "https://62cbc6738042b16aa7c22df2.mockapi.io/api/v1/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},
|
||||||
}
|
}
|
||||||
|
@@ -93,6 +93,5 @@ 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"])
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -114,17 +114,16 @@ func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonc
|
|||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
customClaims := jwt.MapClaims{
|
customClaims := jwt.MapClaims{
|
||||||
"iss": hostname,
|
"iss": hostname,
|
||||||
"aud": clientID,
|
"aud": clientID,
|
||||||
"sub": user.ID,
|
"sub": user.ID,
|
||||||
"exp": expiresAt,
|
"exp": expiresAt,
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": constants.TokenTypeRefreshToken,
|
"token_type": constants.TokenTypeRefreshToken,
|
||||||
"roles": roles,
|
"roles": roles,
|
||||||
"scope": scopes,
|
"scope": scopes,
|
||||||
"nonce": nonce,
|
"nonce": nonce,
|
||||||
"login_method": loginMethod,
|
"login_method": loginMethod,
|
||||||
"allowed_roles": strings.Split(user.Roles, ","),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := SignJWTToken(customClaims)
|
token, err := SignJWTToken(customClaims)
|
||||||
@@ -154,17 +153,16 @@ func CreateAccessToken(user models.User, roles, scopes []string, hostName, nonce
|
|||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
customClaims := jwt.MapClaims{
|
customClaims := jwt.MapClaims{
|
||||||
"iss": hostName,
|
"iss": hostName,
|
||||||
"aud": clientID,
|
"aud": clientID,
|
||||||
"nonce": nonce,
|
"nonce": nonce,
|
||||||
"sub": user.ID,
|
"sub": user.ID,
|
||||||
"exp": expiresAt,
|
"exp": expiresAt,
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": constants.TokenTypeAccessToken,
|
"token_type": constants.TokenTypeAccessToken,
|
||||||
"scope": scopes,
|
"scope": scopes,
|
||||||
"roles": roles,
|
"roles": roles,
|
||||||
"login_method": loginMethod,
|
"login_method": loginMethod,
|
||||||
"allowed_roles": strings.Split(user.Roles, ","),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := SignJWTToken(customClaims)
|
token, err := SignJWTToken(customClaims)
|
||||||
@@ -258,6 +256,7 @@ func ValidateRefreshToken(gc *gin.Context, refreshToken string) (map[string]inte
|
|||||||
if loginMethod != nil && loginMethod != "" {
|
if loginMethod != nil && loginMethod != "" {
|
||||||
sessionKey = loginMethod.(string) + ":" + userID
|
sessionKey = loginMethod.(string) + ":" + userID
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+nonce)
|
token, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+nonce)
|
||||||
if nonce == "" || err != nil {
|
if nonce == "" || err != nil {
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
|
@@ -81,8 +81,17 @@ 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, redirectURI string) string {
|
func GetForgotPasswordURL(token, hostname string) string {
|
||||||
verificationURL := redirectURI + "?token=" + token
|
resetPasswordUrl, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
templates/authorize_form_post.tmpl
Normal file
13
templates/authorize_form_post.tmpl
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Authorization Response</title>
|
||||||
|
</head>
|
||||||
|
<body onload="document.forms['authorize_form_post'].submit()">
|
||||||
|
<form action="{{.target_origin}}" name="authorize_form_post" method="POST">
|
||||||
|
{{ range $key, $val := .authorization_response }}
|
||||||
|
<input type="hidden" key="{{$key}}" value="{{$val}}" name="{{$key}}" id="{{$key}}" />
|
||||||
|
{{ end }}
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
Reference in New Issue
Block a user