Compare commits
18 Commits
1.1.21.bet
...
feat/user-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0f67d74657 | ||
![]() |
6e09307c22 | ||
![]() |
7fc69dcc55 | ||
![]() |
8449821d1b | ||
![]() |
476bdf00fc | ||
![]() |
e41f123866 | ||
![]() |
094782aeca | ||
![]() |
9630cbbc3e | ||
![]() |
1ac060136a | ||
![]() |
844e867d96 | ||
![]() |
820d294130 | ||
![]() |
e37472d498 | ||
![]() |
589af96888 | ||
![]() |
dccc70e5c0 | ||
![]() |
896d8e046d | ||
![]() |
a6d5d4af24 | ||
![]() |
cc4eaa2847 | ||
![]() |
dba8944565 |
@@ -6,4 +6,5 @@ SMTP_HOST=smtp.mailtrap.io
|
||||
SMTP_PORT=2525
|
||||
SMTP_USERNAME=test
|
||||
SMTP_PASSWORD=test
|
||||
SENDER_EMAIL="info@authorizer.dev"
|
||||
SENDER_EMAIL="info@authorizer.dev"
|
||||
AWS_REGION=ap-south-1
|
8
Makefile
8
Makefile
@@ -23,14 +23,20 @@ test-arangodb:
|
||||
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
|
||||
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:
|
||||
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_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
|
||||
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb" go test -p 1 -v ./test
|
||||
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,dynamodb" go test -p 1 -v ./test
|
||||
docker rm -vf authorizer_scylla_db
|
||||
docker rm -vf authorizer_mongodb_db
|
||||
docker rm -vf authorizer_arangodb
|
||||
docker rm -vf dynamodb-local-test
|
||||
generate:
|
||||
cd server && go run github.com/99designs/gqlgen generate && go mod tidy
|
||||
|
@@ -1,250 +1,263 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Center,
|
||||
Flex,
|
||||
MenuItem,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Stack,
|
||||
useDisclosure,
|
||||
Text,
|
||||
useToast,
|
||||
Button,
|
||||
Center,
|
||||
Flex,
|
||||
MenuItem,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Stack,
|
||||
useDisclosure,
|
||||
Text,
|
||||
useToast,
|
||||
} from '@chakra-ui/react';
|
||||
import { useClient } from 'urql';
|
||||
import { FaSave } from 'react-icons/fa';
|
||||
import InputField from './InputField';
|
||||
import {
|
||||
ArrayInputType,
|
||||
DateInputType,
|
||||
SelectInputType,
|
||||
TextInputType,
|
||||
DateInputType,
|
||||
MultiSelectInputType,
|
||||
SelectInputType,
|
||||
TextInputType,
|
||||
} from '../constants';
|
||||
import { getObjectDiff } from '../utils';
|
||||
import { UpdateUser } from '../graphql/mutation';
|
||||
import { GetAvailableRolesQuery } from '../graphql/queries';
|
||||
|
||||
const GenderTypes = {
|
||||
Undisclosed: null,
|
||||
Male: 'Male',
|
||||
Female: 'Female',
|
||||
Undisclosed: null,
|
||||
Male: 'Male',
|
||||
Female: 'Female',
|
||||
};
|
||||
|
||||
interface userDataTypes {
|
||||
id: string;
|
||||
email: string;
|
||||
given_name: string;
|
||||
family_name: string;
|
||||
middle_name: string;
|
||||
nickname: string;
|
||||
gender: string;
|
||||
birthdate: string;
|
||||
phone_number: string;
|
||||
picture: string;
|
||||
roles: [string] | [];
|
||||
id: string;
|
||||
email: string;
|
||||
given_name: string;
|
||||
family_name: string;
|
||||
middle_name: string;
|
||||
nickname: string;
|
||||
gender: string;
|
||||
birthdate: string;
|
||||
phone_number: string;
|
||||
picture: string;
|
||||
roles: [string] | [];
|
||||
}
|
||||
|
||||
const EditUserModal = ({
|
||||
user,
|
||||
updateUserList,
|
||||
user,
|
||||
updateUserList,
|
||||
}: {
|
||||
user: userDataTypes;
|
||||
updateUserList: Function;
|
||||
user: userDataTypes;
|
||||
updateUserList: Function;
|
||||
}) => {
|
||||
const client = useClient();
|
||||
const toast = useToast();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [userData, setUserData] = React.useState<userDataTypes>({
|
||||
id: '',
|
||||
email: '',
|
||||
given_name: '',
|
||||
family_name: '',
|
||||
middle_name: '',
|
||||
nickname: '',
|
||||
gender: '',
|
||||
birthdate: '',
|
||||
phone_number: '',
|
||||
picture: '',
|
||||
roles: [],
|
||||
});
|
||||
React.useEffect(() => {
|
||||
setUserData(user);
|
||||
}, []);
|
||||
const saveHandler = async () => {
|
||||
const diff = getObjectDiff(user, userData);
|
||||
const updatedUserData = diff.reduce(
|
||||
(acc: any, property: string) => ({
|
||||
...acc,
|
||||
// @ts-ignore
|
||||
[property]: userData[property],
|
||||
}),
|
||||
{},
|
||||
);
|
||||
const res = await client
|
||||
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })
|
||||
.toPromise();
|
||||
if (res.error) {
|
||||
toast({
|
||||
title: 'User data update failed',
|
||||
isClosable: true,
|
||||
status: 'error',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
} else if (res.data?._update_user?.id) {
|
||||
toast({
|
||||
title: 'User data update successful',
|
||||
isClosable: true,
|
||||
status: 'success',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
}
|
||||
onClose();
|
||||
updateUserList();
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<MenuItem onClick={onOpen}>Edit User Details</MenuItem>
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Edit User Details</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Stack>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Given Name:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.GIVEN_NAME}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Middle Name:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.MIDDLE_NAME}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Family Name:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.FAMILY_NAME}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Birth Date:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={DateInputType.BIRTHDATE}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Nickname:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.NICKNAME}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Gender:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={SelectInputType.GENDER}
|
||||
value={userData.gender}
|
||||
options={GenderTypes}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Phone Number:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.PHONE_NUMBER}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Picture:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.PICTURE}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Roles:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={ArrayInputType.USER_ROLES}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</ModalBody>
|
||||
const client = useClient();
|
||||
const toast = useToast();
|
||||
const [availableRoles, setAvailableRoles] = useState<string[]>([]);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [userData, setUserData] = useState<userDataTypes>({
|
||||
id: '',
|
||||
email: '',
|
||||
given_name: '',
|
||||
family_name: '',
|
||||
middle_name: '',
|
||||
nickname: '',
|
||||
gender: '',
|
||||
birthdate: '',
|
||||
phone_number: '',
|
||||
picture: '',
|
||||
roles: [],
|
||||
});
|
||||
React.useEffect(() => {
|
||||
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 diff = getObjectDiff(user, userData);
|
||||
const updatedUserData = diff.reduce(
|
||||
(acc: any, property: string) => ({
|
||||
...acc,
|
||||
// @ts-ignore
|
||||
[property]: userData[property],
|
||||
}),
|
||||
{},
|
||||
);
|
||||
const res = await client
|
||||
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })
|
||||
.toPromise();
|
||||
if (res.error) {
|
||||
toast({
|
||||
title: 'User data update failed',
|
||||
isClosable: true,
|
||||
status: 'error',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
} else if (res.data?._update_user?.id) {
|
||||
toast({
|
||||
title: 'User data update successful',
|
||||
isClosable: true,
|
||||
status: 'success',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
}
|
||||
onClose();
|
||||
updateUserList();
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<MenuItem onClick={onOpen}>Edit User Details</MenuItem>
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Edit User Details</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Stack>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Given Name:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.GIVEN_NAME}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Middle Name:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.MIDDLE_NAME}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Family Name:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.FAMILY_NAME}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Birth Date:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={DateInputType.BIRTHDATE}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Nickname:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.NICKNAME}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Gender:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={SelectInputType.GENDER}
|
||||
value={userData.gender}
|
||||
options={GenderTypes}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Phone Number:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.PHONE_NUMBER}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Picture:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
inputType={TextInputType.PICTURE}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||
<Text fontSize="sm">Roles:</Text>
|
||||
</Flex>
|
||||
<Center w="70%">
|
||||
<InputField
|
||||
variables={userData}
|
||||
setVariables={setUserData}
|
||||
availableRoles={availableRoles}
|
||||
inputType={MultiSelectInputType.USER_ROLES}
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
leftIcon={<FaSave />}
|
||||
colorScheme="blue"
|
||||
variant="solid"
|
||||
onClick={saveHandler}
|
||||
isDisabled={false}
|
||||
>
|
||||
<Center h="100%" pt="5%">
|
||||
Save
|
||||
</Center>
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
<ModalFooter>
|
||||
<Button
|
||||
leftIcon={<FaSave />}
|
||||
colorScheme="blue"
|
||||
variant="solid"
|
||||
onClick={saveHandler}
|
||||
isDisabled={false}
|
||||
>
|
||||
<Center h="100%" pt="5%">
|
||||
Save
|
||||
</Center>
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditUserModal;
|
||||
|
@@ -1,340 +1,432 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Input,
|
||||
Center,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
Tag,
|
||||
TagLabel,
|
||||
TagRightIcon,
|
||||
Select,
|
||||
Textarea,
|
||||
Switch,
|
||||
Text,
|
||||
Box,
|
||||
Flex,
|
||||
Input,
|
||||
Center,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
Tag,
|
||||
TagLabel,
|
||||
TagRightIcon,
|
||||
Select,
|
||||
Textarea,
|
||||
Switch,
|
||||
Text,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItemOption,
|
||||
MenuOptionGroup,
|
||||
Button,
|
||||
Menu,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
FaRegClone,
|
||||
FaRegEye,
|
||||
FaRegEyeSlash,
|
||||
FaPlus,
|
||||
FaTimes,
|
||||
FaRegClone,
|
||||
FaRegEye,
|
||||
FaRegEyeSlash,
|
||||
FaPlus,
|
||||
FaTimes,
|
||||
FaAngleDown,
|
||||
} from 'react-icons/fa';
|
||||
import {
|
||||
ArrayInputOperations,
|
||||
ArrayInputType,
|
||||
SelectInputType,
|
||||
HiddenInputType,
|
||||
TextInputType,
|
||||
TextAreaInputType,
|
||||
SwitchInputType,
|
||||
DateInputType,
|
||||
ArrayInputOperations,
|
||||
ArrayInputType,
|
||||
SelectInputType,
|
||||
HiddenInputType,
|
||||
TextInputType,
|
||||
TextAreaInputType,
|
||||
SwitchInputType,
|
||||
DateInputType,
|
||||
MultiSelectInputType,
|
||||
} from '../constants';
|
||||
import { copyTextToClipboard } from '../utils';
|
||||
|
||||
const InputField = ({
|
||||
inputType,
|
||||
variables,
|
||||
setVariables,
|
||||
fieldVisibility,
|
||||
setFieldVisibility,
|
||||
...downshiftProps
|
||||
inputType,
|
||||
variables,
|
||||
setVariables,
|
||||
fieldVisibility,
|
||||
setFieldVisibility,
|
||||
availableRoles,
|
||||
...downshiftProps
|
||||
}: any) => {
|
||||
const props = {
|
||||
size: 'sm',
|
||||
...downshiftProps,
|
||||
};
|
||||
const [inputFieldVisibility, setInputFieldVisibility] = React.useState<
|
||||
Record<string, boolean>
|
||||
>({
|
||||
ROLES: false,
|
||||
DEFAULT_ROLES: false,
|
||||
PROTECTED_ROLES: false,
|
||||
ALLOWED_ORIGINS: false,
|
||||
roles: false,
|
||||
});
|
||||
const [inputData, setInputData] = React.useState<Record<string, string>>({
|
||||
ROLES: '',
|
||||
DEFAULT_ROLES: '',
|
||||
PROTECTED_ROLES: '',
|
||||
ALLOWED_ORIGINS: '',
|
||||
roles: '',
|
||||
});
|
||||
const updateInputHandler = (
|
||||
type: string,
|
||||
operation: any,
|
||||
role: string = '',
|
||||
) => {
|
||||
if (operation === ArrayInputOperations.APPEND) {
|
||||
if (inputData[type] !== '') {
|
||||
setVariables({
|
||||
...variables,
|
||||
[type]: [...variables[type], inputData[type]],
|
||||
});
|
||||
setInputData({ ...inputData, [type]: '' });
|
||||
}
|
||||
setInputFieldVisibility({ ...inputFieldVisibility, [type]: false });
|
||||
}
|
||||
if (operation === ArrayInputOperations.REMOVE) {
|
||||
let updatedEnvVars = variables[type].filter(
|
||||
(item: string) => item !== role,
|
||||
);
|
||||
setVariables({
|
||||
...variables,
|
||||
[type]: updatedEnvVars,
|
||||
});
|
||||
}
|
||||
};
|
||||
if (Object.values(TextInputType).includes(inputType)) {
|
||||
return (
|
||||
<InputGroup size="sm">
|
||||
<Input
|
||||
{...props}
|
||||
value={variables[inputType] ? variables[inputType] : ''}
|
||||
onChange={(
|
||||
event: Event & {
|
||||
target: HTMLInputElement;
|
||||
},
|
||||
) =>
|
||||
setVariables({
|
||||
...variables,
|
||||
[inputType]: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<InputRightElement
|
||||
children={<FaRegClone color="#bfbfbf" />}
|
||||
cursor="pointer"
|
||||
onClick={() => copyTextToClipboard(variables[inputType])}
|
||||
/>
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
if (Object.values(HiddenInputType).includes(inputType)) {
|
||||
return (
|
||||
<InputGroup size="sm">
|
||||
<Input
|
||||
{...props}
|
||||
value={variables[inputType] ?? ''}
|
||||
onChange={(
|
||||
event: Event & {
|
||||
target: HTMLInputElement;
|
||||
},
|
||||
) =>
|
||||
setVariables({
|
||||
...variables,
|
||||
[inputType]: event.target.value,
|
||||
})
|
||||
}
|
||||
type={!fieldVisibility[inputType] ? 'password' : 'text'}
|
||||
/>
|
||||
<InputRightElement
|
||||
right="15px"
|
||||
children={
|
||||
<Flex>
|
||||
{fieldVisibility[inputType] ? (
|
||||
<Center
|
||||
w="25px"
|
||||
margin="0 1.5%"
|
||||
cursor="pointer"
|
||||
onClick={() =>
|
||||
setFieldVisibility({
|
||||
...fieldVisibility,
|
||||
[inputType]: false,
|
||||
})
|
||||
}
|
||||
>
|
||||
<FaRegEyeSlash color="#bfbfbf" />
|
||||
</Center>
|
||||
) : (
|
||||
<Center
|
||||
w="25px"
|
||||
margin="0 1.5%"
|
||||
cursor="pointer"
|
||||
onClick={() =>
|
||||
setFieldVisibility({
|
||||
...fieldVisibility,
|
||||
[inputType]: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<FaRegEye color="#bfbfbf" />
|
||||
</Center>
|
||||
)}
|
||||
<Center
|
||||
w="25px"
|
||||
margin="0 1.5%"
|
||||
cursor="pointer"
|
||||
onClick={() => copyTextToClipboard(variables[inputType])}
|
||||
>
|
||||
<FaRegClone color="#bfbfbf" />
|
||||
</Center>
|
||||
</Flex>
|
||||
}
|
||||
/>
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
if (Object.values(ArrayInputType).includes(inputType)) {
|
||||
return (
|
||||
<Flex
|
||||
border="1px solid #e2e8f0"
|
||||
w="100%"
|
||||
borderRadius={5}
|
||||
paddingTop="0.5%"
|
||||
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
|
||||
overflowY="hidden"
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
{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>
|
||||
))}
|
||||
{inputFieldVisibility[inputType] ? (
|
||||
<Box ml="1%" mb="0.75%">
|
||||
<Input
|
||||
type="text"
|
||||
size="xs"
|
||||
minW="150px"
|
||||
placeholder="add a new value"
|
||||
value={inputData[inputType] ?? ''}
|
||||
onChange={(e: any) => {
|
||||
setInputData({ ...inputData, [inputType]: e.target.value });
|
||||
}}
|
||||
onBlur={() =>
|
||||
updateInputHandler(inputType, ArrayInputOperations.APPEND)
|
||||
}
|
||||
onKeyPress={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
updateInputHandler(inputType, ArrayInputOperations.APPEND);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
marginLeft="0.5%"
|
||||
cursor="pointer"
|
||||
onClick={() =>
|
||||
setInputFieldVisibility({
|
||||
...inputFieldVisibility,
|
||||
[inputType]: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<Tag
|
||||
size="sm"
|
||||
variant="outline"
|
||||
colorScheme="gray"
|
||||
minW="fit-content"
|
||||
>
|
||||
<FaPlus />
|
||||
</Tag>
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
if (Object.values(SelectInputType).includes(inputType)) {
|
||||
const { options, ...rest } = props;
|
||||
return (
|
||||
<Select
|
||||
size="sm"
|
||||
{...rest}
|
||||
value={variables[inputType] ? variables[inputType] : ''}
|
||||
onChange={(e) =>
|
||||
setVariables({ ...variables, [inputType]: e.target.value })
|
||||
}
|
||||
>
|
||||
{Object.entries(options).map(([key, value]: any) => (
|
||||
<option value={value} key={key}>
|
||||
{key}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
if (Object.values(TextAreaInputType).includes(inputType)) {
|
||||
return (
|
||||
<Textarea
|
||||
{...props}
|
||||
size="lg"
|
||||
fontSize={14}
|
||||
value={variables[inputType] ? variables[inputType] : ''}
|
||||
onChange={(
|
||||
event: Event & {
|
||||
target: HTMLInputElement;
|
||||
},
|
||||
) =>
|
||||
setVariables({
|
||||
...variables,
|
||||
[inputType]: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (Object.values(SwitchInputType).includes(inputType)) {
|
||||
return (
|
||||
<Flex w="25%" justifyContent="space-between">
|
||||
<Text h="75%" fontWeight="bold" marginRight="2">
|
||||
Off
|
||||
</Text>
|
||||
<Switch
|
||||
size="md"
|
||||
isChecked={variables[inputType]}
|
||||
onChange={() => {
|
||||
setVariables({
|
||||
...variables,
|
||||
[inputType]: !variables[inputType],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Text h="75%" fontWeight="bold" marginLeft="2">
|
||||
On
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
if (Object.values(DateInputType).includes(inputType)) {
|
||||
return (
|
||||
<Flex border="1px solid #e2e8f0" w="100%" h="33px" padding="1%">
|
||||
<input
|
||||
type="date"
|
||||
style={{ width: '100%', paddingLeft: '2.5%' }}
|
||||
value={variables[inputType] ? variables[inputType] : ''}
|
||||
onChange={(e) =>
|
||||
setVariables({ ...variables, [inputType]: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
const props = {
|
||||
size: 'sm',
|
||||
...downshiftProps,
|
||||
};
|
||||
const [availableUserRoles, setAvailableUserRoles] =
|
||||
useState<string[]>(availableRoles);
|
||||
const [inputFieldVisibility, setInputFieldVisibility] = useState<
|
||||
Record<string, boolean>
|
||||
>({
|
||||
ROLES: false,
|
||||
DEFAULT_ROLES: false,
|
||||
PROTECTED_ROLES: false,
|
||||
ALLOWED_ORIGINS: false,
|
||||
roles: false,
|
||||
});
|
||||
const [inputData, setInputData] = useState<Record<string, string>>({
|
||||
ROLES: '',
|
||||
DEFAULT_ROLES: '',
|
||||
PROTECTED_ROLES: '',
|
||||
ALLOWED_ORIGINS: '',
|
||||
roles: '',
|
||||
});
|
||||
const updateInputHandler = (
|
||||
type: string,
|
||||
operation: any,
|
||||
role: string = '',
|
||||
) => {
|
||||
if (operation === ArrayInputOperations.APPEND) {
|
||||
if (inputData[type] !== '') {
|
||||
setVariables({
|
||||
...variables,
|
||||
[type]: [...variables[type], inputData[type]],
|
||||
});
|
||||
setInputData({ ...inputData, [type]: '' });
|
||||
}
|
||||
setInputFieldVisibility({ ...inputFieldVisibility, [type]: false });
|
||||
}
|
||||
if (operation === ArrayInputOperations.REMOVE) {
|
||||
let updatedEnvVars = variables[type].filter(
|
||||
(item: string) => item !== role,
|
||||
);
|
||||
setVariables({
|
||||
...variables,
|
||||
[type]: updatedEnvVars,
|
||||
});
|
||||
}
|
||||
};
|
||||
if (Object.values(TextInputType).includes(inputType)) {
|
||||
return (
|
||||
<InputGroup size="sm">
|
||||
<Input
|
||||
{...props}
|
||||
value={variables[inputType] ? variables[inputType] : ''}
|
||||
onChange={(
|
||||
event: Event & {
|
||||
target: HTMLInputElement;
|
||||
},
|
||||
) =>
|
||||
setVariables({
|
||||
...variables,
|
||||
[inputType]: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<InputRightElement
|
||||
children={<FaRegClone color="#bfbfbf" />}
|
||||
cursor="pointer"
|
||||
onClick={() => copyTextToClipboard(variables[inputType])}
|
||||
/>
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
if (Object.values(HiddenInputType).includes(inputType)) {
|
||||
return (
|
||||
<InputGroup size="sm">
|
||||
<Input
|
||||
{...props}
|
||||
value={variables[inputType] || ''}
|
||||
onChange={(
|
||||
event: Event & {
|
||||
target: HTMLInputElement;
|
||||
},
|
||||
) =>
|
||||
setVariables({
|
||||
...variables,
|
||||
[inputType]: event.target.value,
|
||||
})
|
||||
}
|
||||
type={!fieldVisibility[inputType] ? 'password' : 'text'}
|
||||
/>
|
||||
<InputRightElement
|
||||
right="15px"
|
||||
children={
|
||||
<Flex>
|
||||
{fieldVisibility[inputType] ? (
|
||||
<Center
|
||||
w="25px"
|
||||
margin="0 1.5%"
|
||||
cursor="pointer"
|
||||
onClick={() =>
|
||||
setFieldVisibility({
|
||||
...fieldVisibility,
|
||||
[inputType]: false,
|
||||
})
|
||||
}
|
||||
>
|
||||
<FaRegEyeSlash color="#bfbfbf" />
|
||||
</Center>
|
||||
) : (
|
||||
<Center
|
||||
w="25px"
|
||||
margin="0 1.5%"
|
||||
cursor="pointer"
|
||||
onClick={() =>
|
||||
setFieldVisibility({
|
||||
...fieldVisibility,
|
||||
[inputType]: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<FaRegEye color="#bfbfbf" />
|
||||
</Center>
|
||||
)}
|
||||
<Center
|
||||
w="25px"
|
||||
margin="0 1.5%"
|
||||
cursor="pointer"
|
||||
onClick={() => copyTextToClipboard(variables[inputType])}
|
||||
>
|
||||
<FaRegClone color="#bfbfbf" />
|
||||
</Center>
|
||||
</Flex>
|
||||
}
|
||||
/>
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
if (Object.values(ArrayInputType).includes(inputType)) {
|
||||
return (
|
||||
<Flex
|
||||
border="1px solid #e2e8f0"
|
||||
w="100%"
|
||||
borderRadius={5}
|
||||
paddingTop="0.5%"
|
||||
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
|
||||
overflowY="hidden"
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
{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>
|
||||
))}
|
||||
{inputFieldVisibility[inputType] ? (
|
||||
<Box ml="1%" mb="0.75%">
|
||||
<Input
|
||||
type="text"
|
||||
size="xs"
|
||||
minW="150px"
|
||||
placeholder="add a new value"
|
||||
value={inputData[inputType] || ''}
|
||||
onChange={(e: any) => {
|
||||
setInputData({ ...inputData, [inputType]: e.target.value });
|
||||
}}
|
||||
onBlur={() =>
|
||||
updateInputHandler(inputType, ArrayInputOperations.APPEND)
|
||||
}
|
||||
onKeyPress={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
updateInputHandler(inputType, ArrayInputOperations.APPEND);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
marginLeft="0.5%"
|
||||
cursor="pointer"
|
||||
onClick={() =>
|
||||
setInputFieldVisibility({
|
||||
...inputFieldVisibility,
|
||||
[inputType]: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<Tag
|
||||
size="sm"
|
||||
variant="outline"
|
||||
colorScheme="gray"
|
||||
minW="fit-content"
|
||||
>
|
||||
<FaPlus />
|
||||
</Tag>
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
if (Object.values(SelectInputType).includes(inputType)) {
|
||||
const { options, ...rest } = props;
|
||||
return (
|
||||
<Select
|
||||
size="sm"
|
||||
{...rest}
|
||||
value={variables[inputType] ? variables[inputType] : ''}
|
||||
onChange={(e) =>
|
||||
setVariables({ ...variables, [inputType]: e.target.value })
|
||||
}
|
||||
>
|
||||
{Object.entries(options).map(([key, value]: any) => (
|
||||
<option value={value} key={key}>
|
||||
{key}
|
||||
</option>
|
||||
))}
|
||||
</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)) {
|
||||
return (
|
||||
<Textarea
|
||||
{...props}
|
||||
size="lg"
|
||||
fontSize={14}
|
||||
value={variables[inputType] ? variables[inputType] : ''}
|
||||
onChange={(
|
||||
event: Event & {
|
||||
target: HTMLInputElement;
|
||||
},
|
||||
) =>
|
||||
setVariables({
|
||||
...variables,
|
||||
[inputType]: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (Object.values(SwitchInputType).includes(inputType)) {
|
||||
return (
|
||||
<Flex w="25%" justifyContent="space-between">
|
||||
<Text h="75%" fontWeight="bold" marginRight="2">
|
||||
Off
|
||||
</Text>
|
||||
<Switch
|
||||
size="md"
|
||||
isChecked={variables[inputType]}
|
||||
onChange={() => {
|
||||
setVariables({
|
||||
...variables,
|
||||
[inputType]: !variables[inputType],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Text h="75%" fontWeight="bold" marginLeft="2">
|
||||
On
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
if (Object.values(DateInputType).includes(inputType)) {
|
||||
return (
|
||||
<Flex border="1px solid #e2e8f0" w="100%" h="33px" padding="1%">
|
||||
<input
|
||||
type="date"
|
||||
style={{ width: '100%', paddingLeft: '2.5%' }}
|
||||
value={variables[inputType] ? variables[inputType] : ''}
|
||||
onChange={(e) =>
|
||||
setVariables({ ...variables, [inputType]: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default InputField;
|
||||
|
@@ -1,309 +1,312 @@
|
||||
export const LOGO_URL =
|
||||
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
|
||||
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
|
||||
|
||||
export const TextInputType = {
|
||||
ACCESS_TOKEN_EXPIRY_TIME: 'ACCESS_TOKEN_EXPIRY_TIME',
|
||||
CLIENT_ID: 'CLIENT_ID',
|
||||
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
|
||||
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
|
||||
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
|
||||
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
|
||||
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
|
||||
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
|
||||
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
|
||||
REDIS_URL: 'REDIS_URL',
|
||||
SMTP_HOST: 'SMTP_HOST',
|
||||
SMTP_PORT: 'SMTP_PORT',
|
||||
SMTP_USERNAME: 'SMTP_USERNAME',
|
||||
SENDER_EMAIL: 'SENDER_EMAIL',
|
||||
ORGANIZATION_NAME: 'ORGANIZATION_NAME',
|
||||
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
|
||||
DATABASE_NAME: 'DATABASE_NAME',
|
||||
DATABASE_TYPE: 'DATABASE_TYPE',
|
||||
DATABASE_URL: 'DATABASE_URL',
|
||||
GIVEN_NAME: 'given_name',
|
||||
MIDDLE_NAME: 'middle_name',
|
||||
FAMILY_NAME: 'family_name',
|
||||
NICKNAME: 'nickname',
|
||||
PHONE_NUMBER: 'phone_number',
|
||||
PICTURE: 'picture',
|
||||
ACCESS_TOKEN_EXPIRY_TIME: 'ACCESS_TOKEN_EXPIRY_TIME',
|
||||
CLIENT_ID: 'CLIENT_ID',
|
||||
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
|
||||
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
|
||||
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
|
||||
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
|
||||
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
|
||||
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
|
||||
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
|
||||
REDIS_URL: 'REDIS_URL',
|
||||
SMTP_HOST: 'SMTP_HOST',
|
||||
SMTP_PORT: 'SMTP_PORT',
|
||||
SMTP_USERNAME: 'SMTP_USERNAME',
|
||||
SENDER_EMAIL: 'SENDER_EMAIL',
|
||||
ORGANIZATION_NAME: 'ORGANIZATION_NAME',
|
||||
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
|
||||
DATABASE_NAME: 'DATABASE_NAME',
|
||||
DATABASE_TYPE: 'DATABASE_TYPE',
|
||||
DATABASE_URL: 'DATABASE_URL',
|
||||
GIVEN_NAME: 'given_name',
|
||||
MIDDLE_NAME: 'middle_name',
|
||||
FAMILY_NAME: 'family_name',
|
||||
NICKNAME: 'nickname',
|
||||
PHONE_NUMBER: 'phone_number',
|
||||
PICTURE: 'picture',
|
||||
};
|
||||
|
||||
export const HiddenInputType = {
|
||||
CLIENT_SECRET: 'CLIENT_SECRET',
|
||||
GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET',
|
||||
GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET',
|
||||
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
|
||||
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
|
||||
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
|
||||
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
|
||||
JWT_SECRET: 'JWT_SECRET',
|
||||
SMTP_PASSWORD: 'SMTP_PASSWORD',
|
||||
ADMIN_SECRET: 'ADMIN_SECRET',
|
||||
OLD_ADMIN_SECRET: 'OLD_ADMIN_SECRET',
|
||||
CLIENT_SECRET: 'CLIENT_SECRET',
|
||||
GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET',
|
||||
GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET',
|
||||
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
|
||||
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
|
||||
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
|
||||
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
|
||||
JWT_SECRET: 'JWT_SECRET',
|
||||
SMTP_PASSWORD: 'SMTP_PASSWORD',
|
||||
ADMIN_SECRET: 'ADMIN_SECRET',
|
||||
OLD_ADMIN_SECRET: 'OLD_ADMIN_SECRET',
|
||||
};
|
||||
|
||||
export const ArrayInputType = {
|
||||
ROLES: 'ROLES',
|
||||
DEFAULT_ROLES: 'DEFAULT_ROLES',
|
||||
PROTECTED_ROLES: 'PROTECTED_ROLES',
|
||||
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
|
||||
USER_ROLES: 'roles',
|
||||
ROLES: 'ROLES',
|
||||
DEFAULT_ROLES: 'DEFAULT_ROLES',
|
||||
PROTECTED_ROLES: 'PROTECTED_ROLES',
|
||||
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
|
||||
};
|
||||
|
||||
export const SelectInputType = {
|
||||
JWT_TYPE: 'JWT_TYPE',
|
||||
GENDER: 'gender',
|
||||
JWT_TYPE: 'JWT_TYPE',
|
||||
GENDER: 'gender',
|
||||
};
|
||||
|
||||
export const MultiSelectInputType = {
|
||||
USER_ROLES: 'roles',
|
||||
};
|
||||
|
||||
export const TextAreaInputType = {
|
||||
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
|
||||
JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY',
|
||||
JWT_PUBLIC_KEY: 'JWT_PUBLIC_KEY',
|
||||
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
|
||||
JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY',
|
||||
JWT_PUBLIC_KEY: 'JWT_PUBLIC_KEY',
|
||||
};
|
||||
|
||||
export const SwitchInputType = {
|
||||
APP_COOKIE_SECURE: 'APP_COOKIE_SECURE',
|
||||
ADMIN_COOKIE_SECURE: 'ADMIN_COOKIE_SECURE',
|
||||
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
|
||||
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
|
||||
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
|
||||
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
|
||||
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
|
||||
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
|
||||
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
|
||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
|
||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
|
||||
APP_COOKIE_SECURE: 'APP_COOKIE_SECURE',
|
||||
ADMIN_COOKIE_SECURE: 'ADMIN_COOKIE_SECURE',
|
||||
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
|
||||
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
|
||||
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
|
||||
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
|
||||
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
|
||||
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
|
||||
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
|
||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
|
||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
|
||||
};
|
||||
|
||||
export const DateInputType = {
|
||||
BIRTHDATE: 'birthdate',
|
||||
BIRTHDATE: 'birthdate',
|
||||
};
|
||||
|
||||
export const ArrayInputOperations = {
|
||||
APPEND: 'APPEND',
|
||||
REMOVE: 'REMOVE',
|
||||
APPEND: 'APPEND',
|
||||
REMOVE: 'REMOVE',
|
||||
};
|
||||
|
||||
export const HMACEncryptionType = {
|
||||
HS256: 'HS256',
|
||||
HS384: 'HS384',
|
||||
HS512: 'HS512',
|
||||
HS256: 'HS256',
|
||||
HS384: 'HS384',
|
||||
HS512: 'HS512',
|
||||
};
|
||||
|
||||
export const RSAEncryptionType = {
|
||||
RS256: 'RS256',
|
||||
RS384: 'RS384',
|
||||
RS512: 'RS512',
|
||||
RS256: 'RS256',
|
||||
RS384: 'RS384',
|
||||
RS512: 'RS512',
|
||||
};
|
||||
|
||||
export const ECDSAEncryptionType = {
|
||||
ES256: 'ES256',
|
||||
ES384: 'ES384',
|
||||
ES512: 'ES512',
|
||||
ES256: 'ES256',
|
||||
ES384: 'ES384',
|
||||
ES512: 'ES512',
|
||||
};
|
||||
|
||||
export interface envVarTypes {
|
||||
GOOGLE_CLIENT_ID: string;
|
||||
GOOGLE_CLIENT_SECRET: string;
|
||||
GITHUB_CLIENT_ID: string;
|
||||
GITHUB_CLIENT_SECRET: string;
|
||||
FACEBOOK_CLIENT_ID: string;
|
||||
FACEBOOK_CLIENT_SECRET: string;
|
||||
LINKEDIN_CLIENT_ID: string;
|
||||
LINKEDIN_CLIENT_SECRET: string;
|
||||
APPLE_CLIENT_ID: string;
|
||||
APPLE_CLIENT_SECRET: string;
|
||||
TWITTER_CLIENT_ID: string;
|
||||
TWITTER_CLIENT_SECRET: string;
|
||||
ROLES: [string] | [];
|
||||
DEFAULT_ROLES: [string] | [];
|
||||
PROTECTED_ROLES: [string] | [];
|
||||
JWT_TYPE: string;
|
||||
JWT_SECRET: string;
|
||||
JWT_ROLE_CLAIM: string;
|
||||
JWT_PRIVATE_KEY: string;
|
||||
JWT_PUBLIC_KEY: string;
|
||||
REDIS_URL: string;
|
||||
SMTP_HOST: string;
|
||||
SMTP_PORT: string;
|
||||
SMTP_USERNAME: string;
|
||||
SMTP_PASSWORD: string;
|
||||
SENDER_EMAIL: string;
|
||||
ALLOWED_ORIGINS: [string] | [];
|
||||
ORGANIZATION_NAME: string;
|
||||
ORGANIZATION_LOGO: string;
|
||||
CUSTOM_ACCESS_TOKEN_SCRIPT: string;
|
||||
ADMIN_SECRET: string;
|
||||
APP_COOKIE_SECURE: boolean;
|
||||
ADMIN_COOKIE_SECURE: boolean;
|
||||
DISABLE_LOGIN_PAGE: boolean;
|
||||
DISABLE_MAGIC_LINK_LOGIN: boolean;
|
||||
DISABLE_EMAIL_VERIFICATION: boolean;
|
||||
DISABLE_BASIC_AUTHENTICATION: boolean;
|
||||
DISABLE_SIGN_UP: boolean;
|
||||
DISABLE_STRONG_PASSWORD: boolean;
|
||||
OLD_ADMIN_SECRET: string;
|
||||
DATABASE_NAME: string;
|
||||
DATABASE_TYPE: string;
|
||||
DATABASE_URL: string;
|
||||
ACCESS_TOKEN_EXPIRY_TIME: string;
|
||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean;
|
||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
|
||||
GOOGLE_CLIENT_ID: string;
|
||||
GOOGLE_CLIENT_SECRET: string;
|
||||
GITHUB_CLIENT_ID: string;
|
||||
GITHUB_CLIENT_SECRET: string;
|
||||
FACEBOOK_CLIENT_ID: string;
|
||||
FACEBOOK_CLIENT_SECRET: string;
|
||||
LINKEDIN_CLIENT_ID: string;
|
||||
LINKEDIN_CLIENT_SECRET: string;
|
||||
APPLE_CLIENT_ID: string;
|
||||
APPLE_CLIENT_SECRET: string;
|
||||
TWITTER_CLIENT_ID: string;
|
||||
TWITTER_CLIENT_SECRET: string;
|
||||
ROLES: [string] | [];
|
||||
DEFAULT_ROLES: [string] | [];
|
||||
PROTECTED_ROLES: [string] | [];
|
||||
JWT_TYPE: string;
|
||||
JWT_SECRET: string;
|
||||
JWT_ROLE_CLAIM: string;
|
||||
JWT_PRIVATE_KEY: string;
|
||||
JWT_PUBLIC_KEY: string;
|
||||
REDIS_URL: string;
|
||||
SMTP_HOST: string;
|
||||
SMTP_PORT: string;
|
||||
SMTP_USERNAME: string;
|
||||
SMTP_PASSWORD: string;
|
||||
SENDER_EMAIL: string;
|
||||
ALLOWED_ORIGINS: [string] | [];
|
||||
ORGANIZATION_NAME: string;
|
||||
ORGANIZATION_LOGO: string;
|
||||
CUSTOM_ACCESS_TOKEN_SCRIPT: string;
|
||||
ADMIN_SECRET: string;
|
||||
APP_COOKIE_SECURE: boolean;
|
||||
ADMIN_COOKIE_SECURE: boolean;
|
||||
DISABLE_LOGIN_PAGE: boolean;
|
||||
DISABLE_MAGIC_LINK_LOGIN: boolean;
|
||||
DISABLE_EMAIL_VERIFICATION: boolean;
|
||||
DISABLE_BASIC_AUTHENTICATION: boolean;
|
||||
DISABLE_SIGN_UP: boolean;
|
||||
DISABLE_STRONG_PASSWORD: boolean;
|
||||
OLD_ADMIN_SECRET: string;
|
||||
DATABASE_NAME: string;
|
||||
DATABASE_TYPE: string;
|
||||
DATABASE_URL: string;
|
||||
ACCESS_TOKEN_EXPIRY_TIME: string;
|
||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean;
|
||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
|
||||
}
|
||||
|
||||
export const envSubViews = {
|
||||
INSTANCE_INFO: 'instance-info',
|
||||
ROLES: 'roles',
|
||||
JWT_CONFIG: 'jwt-config',
|
||||
SESSION_STORAGE: 'session-storage',
|
||||
EMAIL_CONFIG: 'email-config',
|
||||
WHITELIST_VARIABLES: 'whitelist-variables',
|
||||
ORGANIZATION_INFO: 'organization-info',
|
||||
ACCESS_TOKEN: 'access-token',
|
||||
FEATURES: 'features',
|
||||
ADMIN_SECRET: 'admin-secret',
|
||||
DB_CRED: 'db-cred',
|
||||
INSTANCE_INFO: 'instance-info',
|
||||
ROLES: 'roles',
|
||||
JWT_CONFIG: 'jwt-config',
|
||||
SESSION_STORAGE: 'session-storage',
|
||||
EMAIL_CONFIG: 'email-config',
|
||||
WHITELIST_VARIABLES: 'whitelist-variables',
|
||||
ORGANIZATION_INFO: 'organization-info',
|
||||
ACCESS_TOKEN: 'access-token',
|
||||
FEATURES: 'features',
|
||||
ADMIN_SECRET: 'admin-secret',
|
||||
DB_CRED: 'db-cred',
|
||||
};
|
||||
|
||||
export enum WebhookInputDataFields {
|
||||
ID = 'id',
|
||||
EVENT_NAME = 'event_name',
|
||||
ENDPOINT = 'endpoint',
|
||||
ENABLED = 'enabled',
|
||||
HEADERS = 'headers',
|
||||
ID = 'id',
|
||||
EVENT_NAME = 'event_name',
|
||||
ENDPOINT = 'endpoint',
|
||||
ENABLED = 'enabled',
|
||||
HEADERS = 'headers',
|
||||
}
|
||||
|
||||
export enum EmailTemplateInputDataFields {
|
||||
ID = 'id',
|
||||
EVENT_NAME = 'event_name',
|
||||
SUBJECT = 'subject',
|
||||
CREATED_AT = 'created_at',
|
||||
TEMPLATE = 'template',
|
||||
DESIGN = 'design',
|
||||
ID = 'id',
|
||||
EVENT_NAME = 'event_name',
|
||||
SUBJECT = 'subject',
|
||||
CREATED_AT = 'created_at',
|
||||
TEMPLATE = 'template',
|
||||
DESIGN = 'design',
|
||||
}
|
||||
|
||||
export enum WebhookInputHeaderFields {
|
||||
KEY = 'key',
|
||||
VALUE = 'value',
|
||||
KEY = 'key',
|
||||
VALUE = 'value',
|
||||
}
|
||||
|
||||
export enum UpdateModalViews {
|
||||
ADD = 'add',
|
||||
Edit = 'edit',
|
||||
ADD = 'add',
|
||||
Edit = 'edit',
|
||||
}
|
||||
|
||||
export const pageLimits: number[] = [5, 10, 15];
|
||||
|
||||
export const webhookEventNames = {
|
||||
'User signup': 'user.signup',
|
||||
'User created': 'user.created',
|
||||
'User login': 'user.login',
|
||||
'User deleted': 'user.deleted',
|
||||
'User access enabled': 'user.access_enabled',
|
||||
'User access revoked': 'user.access_revoked',
|
||||
'User signup': 'user.signup',
|
||||
'User created': 'user.created',
|
||||
'User login': 'user.login',
|
||||
'User deleted': 'user.deleted',
|
||||
'User access enabled': 'user.access_enabled',
|
||||
'User access revoked': 'user.access_revoked',
|
||||
};
|
||||
|
||||
export const emailTemplateEventNames = {
|
||||
Signup: 'basic_auth_signup',
|
||||
'Magic Link Login': 'magic_link_login',
|
||||
'Update Email': 'update_email',
|
||||
'Forgot Password': 'forgot_password',
|
||||
'Verify Otp': 'verify_otp',
|
||||
'Invite member': 'invite_member',
|
||||
Signup: 'basic_auth_signup',
|
||||
'Magic Link Login': 'magic_link_login',
|
||||
'Update Email': 'update_email',
|
||||
'Forgot Password': 'forgot_password',
|
||||
'Verify Otp': 'verify_otp',
|
||||
'Invite member': 'invite_member',
|
||||
};
|
||||
|
||||
export enum webhookVerifiedStatus {
|
||||
VERIFIED = 'verified',
|
||||
NOT_VERIFIED = 'not_verified',
|
||||
PENDING = 'verification_pending',
|
||||
VERIFIED = 'verified',
|
||||
NOT_VERIFIED = 'not_verified',
|
||||
PENDING = 'verification_pending',
|
||||
}
|
||||
|
||||
export const emailTemplateVariables = {
|
||||
'user.id': {
|
||||
description: `User identifier`,
|
||||
value: '{.user.id}}',
|
||||
},
|
||||
'user.email': {
|
||||
description: 'User email address',
|
||||
value: '{.user.email}}',
|
||||
},
|
||||
'user.given_name': {
|
||||
description: `User first name`,
|
||||
value: '{.user.given_name}}',
|
||||
},
|
||||
'user.family_name': {
|
||||
description: `User last name`,
|
||||
value: '{.user.family_name}}',
|
||||
},
|
||||
'user.middle_name': {
|
||||
description: `Middle name of user`,
|
||||
value: '{.user.middle_name}}',
|
||||
},
|
||||
'user.nickname': {
|
||||
description: `Nick name of user`,
|
||||
value: '{.user.nickname}}',
|
||||
},
|
||||
'user.preferred_username': {
|
||||
description: `Username, by default it is email`,
|
||||
value: '{.user.preferred_username}}',
|
||||
},
|
||||
'user.signup_methods': {
|
||||
description: `Comma separated list of methods using which user has signed up`,
|
||||
value: '{.user.signup_methods}}',
|
||||
},
|
||||
'user.email_verified': {
|
||||
description: `Whether email is verified or not`,
|
||||
value: '{.user.email_verified}}',
|
||||
},
|
||||
'user.picture': {
|
||||
description: `URL of the user profile picture`,
|
||||
value: '{.user.picture}}',
|
||||
},
|
||||
'user.roles': {
|
||||
description: `Comma separated list of roles assigned to user`,
|
||||
value: '{.user.roles}}',
|
||||
},
|
||||
'user.gender': {
|
||||
description: `Gender of user`,
|
||||
value: '{.user.gender}}',
|
||||
},
|
||||
'user.birthdate': {
|
||||
description: `BirthDate of user`,
|
||||
value: '{.user.birthdate}}',
|
||||
},
|
||||
'user.phone_number': {
|
||||
description: `Phone number of user`,
|
||||
value: '{.user.phone_number}}',
|
||||
},
|
||||
'user.phone_number_verified': {
|
||||
description: `Whether phone number is verified or not`,
|
||||
value: '{.user.phone_number_verified}}',
|
||||
},
|
||||
'user.created_at': {
|
||||
description: `User created at time`,
|
||||
value: '{.user.created_at}}',
|
||||
},
|
||||
'user.updated_at': {
|
||||
description: `Last updated time at user`,
|
||||
value: '{.user.updated_at}}',
|
||||
},
|
||||
'organization.name': {
|
||||
description: `Organization name`,
|
||||
value: '{.organization.name}}',
|
||||
},
|
||||
'organization.logo': {
|
||||
description: `Organization logo`,
|
||||
value: '{.organization.logo}}',
|
||||
},
|
||||
verification_url: {
|
||||
description: `Verification URL in case of events other than verify otp`,
|
||||
value: '{.verification_url}}',
|
||||
},
|
||||
otp: {
|
||||
description: `OTP sent during login with Multi factor authentication`,
|
||||
value: '{.otp}}',
|
||||
},
|
||||
'user.id': {
|
||||
description: `User identifier`,
|
||||
value: '{.user.id}}',
|
||||
},
|
||||
'user.email': {
|
||||
description: 'User email address',
|
||||
value: '{.user.email}}',
|
||||
},
|
||||
'user.given_name': {
|
||||
description: `User first name`,
|
||||
value: '{.user.given_name}}',
|
||||
},
|
||||
'user.family_name': {
|
||||
description: `User last name`,
|
||||
value: '{.user.family_name}}',
|
||||
},
|
||||
'user.middle_name': {
|
||||
description: `Middle name of user`,
|
||||
value: '{.user.middle_name}}',
|
||||
},
|
||||
'user.nickname': {
|
||||
description: `Nick name of user`,
|
||||
value: '{.user.nickname}}',
|
||||
},
|
||||
'user.preferred_username': {
|
||||
description: `Username, by default it is email`,
|
||||
value: '{.user.preferred_username}}',
|
||||
},
|
||||
'user.signup_methods': {
|
||||
description: `Comma separated list of methods using which user has signed up`,
|
||||
value: '{.user.signup_methods}}',
|
||||
},
|
||||
'user.email_verified': {
|
||||
description: `Whether email is verified or not`,
|
||||
value: '{.user.email_verified}}',
|
||||
},
|
||||
'user.picture': {
|
||||
description: `URL of the user profile picture`,
|
||||
value: '{.user.picture}}',
|
||||
},
|
||||
'user.roles': {
|
||||
description: `Comma separated list of roles assigned to user`,
|
||||
value: '{.user.roles}}',
|
||||
},
|
||||
'user.gender': {
|
||||
description: `Gender of user`,
|
||||
value: '{.user.gender}}',
|
||||
},
|
||||
'user.birthdate': {
|
||||
description: `BirthDate of user`,
|
||||
value: '{.user.birthdate}}',
|
||||
},
|
||||
'user.phone_number': {
|
||||
description: `Phone number of user`,
|
||||
value: '{.user.phone_number}}',
|
||||
},
|
||||
'user.phone_number_verified': {
|
||||
description: `Whether phone number is verified or not`,
|
||||
value: '{.user.phone_number_verified}}',
|
||||
},
|
||||
'user.created_at': {
|
||||
description: `User created at time`,
|
||||
value: '{.user.created_at}}',
|
||||
},
|
||||
'user.updated_at': {
|
||||
description: `Last updated time at user`,
|
||||
value: '{.user.updated_at}}',
|
||||
},
|
||||
'organization.name': {
|
||||
description: `Organization name`,
|
||||
value: '{.organization.name}}',
|
||||
},
|
||||
'organization.logo': {
|
||||
description: `Organization logo`,
|
||||
value: '{.organization.logo}}',
|
||||
},
|
||||
verification_url: {
|
||||
description: `Verification URL in case of events other than verify otp`,
|
||||
value: '{.verification_url}}',
|
||||
},
|
||||
otp: {
|
||||
description: `OTP sent during login with Multi factor authentication`,
|
||||
value: '{.otp}}',
|
||||
},
|
||||
};
|
||||
|
||||
export const webhookPayloadExample: string = `{
|
||||
|
@@ -169,3 +169,12 @@ export const WebhookLogsQuery = `
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GetAvailableRolesQuery = `
|
||||
query {
|
||||
_env {
|
||||
ROLES
|
||||
PROTECTED_ROLES
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@@ -25,4 +25,6 @@ const (
|
||||
DbTypeCockroachDB = "cockroachdb"
|
||||
// DbTypePlanetScaleDB is the planetscale database type
|
||||
DbTypePlanetScaleDB = "planetscale"
|
||||
// DbTypeDynamoDB is the Dynamo database type
|
||||
DbTypeDynamoDB = "dynamodb"
|
||||
)
|
||||
|
@@ -21,6 +21,12 @@ const (
|
||||
EnvKeyDatabaseType = "DATABASE_TYPE"
|
||||
// EnvKeyDatabaseURL key for env variable 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 = "DATABASE_NAME"
|
||||
// EnvKeyDatabaseUsername key for env variable DATABASE_USERNAME
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/authorizerdev/authorizer/server/db/providers"
|
||||
"github.com/authorizerdev/authorizer/server/db/providers/arangodb"
|
||||
"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/sql"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
@@ -20,10 +21,11 @@ func InitDB() error {
|
||||
|
||||
envs := memorystore.RequiredEnvStoreObj.GetRequiredEnv()
|
||||
|
||||
isSQL := envs.DatabaseType != constants.DbTypeArangodb && envs.DatabaseType != constants.DbTypeMongodb && envs.DatabaseType != constants.DbTypeCassandraDB && envs.DatabaseType != constants.DbTypeScyllaDB
|
||||
isSQL := envs.DatabaseType != constants.DbTypeArangodb && envs.DatabaseType != constants.DbTypeMongodb && envs.DatabaseType != constants.DbTypeCassandraDB && envs.DatabaseType != constants.DbTypeScyllaDB && envs.DatabaseType != constants.DbTypeDynamoDB
|
||||
isArangoDB := envs.DatabaseType == constants.DbTypeArangodb
|
||||
isMongoDB := envs.DatabaseType == constants.DbTypeMongodb
|
||||
isCassandra := envs.DatabaseType == constants.DbTypeCassandraDB || envs.DatabaseType == constants.DbTypeScyllaDB
|
||||
isDynamoDB := envs.DatabaseType == constants.DbTypeDynamoDB
|
||||
|
||||
if isSQL {
|
||||
log.Info("Initializing SQL Driver for: ", envs.DatabaseType)
|
||||
@@ -61,5 +63,14 @@ 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
|
||||
}
|
||||
|
@@ -9,14 +9,14 @@ import (
|
||||
|
||||
// EmailTemplate model for database
|
||||
type EmailTemplate struct {
|
||||
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"`
|
||||
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
|
||||
Subject string `json:"subject" bson:"subject" cql:"subject"`
|
||||
Template string `json:"template" bson:"template" cql:"template"`
|
||||
Design string `json:"design" bson:"design" cql:"design"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
||||
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"`
|
||||
Subject string `json:"subject" bson:"subject" cql:"subject" dynamo:"subject"`
|
||||
Template string `json:"template" bson:"template" cql:"template" dynamo:"template"`
|
||||
Design string `json:"design" bson:"design" cql:"design" dynamo:"design"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
||||
}
|
||||
|
||||
// AsAPIEmailTemplate to return email template as graphql response object
|
||||
|
@@ -4,10 +4,10 @@ package models
|
||||
|
||||
// Env model for db
|
||||
type Env struct {
|
||||
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"`
|
||||
EnvData string `json:"env" bson:"env" cql:"env"`
|
||||
Hash string `json:"hash" bson:"hash" cql:"hash"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
||||
EnvData string `json:"env" bson:"env" cql:"env" dynamo:"env"`
|
||||
Hash string `json:"hash" bson:"hash" cql:"hash" dynamo:"hash"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
||||
}
|
||||
|
@@ -2,11 +2,15 @@ package models
|
||||
|
||||
// OTP model for database
|
||||
type OTP struct {
|
||||
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"`
|
||||
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
|
||||
Otp string `json:"otp" bson:"otp" cql:"otp"`
|
||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
||||
Email string `gorm:"unique" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
|
||||
Otp string `json:"otp" bson:"otp" cql:"otp" dynamo:"otp"`
|
||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
||||
}
|
||||
|
||||
type Paging struct {
|
||||
ID string `json:"id,omitempty" dynamo:"id,hash"`
|
||||
}
|
||||
|
@@ -4,11 +4,11 @@ package models
|
||||
|
||||
// Session model for db
|
||||
type Session struct {
|
||||
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"`
|
||||
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"`
|
||||
IP string `json:"ip" bson:"ip" cql:"ip"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
||||
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" index:"user_id,hash"`
|
||||
UserAgent string `json:"user_agent" bson:"user_agent" cql:"user_agent" dynamo:"user_agent"`
|
||||
IP string `json:"ip" bson:"ip" cql:"ip" dynamo:"ip"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
||||
}
|
||||
|
@@ -12,27 +12,27 @@ import (
|
||||
|
||||
// User model for db
|
||||
type User struct {
|
||||
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"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,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"`
|
||||
Password *string `json:"password" bson:"password" cql:"password"`
|
||||
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
|
||||
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
|
||||
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
|
||||
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name"`
|
||||
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname"`
|
||||
Gender *string `json:"gender" bson:"gender" cql:"gender"`
|
||||
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
|
||||
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"`
|
||||
Picture *string `json:"picture" bson:"picture" cql:"picture"`
|
||||
Roles string `json:"roles" bson:"roles" cql:"roles"`
|
||||
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"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
Email string `gorm:"unique" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
|
||||
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at" dynamo:"email_verified_at"`
|
||||
Password *string `json:"password" bson:"password" cql:"password" dynamo:"password"`
|
||||
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods" dynamo:"signup_methods"`
|
||||
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name" dynamo:"given_name"`
|
||||
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name" dynamo:"family_name"`
|
||||
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name" dynamo:"middle_name"`
|
||||
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname" dynamo:"nickname"`
|
||||
Gender *string `json:"gender" bson:"gender" cql:"gender" dynamo:"gender"`
|
||||
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate" dynamo:"birthdate"`
|
||||
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
|
||||
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at" dynamo:"phone_number_verified_at"`
|
||||
Picture *string `json:"picture" bson:"picture" cql:"picture" dynamo:"picture"`
|
||||
Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"`
|
||||
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp" dynamo:"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"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
||||
}
|
||||
|
||||
func (user *User) AsAPIUser() *model.User {
|
||||
|
@@ -11,16 +11,16 @@ import (
|
||||
|
||||
// VerificationRequest model for db
|
||||
type VerificationRequest struct {
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
Nonce string `json:"nonce" bson:"nonce" cql:"nonce"`
|
||||
RedirectURI string `json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
||||
Token string `json:"token" bson:"token" cql:"jwt_token" dynamo:"token" index:"token,hash"`
|
||||
Identifier string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(64)" json:"identifier" bson:"identifier" cql:"identifier" dynamo:"identifier"`
|
||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
|
||||
Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email" dynamo:"email"`
|
||||
Nonce string `json:"nonce" bson:"nonce" cql:"nonce" dynamo:"nonce"`
|
||||
RedirectURI string `json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri" dynamo:"redirect_uri"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
||||
}
|
||||
|
||||
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
||||
|
@@ -12,14 +12,14 @@ import (
|
||||
|
||||
// Webhook model for db
|
||||
type Webhook struct {
|
||||
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"`
|
||||
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
|
||||
EndPoint string `json:"endpoint" bson:"endpoint" cql:"endpoint"`
|
||||
Headers string `json:"headers" bson:"headers" cql:"headers"`
|
||||
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
||||
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"`
|
||||
EndPoint string `json:"endpoint" bson:"endpoint" cql:"endpoint" dynamo:"endpoint"`
|
||||
Headers string `json:"headers" bson:"headers" cql:"headers" dynamo:"headers"`
|
||||
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled" dynamo:"enabled"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
||||
}
|
||||
|
||||
// AsAPIWebhook to return webhook as graphql response object
|
||||
|
@@ -11,14 +11,14 @@ import (
|
||||
|
||||
// WebhookLog model for db
|
||||
type WebhookLog struct {
|
||||
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"`
|
||||
HttpStatus int64 `json:"http_status" bson:"http_status" cql:"http_status"`
|
||||
Response string `json:"response" bson:"response" cql:"response"`
|
||||
Request string `json:"request" bson:"request" cql:"request"`
|
||||
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"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
||||
HttpStatus int64 `json:"http_status" bson:"http_status" cql:"http_status" dynamo:"http_status"`
|
||||
Response string `json:"response" bson:"response" cql:"response" dynamo:"response"`
|
||||
Request string `json:"request" bson:"request" cql:"request" dynamo:"request"`
|
||||
WebhookID string `gorm:"type:char(36)" json:"webhook_id" bson:"webhook_id" cql:"webhook_id" dynamo:"webhook_id" index:"webhook_id,hash"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
||||
}
|
||||
|
||||
// AsAPIWebhookLog to return webhook log as graphql response object
|
||||
|
121
server/db/providers/dynamodb/email_template.go
Normal file
121
server/db/providers/dynamodb/email_template.go
Normal file
@@ -0,0 +1,121 @@
|
||||
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
|
||||
}
|
72
server/db/providers/dynamodb/env.go
Normal file
72
server/db/providers/dynamodb/env.go
Normal file
@@ -0,0 +1,72 @@
|
||||
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
|
||||
}
|
80
server/db/providers/dynamodb/otp.go
Normal file
80
server/db/providers/dynamodb/otp.go
Normal file
@@ -0,0 +1,80 @@
|
||||
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
|
||||
}
|
62
server/db/providers/dynamodb/provider.go
Normal file
62
server/db/providers/dynamodb/provider.go
Normal file
@@ -0,0 +1,62 @@
|
||||
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
|
||||
}
|
28
server/db/providers/dynamodb/session.go
Normal file
28
server/db/providers/dynamodb/session.go
Normal file
@@ -0,0 +1,28 @@
|
||||
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
|
||||
}
|
46
server/db/providers/dynamodb/shared.go
Normal file
46
server/db/providers/dynamodb/shared.go
Normal file
@@ -0,0 +1,46 @@
|
||||
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
|
||||
}
|
195
server/db/providers/dynamodb/user.go
Normal file
195
server/db/providers/dynamodb/user.go
Normal file
@@ -0,0 +1,195 @@
|
||||
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
|
||||
}
|
116
server/db/providers/dynamodb/verification_requests.go
Normal file
116
server/db/providers/dynamodb/verification_requests.go
Normal file
@@ -0,0 +1,116 @@
|
||||
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
|
||||
}
|
148
server/db/providers/dynamodb/webhook.go
Normal file
148
server/db/providers/dynamodb/webhook.go
Normal file
@@ -0,0 +1,148 @@
|
||||
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
|
||||
}
|
78
server/db/providers/dynamodb/webhook_log.go
Normal file
78
server/db/providers/dynamodb/webhook_log.go
Normal file
@@ -0,0 +1,78 @@
|
||||
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,6 +1,7 @@
|
||||
package sql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
@@ -21,6 +22,16 @@ type provider struct {
|
||||
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
|
||||
func NewProvider() (*provider, error) {
|
||||
var sqlDB *gorm.DB
|
||||
@@ -65,6 +76,32 @@ func NewProvider() (*provider, error) {
|
||||
if err != nil {
|
||||
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{
|
||||
db: sqlDB,
|
||||
}, nil
|
||||
|
24
server/env/env.go
vendored
24
server/env/env.go
vendored
@@ -77,6 +77,9 @@ func InitAllEnv() error {
|
||||
osResetPasswordURL := os.Getenv(constants.EnvKeyResetPasswordURL)
|
||||
osOrganizationName := os.Getenv(constants.EnvKeyOrganizationName)
|
||||
osOrganizationLogo := os.Getenv(constants.EnvKeyOrganizationLogo)
|
||||
osAwsRegion := os.Getenv(constants.EnvAwsRegion)
|
||||
osAwsAccessKey := os.Getenv(constants.EnvAwsAccessKeyID)
|
||||
osAwsSecretKey := os.Getenv(constants.EnvAwsSecretAccessKey)
|
||||
|
||||
// os bool vars
|
||||
osAppCookieSecure := os.Getenv(constants.EnvKeyAppCookieSecure)
|
||||
@@ -119,6 +122,27 @@ 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 == "" {
|
||||
envData[constants.EnvKeyAppURL] = osAppURL
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ go 1.16
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.17.20
|
||||
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/gin-gonic/gin v1.8.1
|
||||
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
||||
@@ -14,6 +15,7 @@ require (
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/guregu/dynamo v1.16.0
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||
|
@@ -49,10 +49,15 @@ 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/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/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/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/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/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -120,6 +125,8 @@ github.com/gocql/gocql v1.2.0 h1:TZhsCd7fRuye4VyHr3WCvWwIQaZUmjsqnSIXK9FcVCE=
|
||||
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.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/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
@@ -185,6 +192,8 @@ 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/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
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/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
@@ -247,6 +256,9 @@ 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.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI=
|
||||
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/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
@@ -483,6 +495,9 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
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/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -552,9 +567,12 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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-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.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@@ -30,7 +30,7 @@ func AppHandler() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
redirect_uri := strings.TrimSpace(c.Query("redirect_uri"))
|
||||
redirectURI := strings.TrimSpace(c.Query("redirect_uri"))
|
||||
state := strings.TrimSpace(c.Query("state"))
|
||||
scopeString := strings.TrimSpace(c.Query("scope"))
|
||||
|
||||
@@ -41,11 +41,11 @@ func AppHandler() gin.HandlerFunc {
|
||||
scope = strings.Split(scopeString, " ")
|
||||
}
|
||||
|
||||
if redirect_uri == "" {
|
||||
redirect_uri = hostname + "/app"
|
||||
if redirectURI == "" {
|
||||
redirectURI = hostname + "/app"
|
||||
} else {
|
||||
// validate redirect url with allowed origins
|
||||
if !validators.IsValidOrigin(redirect_uri) {
|
||||
if !validators.IsValidOrigin(redirectURI) {
|
||||
log.Debug("Invalid redirect_uri")
|
||||
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
||||
return
|
||||
@@ -75,7 +75,7 @@ func AppHandler() gin.HandlerFunc {
|
||||
c.HTML(http.StatusOK, "app.tmpl", gin.H{
|
||||
"data": map[string]interface{}{
|
||||
"authorizerURL": hostname,
|
||||
"redirectURL": redirect_uri,
|
||||
"redirectURL": redirectURI,
|
||||
"scope": scope,
|
||||
"state": state,
|
||||
"organizationName": orgName,
|
||||
|
@@ -17,14 +17,14 @@ func OpenIDConfigurationHandler() gin.HandlerFunc {
|
||||
c.JSON(200, gin.H{
|
||||
"issuer": issuer,
|
||||
"authorization_endpoint": issuer + "/authorize",
|
||||
"token_endpoint": issuer + "/token",
|
||||
"token_endpoint": issuer + "/oauth/token",
|
||||
"userinfo_endpoint": issuer + "/userinfo",
|
||||
"jwks_uri": issuer + "/.well-known/jwks.json",
|
||||
"response_types_supported": []string{"code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token"},
|
||||
"response_types_supported": []string{"code", "token", "id_token"},
|
||||
"scopes_supported": []string{"openid", "email", "profile", "email_verified", "given_name", "family_name", "nick_name", "picture"},
|
||||
"response_modes_supported": []string{"query", "fragment", "form_post"},
|
||||
"response_modes_supported": []string{"query", "fragment", "form_post", "web_message"},
|
||||
"id_token_signing_alg_values_supported": []string{jwtType},
|
||||
"claims_supported": []string{"aud", "exp", "iss", "iat", "sub", "given_name", "family_name", "middle_name", "nickname", "preferred_username", "picture", "email", "email_verified", "roles", "gender", "birthdate", "phone_number", "phone_number_verified"},
|
||||
"claims_supported": []string{"aud", "exp", "iss", "iat", "sub", "given_name", "family_name", "middle_name", "nickname", "preferred_username", "picture", "email", "email_verified", "roles", "role", "gender", "birthdate", "phone_number", "phone_number_verified", "nonce", "updated_at", "created_at", "revoked_timestamp", "login_method", "signup_methods", "token_type"},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -15,12 +15,15 @@ import (
|
||||
"github.com/authorizerdev/authorizer/server/routes"
|
||||
)
|
||||
|
||||
// VERSION is used to define the version of authorizer from build tags
|
||||
var VERSION string
|
||||
|
||||
// LogUTCFormatter hels in setting UTC time format for the logs
|
||||
type LogUTCFormatter struct {
|
||||
log.Formatter
|
||||
}
|
||||
|
||||
// Format helps fomratting time to UTC
|
||||
func (u LogUTCFormatter) Format(e *log.Entry) ([]byte, error) {
|
||||
e.Time = e.Time.UTC()
|
||||
return u.Formatter.Format(e)
|
||||
|
@@ -57,7 +57,7 @@ func InitMemStore() error {
|
||||
}
|
||||
|
||||
redisURL := requiredEnvs.RedisURL
|
||||
if redisURL != "" && !requiredEnvs.disableRedisForEnv {
|
||||
if redisURL != "" && !requiredEnvs.DisableRedisForEnv {
|
||||
log.Info("Initializing Redis memory store")
|
||||
Provider, err = redis.NewRedisProvider(redisURL)
|
||||
if err != nil {
|
||||
|
@@ -27,7 +27,11 @@ type RequiredEnv struct {
|
||||
DatabaseCertKey string `json:"DATABASE_CERT_KEY"`
|
||||
DatabaseCACert string `json:"DATABASE_CA_CERT"`
|
||||
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.
|
||||
@@ -53,7 +57,8 @@ func (r *RequiredEnvStore) SetRequiredEnv(requiredEnv RequiredEnv) {
|
||||
|
||||
var RequiredEnvStoreObj *RequiredEnvStore
|
||||
|
||||
// InitRequiredEnv to initialize EnvData and through error if required env are not present
|
||||
// InitRequiredEnv to initialize EnvData and throw error if required env are not present
|
||||
// This includes env that only configurable via env vars and not the ui
|
||||
func InitRequiredEnv() error {
|
||||
envPath := os.Getenv(constants.EnvKeyEnvPath)
|
||||
|
||||
@@ -85,6 +90,9 @@ func InitRequiredEnv() error {
|
||||
dbCACert := os.Getenv(constants.EnvKeyDatabaseCACert)
|
||||
redisURL := os.Getenv(constants.EnvKeyRedisURL)
|
||||
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 cli.ARG_REDIS_URL != nil && *cli.ARG_REDIS_URL != "" {
|
||||
@@ -113,7 +121,8 @@ func InitRequiredEnv() error {
|
||||
dbURL = strings.TrimSpace(*cli.ARG_DB_URL)
|
||||
}
|
||||
|
||||
if dbURL == "" && dbPort == "" && dbHost == "" && dbUsername == "" && dbPassword == "" {
|
||||
// In dynamoDB these field are not always mandatory
|
||||
if dbType != constants.DbTypeDynamoDB && dbURL == "" && dbPort == "" && dbHost == "" && dbUsername == "" && dbPassword == "" {
|
||||
log.Debug("DATABASE_URL is not set")
|
||||
return errors.New("invalid database url. DATABASE_URL is required")
|
||||
}
|
||||
@@ -138,7 +147,10 @@ func InitRequiredEnv() error {
|
||||
DatabaseCertKey: dbCertKey,
|
||||
DatabaseCACert: dbCACert,
|
||||
RedisURL: redisURL,
|
||||
disableRedisForEnv: disableRedisForEnv,
|
||||
DisableRedisForEnv: disableRedisForEnv,
|
||||
AwsRegion: awsRegion,
|
||||
AwsAccessKeyID: awsAccessKeyID,
|
||||
AwsSecretAccessKey: awsSecretAccessKey,
|
||||
}
|
||||
|
||||
RequiredEnvStoreObj = &RequiredEnvStore{
|
||||
|
@@ -2,6 +2,7 @@ package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -20,6 +21,7 @@ func TestResolvers(t *testing.T) {
|
||||
constants.DbTypeArangodb: "http://localhost:8529",
|
||||
constants.DbTypeMongodb: "mongodb://localhost:27017",
|
||||
constants.DbTypeScyllaDB: "127.0.0.1:9042",
|
||||
constants.DbTypeDynamoDB: "http://0.0.0.0:8000",
|
||||
}
|
||||
|
||||
testDBs := strings.Split(os.Getenv("TEST_DBS"), ",")
|
||||
@@ -51,6 +53,12 @@ func TestResolvers(t *testing.T) {
|
||||
os.Setenv(constants.EnvKeyDatabaseURL, dbURL)
|
||||
os.Setenv(constants.EnvKeyDatabaseType, dbType)
|
||||
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()
|
||||
|
||||
err := db.InitDB()
|
||||
@@ -60,12 +68,15 @@ func TestResolvers(t *testing.T) {
|
||||
|
||||
// clean the persisted config for test to use fresh config
|
||||
envData, err := db.Provider.GetEnv(ctx)
|
||||
if err == nil {
|
||||
fmt.Println("envData", envData.ID, envData.EnvData)
|
||||
if err == nil && envData.ID != "" {
|
||||
envData.EnvData = ""
|
||||
_, err = db.Provider.UpdateEnv(ctx, envData)
|
||||
if err != nil {
|
||||
t.Errorf("Error updating env: %s", err.Error())
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Errorf("Error getting env: %s", err.Error())
|
||||
}
|
||||
err = env.PersistEnv()
|
||||
if err != nil {
|
||||
|
@@ -84,7 +84,7 @@ func testSetup() TestSetup {
|
||||
testData := TestData{
|
||||
Email: fmt.Sprintf("%d_authorizer_tester@yopmail.com", time.Now().Unix()),
|
||||
Password: "Test@123",
|
||||
WebhookEndpoint: "https://62cbc6738042b16aa7c22df2.mockapi.io/api/v1/webhook",
|
||||
WebhookEndpoint: "https://62f93101e05644803533cf36.mockapi.io/authorizer/webhook",
|
||||
TestWebhookEventTypes: []string{constants.UserAccessEnabledWebhookEvent, constants.UserAccessRevokedWebhookEvent, constants.UserCreatedWebhookEvent, constants.UserDeletedWebhookEvent, constants.UserLoginWebhookEvent, constants.UserSignUpWebhookEvent},
|
||||
TestEmailTemplateEventTypes: []string{constants.VerificationTypeBasicAuthSignup, constants.VerificationTypeForgotPassword, constants.VerificationTypeMagicLinkLogin, constants.VerificationTypeUpdateEmail},
|
||||
}
|
||||
|
Reference in New Issue
Block a user