Compare commits

..

1 Commits

Author SHA1 Message Date
Lakhan Samani
e170569959 Add fauna db provider 2022-01-24 18:53:53 +05:30
100 changed files with 765 additions and 5642 deletions

View File

@@ -1,2 +1,16 @@
ENV=production
DATABASE_URL=data.db
DATABASE_TYPE=sqlite
DATABASE_TYPE=sqlite
ADMIN_SECRET=admin
JWT_SECRET=random_string
SENDER_EMAIL=info@authorizer.dev
SMTP_USERNAME=username
SMTP_PASSWORD=password
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
JWT_TYPE=HS256
ROLES=user
DEFAULT_ROLES=user
PROTECTED_ROLES=admin
JWT_ROLE_CLAIM=role
CUSTOM_ACCESS_TOKEN_SCRIPT=function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}

View File

@@ -24,9 +24,7 @@ FROM alpine:latest
WORKDIR /root/
RUN mkdir app dashboard
COPY --from=node-builder /authorizer/app/build app/build
COPY --from=node-builder /authorizer/app/favicon_io app/favicon_io
COPY --from=node-builder /authorizer/dashboard/build dashboard/build
COPY --from=node-builder /authorizer/dashboard/favicon_io dashboard/favicon_io
COPY --from=go-builder /authorizer/build build
COPY templates templates
EXPOSE 8080

View File

@@ -1,6 +1,6 @@
<p align="center">
<a href="https://authorizer.dev">
<img alt="Logo" src="https://authorizer.dev/images/logo.png" width="60" />
<img alt="Logo" src="https://github.com/authorizerdev/authorizer/blob/main/assets/logo.png" width="60" />
</a>
</p>
<h1 align="center">
@@ -129,13 +129,18 @@ Required environment variables are pre-configured in `.env` file. But based on t
> Note: For mac users, you might have to give binary the permission to execute. Here is the command you can use to grant permission `xattr -d com.apple.quarantine build/server`
Deploy production ready Authorizer instance using one click deployment options available below
## Install instance on Heroku
| **Infra provider** | **One-click link** | **Additional information** |
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
| Railway.app | <a href="https://railway.app/new/template?template=https://github.com/authorizerdev/authorizer-railway&amp;plugins=postgresql,redis"><img src="https://railway.app/button.svg" style="height: 44px" alt="Deploy on Railway"></a> | [docs](https://docs.authorizer.dev/deployment/railway) |
| Heroku | <a href="https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku"><img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy to Heroku" style="height: 44px;"></a> | [docs](https://docs.authorizer.dev/deployment/heroku) |
| Render | [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render) | [docs](https://docs.authorizer.dev/deployment/render) |
Deploy Authorizer using [heroku](https://github.com/authorizerdev/authorizer-heroku) and quickly play with it in 30seconds
<br/><br/>
[![Deploy to Heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku)
# Install instance on railway
Deploy production ready Authorizer instance using [railway.app](https://github.com/authorizerdev/authorizer-railway) with postgres and redis for free and build with it in 30seconds
<br/>
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2Fauthorizerdev%2Fauthorizer-railway&plugins=postgresql%2Credis&envs=ENV%2CDATABASE_TYPE%2CADMIN_SECRET%2CCOOKIE_NAME%2CJWT_ROLE_CLAIM%2CJWT_TYPE%2CJWT_SECRET%2CFACEBOOK_CLIENT_ID%2CFACEBOOK_CLIENT_SECRET%2CGOOGLE_CLIENT_ID%2CGOOGLE_CLIENT_SECRET%2CGITHUB_CLIENT_ID%2CGITHUB_CLIENT_SECRET%2CALLOWED_ORIGINS%2CROLES%2CPROTECTED_ROLES%2CDEFAULT_ROLES&optionalEnvs=FACEBOOK_CLIENT_ID%2CFACEBOOK_CLIENT_SECRET%2CGOOGLE_CLIENT_ID%2CGOOGLE_CLIENT_SECRET%2CGITHUB_CLIENT_ID%2CGITHUB_CLIENT_SECRET%2CALLOWED_ORIGINS%2CROLES%2CPROTECTED_ROLES%2CDEFAULT_ROLES&ENVDesc=Deployment+environment&DATABASE_TYPEDesc=With+railway+we+are+deploying+postgres+db&ADMIN_SECRETDesc=Secret+to+access+the+admin+apis&COOKIE_NAMEDesc=Name+of+http+only+cookie+that+will+be+used+as+session&FACEBOOK_CLIENT_IDDesc=Facebook+client+ID+for+facebook+login&FACEBOOK_CLIENT_SECRETDesc=Facebook+client+secret+for+facebook+login&GOOGLE_CLIENT_IDDesc=Google+client+ID+for+google+login&GOOGLE_CLIENT_SECRETDesc=Google+client+secret+for+google+login&GITHUB_CLIENT_IDDesc=Github+client+ID+for+github+login&GITHUB_CLIENT_SECRETDesc=Github+client+secret+for+github+login&ALLOWED_ORIGINSDesc=Whitelist+the+URL+for+which+this+instance+of+authorizer+is+allowed&ROLESDesc=Comma+separated+list+of+roles+that+platform+supports.+Default+role+is+user&PROTECTED_ROLESDesc=Comma+separated+list+of+protected+roles+for+which+sign-up+is+disabled&DEFAULT_ROLESDesc=Default+role+that+should+be+assigned+to+user.+It+should+be+one+from+the+list+of+%60ROLES%60+env.+Default+role+is+user&JWT_ROLE_CLAIMDesc=JWT+key+to+be+used+to+validate+the+role+field.&JWT_TYPEDesc=JWT+encryption+type&JWT_SECRETDesc=Random+string+that+will+be+used+for+encrypting+the+JWT+token&ENVDefault=PRODUCTION&DATABASE_TYPEDefault=postgres&COOKIE_NAMEDefault=authorizer&JWT_TYPEDefault=HS256&JWT_ROLE_CLAIMDefault=role)
### Things to consider

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -15,7 +15,7 @@ export default function Dashboard() {
return (
<div>
<h1>Hey 👋,</h1>
<p>Thank you for using authorizer.</p>
<p>Thank you for joining authorizer demo app.</p>
<p>
Your email address is{' '}
<a href={`mailto:${user?.email}`} style={{ color: '#3B82F6' }}>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -17,11 +17,9 @@
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"@types/react-router-dom": "^5.3.2",
"dayjs": "^1.10.7",
"esbuild": "^0.14.9",
"framer-motion": "^5.5.5",
"graphql": "^16.2.0",
"lodash": "^4.17.21",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-icons": "^4.3.1",

View File

@@ -12,7 +12,6 @@ const queryClient = createClient({
credentials: 'include',
};
},
requestPolicy: 'network-only',
});
const theme = extendTheme({

0
dashboard/src/Router.tsx Normal file
View File

View File

@@ -1,112 +0,0 @@
import React from 'react';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaRegTrashAlt } from 'react-icons/fa';
import { DeleteUser } from '../graphql/mutation';
import { capitalizeFirstLetter } from '../utils';
interface userDataTypes {
id: string;
email: string;
}
const DeleteUserModal = ({
user,
updateUserList,
}: {
user: userDataTypes;
updateUserList: Function;
}) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [userData, setUserData] = React.useState<userDataTypes>({
id: '',
email: '',
});
React.useEffect(() => {
setUserData(user);
}, []);
const deleteHandler = async () => {
const res = await client
.mutation(DeleteUser, { params: { email: userData.email } })
.toPromise();
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
});
return;
} else if (res.data?._delete_user) {
toast({
title: capitalizeFirstLetter(res.data?._delete_user.message),
isClosable: true,
status: 'success',
position: 'bottom-right',
});
}
onClose();
updateUserList();
};
return (
<>
<MenuItem onClick={onOpen}>Delete User</MenuItem>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Delete User</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text fontSize="md">Are you sure?</Text>
<Flex
padding="5%"
marginTop="5%"
marginBottom="2%"
border="1px solid #ff7875"
borderRadius="5px"
flexDirection="column"
>
<Text fontSize="sm">
User <b>{user.email}</b> will be deleted permanently!
</Text>
</Flex>
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaRegTrashAlt />}
colorScheme="red"
variant="solid"
onClick={deleteHandler}
isDisabled={false}
>
<Center h="100%" pt="5%">
Delete
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default DeleteUserModal;

View File

@@ -1,250 +0,0 @@
import React from 'react';
import {
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,
} from '../constants';
import { getObjectDiff } from '../utils';
import { UpdateUser } from '../graphql/mutation';
const GenderTypes = {
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] | [];
}
const EditUserModal = ({
user,
updateUserList,
}: {
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>
<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;

View File

@@ -1,338 +0,0 @@
import React from 'react';
import {
Box,
Flex,
Input,
Center,
InputGroup,
InputRightElement,
Tag,
TagLabel,
TagRightIcon,
Select,
Textarea,
Switch,
Code,
} from '@chakra-ui/react';
import {
FaRegClone,
FaRegEye,
FaRegEyeSlash,
FaPlus,
FaTimes,
} from 'react-icons/fa';
import {
ArrayInputOperations,
ArrayInputType,
SelectInputType,
HiddenInputType,
TextInputType,
TextAreaInputType,
SwitchInputType,
DateInputType,
} from '../constants';
import { copyTextToClipboard } from '../utils';
const InputField = ({
inputType,
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
...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%"
paddingTop="0.5%"
overflowX="scroll"
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)) {
if (inputType === SelectInputType.JWT_TYPE) {
return (
<Select size="sm" {...props}>
{[variables[inputType]].map((value: string) => (
<option value="value" key={value}>
{value}
</option>
))}
</Select>
);
}
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"
value={inputData[inputType]}
onChange={(e: any) => {
setInputData({ ...inputData, [inputType]: e.target.value });
}}
/>
);
}
if (Object.values(SwitchInputType).includes(inputType)) {
return (
<Flex w="25%" justifyContent="space-between">
<Code h="75%">Off</Code>
<Switch
size="md"
isChecked={variables[inputType]}
onChange={() => {
setVariables({
...variables,
[inputType]: !variables[inputType],
});
}}
/>
<Code h="75%">On</Code>
</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;

View File

@@ -1,6 +1,7 @@
import React, { ReactNode } from 'react';
import {
IconButton,
Avatar,
Box,
CloseButton,
Flex,
@@ -20,7 +21,9 @@ import {
} from '@chakra-ui/react';
import {
FiHome,
FiCode,
FiTrendingUp,
FiCompass,
FiStar,
FiSettings,
FiMenu,
FiUser,
@@ -87,17 +90,6 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
</NavItem>
</NavLink>
))}
<Link
href="/playground"
target="_blank"
style={{
textDecoration: 'none',
}}
_focus={{ _boxShadow: 'none' }}
>
<NavItem icon={FiCode}>API Playground</NavItem>
</Link>
</Box>
);
};
@@ -108,31 +100,37 @@ interface NavItemProps extends FlexProps {
}
export const NavItem = ({ icon, children, ...rest }: NavItemProps) => {
return (
<Flex
align="center"
p="3"
mx="3"
borderRadius="md"
role="group"
cursor="pointer"
_hover={{
bg: 'blue.500',
color: 'white',
}}
{...rest}
<Link
href="#"
style={{ textDecoration: 'none' }}
_focus={{ boxShadow: 'none' }}
>
{icon && (
<Icon
mr="4"
fontSize="16"
_groupHover={{
color: 'white',
}}
as={icon}
/>
)}
{children}
</Flex>
<Flex
align="center"
p="3"
mx="3"
borderRadius="md"
role="group"
cursor="pointer"
_hover={{
bg: 'blue.500',
color: 'white',
}}
{...rest}
>
{icon && (
<Icon
mr="4"
fontSize="16"
_groupHover={{
color: 'white',
}}
as={icon}
/>
)}
{children}
</Flex>
</Link>
);
};
@@ -163,7 +161,6 @@ export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
borderBottomWidth="1px"
borderBottomColor={useColorModeValue('gray.200', 'gray.700')}
justifyContent={{ base: 'space-between', md: 'flex-end' }}
zIndex={99}
{...rest}
>
<IconButton

View File

@@ -1,68 +1 @@
export const LOGO_URL =
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
export const TextInputType = {
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
FACEBOOK_CLIENT_ID: 'FACEBOOK_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 = {
GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET',
GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET',
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_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',
};
export const SelectInputType = {
JWT_TYPE: 'JWT_TYPE',
GENDER: 'gender',
};
export const TextAreaInputType = {
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
};
export const SwitchInputType = {
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',
};
export const DateInputType = {
BIRTHDATE: 'birthdate',
};
export const ArrayInputOperations = {
APPEND: 'APPEND',
REMOVE: 'REMOVE',
};
export const LOGO_URL = "https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png"

View File

@@ -32,7 +32,7 @@ export const AuthContextProvider = ({ children }: { children: any }) => {
if (fetching) {
return (
<Center h="100%">
<Center>
<Spinner />
</Center>
);

View File

@@ -21,27 +21,3 @@ export const AdminLogout = `
}
}
`;
export const UpdateEnvVariables = `
mutation updateEnvVariables($params: UpdateEnvInput!) {
_update_env(params: $params) {
message
}
}
`;
export const UpdateUser = `
mutation updateUser($params: UpdateUserInput!) {
_update_user(params: $params) {
id
}
}
`;
export const DeleteUser = `
mutation deleteUser($params: DeleteUserInput!) {
_delete_user(params: $params) {
message
}
}
`;

View File

@@ -5,69 +5,3 @@ export const AdminSessionQuery = `
}
}
`;
export const EnvVariablesQuery = `
query {
_env{
GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET,
GITHUB_CLIENT_ID,
GITHUB_CLIENT_SECRET,
FACEBOOK_CLIENT_ID,
FACEBOOK_CLIENT_SECRET,
ROLES,
DEFAULT_ROLES,
PROTECTED_ROLES,
JWT_TYPE,
JWT_SECRET,
JWT_ROLE_CLAIM,
REDIS_URL,
SMTP_HOST,
SMTP_PORT,
SMTP_USERNAME,
SMTP_PASSWORD,
SENDER_EMAIL,
ALLOWED_ORIGINS,
ORGANIZATION_NAME,
ORGANIZATION_LOGO,
ADMIN_SECRET,
DISABLE_LOGIN_PAGE,
DISABLE_MAGIC_LINK_LOGIN,
DISABLE_EMAIL_VERIFICATION,
DISABLE_BASIC_AUTHENTICATION,
CUSTOM_ACCESS_TOKEN_SCRIPT,
DATABASE_NAME,
DATABASE_TYPE,
DATABASE_URL,
}
}
`;
export const UserDetailsQuery = `
query($params: PaginatedInput) {
_users(params: $params) {
pagination {
limit
page
offset
total
}
users {
id
email
email_verified
given_name
family_name
middle_name
nickname
gender
birthdate
phone_number
picture
signup_methods
roles
created_at
}
}
}
`;

View File

@@ -1,773 +1,35 @@
import React, { useEffect } from 'react';
import {
Box,
Divider,
Flex,
Stack,
Center,
Text,
Button,
Input,
InputGroup,
InputRightElement,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import {
FaGoogle,
FaGithub,
FaFacebookF,
FaSave,
FaRegEyeSlash,
FaRegEye,
} from 'react-icons/fa';
import _ from 'lodash';
import InputField from '../components/InputField';
import { EnvVariablesQuery } from '../graphql/queries';
import {
ArrayInputType,
SelectInputType,
HiddenInputType,
TextInputType,
TextAreaInputType,
SwitchInputType,
} from '../constants';
import { UpdateEnvVariables } from '../graphql/mutation';
import { getObjectDiff, capitalizeFirstLetter } from '../utils';
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;
ROLES: [string] | [];
DEFAULT_ROLES: [string] | [];
PROTECTED_ROLES: [string] | [];
JWT_TYPE: string;
JWT_SECRET: string;
JWT_ROLE_CLAIM: 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;
DISABLE_LOGIN_PAGE: boolean;
DISABLE_MAGIC_LINK_LOGIN: boolean;
DISABLE_EMAIL_VERIFICATION: boolean;
DISABLE_BASIC_AUTHENTICATION: boolean;
OLD_ADMIN_SECRET: string;
DATABASE_NAME: string;
DATABASE_TYPE: string;
DATABASE_URL: string;
}
import { Box, Divider, Flex } from '@chakra-ui/react';
import React from 'react';
// Don't allow changing database from here as it can cause persistence issues
export default function Environment() {
const client = useClient();
const toast = useToast();
const [adminSecret, setAdminSecret] = React.useState<
Record<string, string | boolean>
>({
value: '',
disableInputField: true,
});
const [loading, setLoading] = React.useState<boolean>(true);
const [envVariables, setEnvVariables] = React.useState<envVarTypes>({
GOOGLE_CLIENT_ID: '',
GOOGLE_CLIENT_SECRET: '',
GITHUB_CLIENT_ID: '',
GITHUB_CLIENT_SECRET: '',
FACEBOOK_CLIENT_ID: '',
FACEBOOK_CLIENT_SECRET: '',
ROLES: [],
DEFAULT_ROLES: [],
PROTECTED_ROLES: [],
JWT_TYPE: '',
JWT_SECRET: '',
JWT_ROLE_CLAIM: '',
REDIS_URL: '',
SMTP_HOST: '',
SMTP_PORT: '',
SMTP_USERNAME: '',
SMTP_PASSWORD: '',
SENDER_EMAIL: '',
ALLOWED_ORIGINS: [],
ORGANIZATION_NAME: '',
ORGANIZATION_LOGO: '',
CUSTOM_ACCESS_TOKEN_SCRIPT: '',
ADMIN_SECRET: '',
DISABLE_LOGIN_PAGE: false,
DISABLE_MAGIC_LINK_LOGIN: false,
DISABLE_EMAIL_VERIFICATION: false,
DISABLE_BASIC_AUTHENTICATION: false,
OLD_ADMIN_SECRET: '',
DATABASE_NAME: '',
DATABASE_TYPE: '',
DATABASE_URL: '',
});
const [fieldVisibility, setFieldVisibility] = React.useState<
Record<string, boolean>
>({
GOOGLE_CLIENT_SECRET: false,
GITHUB_CLIENT_SECRET: false,
FACEBOOK_CLIENT_SECRET: false,
JWT_SECRET: false,
SMTP_PASSWORD: false,
ADMIN_SECRET: false,
OLD_ADMIN_SECRET: false,
});
useEffect(() => {
let isMounted = true;
async function getData() {
const {
data: { _env: envData },
} = await client.query(EnvVariablesQuery).toPromise();
if (isMounted) {
setLoading(false);
setEnvVariables({
...envData,
OLD_ADMIN_SECRET: envData.ADMIN_SECRET,
ADMIN_SECRET: '',
});
setAdminSecret({
value: '',
disableInputField: true,
});
}
}
getData();
return () => {
isMounted = false;
};
}, []);
const validateAdminSecretHandler = (event: any) => {
if (envVariables.OLD_ADMIN_SECRET === event.target.value) {
setAdminSecret({
...adminSecret,
value: event.target.value,
disableInputField: false,
});
} else {
setAdminSecret({
...adminSecret,
value: event.target.value,
disableInputField: true,
});
}
if (envVariables.ADMIN_SECRET !== '') {
setEnvVariables({ ...envVariables, ADMIN_SECRET: '' });
}
};
const saveHandler = async () => {
setLoading(true);
const {
data: { _env: envData },
} = await client.query(EnvVariablesQuery).toPromise();
const diff = getObjectDiff(envVariables, envData);
const updatedEnvVariables = diff.reduce(
(acc: any, property: string) => ({
...acc,
// @ts-ignore
[property]: envVariables[property],
}),
{}
);
if (
updatedEnvVariables[HiddenInputType.ADMIN_SECRET] === '' ||
updatedEnvVariables[HiddenInputType.OLD_ADMIN_SECRET] !==
envData.ADMIN_SECRET
) {
delete updatedEnvVariables.OLD_ADMIN_SECRET;
delete updatedEnvVariables.ADMIN_SECRET;
}
delete updatedEnvVariables.DATABASE_URL;
delete updatedEnvVariables.DATABASE_TYPE;
delete updatedEnvVariables.DATABASE_NAME;
const res = await client
.mutation(UpdateEnvVariables, { params: updatedEnvVariables })
.toPromise();
setLoading(false);
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
});
return;
}
setAdminSecret({
value: '',
disableInputField: true,
});
toast({
title: `Successfully updated ${
Object.keys(updatedEnvVariables).length
} variables`,
isClosable: true,
status: 'success',
position: 'bottom-right',
});
};
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Social Media Logins
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Center
w="50px"
marginRight="1.5%"
border="1px solid #e2e8f0"
borderRadius="5px"
>
<FaGoogle style={{ color: '#8c8c8c' }} />
</Center>
<Center w="45%" marginRight="1.5%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.GOOGLE_CLIENT_ID}
placeholder="Google Client ID"
/>
</Center>
<Center w="45%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.GOOGLE_CLIENT_SECRET}
placeholder="Google Secret"
/>
</Center>
</Flex>
<Flex>
<Center
w="50px"
marginRight="1.5%"
border="1px solid #e2e8f0"
borderRadius="5px"
>
<FaGithub style={{ color: '#8c8c8c' }} />
</Center>
<Center w="45%" marginRight="1.5%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.GITHUB_CLIENT_ID}
placeholder="Github Client ID"
/>
</Center>
<Center w="45%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.GITHUB_CLIENT_SECRET}
placeholder="Github Secret"
/>
</Center>
</Flex>
<Flex>
<Center
w="50px"
marginRight="1.5%"
border="1px solid #e2e8f0"
borderRadius="5px"
>
<FaFacebookF style={{ color: '#8c8c8c' }} />
</Center>
<Center w="45%" marginRight="1.5%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.FACEBOOK_CLIENT_ID}
placeholder="Facebook Client ID"
/>
</Center>
<Center w="45%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.FACEBOOK_CLIENT_SECRET}
placeholder="Facebook Secret"
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Roles
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Roles:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={ArrayInputType.ROLES}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Roles:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={ArrayInputType.DEFAULT_ROLES}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Protected Roles:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={ArrayInputType.PROTECTED_ROLES}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
JWT (JSON Web Tokens) Configurations
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Type:</Text>
</Flex>
<Center w="70%">
<Flex w="100%" justifyContent="space-between">
<Flex flex="2">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={SelectInputType.JWT_TYPE}
isDisabled={true}
defaultValue={SelectInputType.JWT_TYPE}
/>
</Flex>
<Flex flex="3" justifyContent="center" alignItems="center">
<Text fontSize="sm">
More JWT types will be enabled in upcoming releases.
</Text>
</Flex>
</Flex>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Secret</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.JWT_SECRET}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Role Claim:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.JWT_ROLE_CLAIM}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Session Storage
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Redis URL:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.REDIS_URL}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Email Configurations
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">SMTP Host:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.SMTP_HOST}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Port:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.SMTP_PORT}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Username:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.SMTP_USERNAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Password:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.SMTP_PASSWORD}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">From Email:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.SENDER_EMAIL}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
White Listing
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Allowed Origins:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={ArrayInputType.ALLOWED_ORIGINS}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Organization Information
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Organization Name:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.ORGANIZATION_NAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Organization Logo:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.ORGANIZATION_LOGO}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Custom Scripts
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Center w="100%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
placeholder="Add script here"
minH="25vh"
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Disable Features
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Login Page:</Text>
</Flex>
<Flex justifyContent="start" w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
/>
</Flex>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Email Verification:</Text>
</Flex>
<Flex justifyContent="start" w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
/>
</Flex>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Magic Login Link:</Text>
</Flex>
<Flex justifyContent="start" w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
/>
</Flex>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Basic Authentication:</Text>
</Flex>
<Flex justifyContent="start" w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
/>
</Flex>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Danger
</Text>
<Stack
spacing={6}
padding="0 5%"
marginTop="3%"
border="1px solid #ff7875"
borderRadius="5px"
>
<Stack spacing={6} padding="3% 0">
<Text fontStyle="italic" fontSize="sm" color="gray.600">
Note: Database related environment variables cannot be updated from
dashboard :(
</Text>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">DataBase Name:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.DATABASE_NAME}
isDisabled={true}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">DataBase Type:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.DATABASE_TYPE}
isDisabled={true}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">DataBase URL:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.DATABASE_URL}
isDisabled={true}
/>
</Center>
</Flex>
</Stack>
<Flex marginTop="3%">
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Old Admin Secret:</Text>
</Flex>
<Center w="70%">
<InputGroup size="sm">
<Input
size="sm"
placeholder="Enter Old Admin Secret"
value={adminSecret.value as string}
onChange={(event: any) => validateAdminSecretHandler(event)}
type={
!fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET]
? 'password'
: 'text'
}
/>
<InputRightElement
right="5px"
children={
<Flex>
{fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET] ? (
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() =>
setFieldVisibility({
...fieldVisibility,
[HiddenInputType.OLD_ADMIN_SECRET]: false,
})
}
>
<FaRegEyeSlash color="#bfbfbf" />
</Center>
) : (
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() =>
setFieldVisibility({
...fieldVisibility,
[HiddenInputType.OLD_ADMIN_SECRET]: true,
})
}
>
<FaRegEye color="#bfbfbf" />
</Center>
)}
</Flex>
}
/>
</InputGroup>
</Center>
</Flex>
<Flex paddingBottom="3%">
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">New Admin Secret:</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={HiddenInputType.ADMIN_SECRET}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
isDisabled={adminSecret.disableInputField}
placeholder="Enter New Admin Secret"
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="5%" marginBottom="2%" />
<Stack spacing={6} padding="1% 0">
<Flex justifyContent="end" alignItems="center">
<Button
leftIcon={<FaSave />}
colorScheme="blue"
variant="solid"
onClick={saveHandler}
isDisabled={loading}
>
Save
</Button>
</Flex>
</Stack>
<Box m="5" p="5" bg="white" rounded="md">
<h1>Social Media Logins</h1>
<Divider />- Add horizontal input for clientID and secret for - Google -
Github - Facebook
<h1>Roles</h1>
<Divider />- Add tagged input for roles, default roles, and protected
roles
<h1>JWT Configurations</h1>
<Divider />- Add input for JWT Type (keep this disabled for now with
notice saying, "More JWT types will be enabled in upcoming releases"),JWT
secret, JWT role claim
<h1>Session Storage</h1>
<Divider />- Add input for redis url
<h1>Email Configurations</h1>
<Divider />- Add input for SMTP Host, PORT, Username, Password, From
Email,
<h1>White Listing</h1>
<Divider />- Add input for allowed origins
<h1>Organization Information</h1>
<Divider />- Add input for organization name, and logo
<h1>Custom Scripts</h1>
<Divider />- For now add text area input for CUSTOM_ACCESS_TOKEN_SCRIPT
<h1>Disable Features</h1>
<Divider />
<h1>Danger</h1>
<Divider />- Include changing admin secret
</Box>
);
}

View File

@@ -1,400 +1,6 @@
import { Box } from '@chakra-ui/react';
import React from 'react';
import { useClient } from 'urql';
import dayjs from 'dayjs';
import {
Box,
Flex,
IconButton,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Select,
Table,
Tag,
Tbody,
Td,
Text,
TableCaption,
Th,
Thead,
Tooltip,
Tr,
Button,
Center,
Menu,
MenuButton,
MenuList,
MenuItem,
useToast,
Spinner,
} from '@chakra-ui/react';
import {
FaAngleLeft,
FaAngleRight,
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaExclamationCircle,
FaAngleDown,
} from 'react-icons/fa';
import { UserDetailsQuery } from '../graphql/queries';
import { UpdateUser } from '../graphql/mutation';
import EditUserModal from '../components/EditUserModal';
import DeleteUserModal from '../components/DeleteUserModal';
interface paginationPropTypes {
limit: number;
page: number;
offset: number;
total: number;
maxPages: number;
}
interface userDataTypes {
id: string;
email: string;
email_verified: boolean;
given_name: string;
family_name: string;
middle_name: string;
nickname: string;
gender: string;
birthdate: string;
phone_number: string;
picture: string;
signup_methods: string;
roles: [string];
created_at: number;
}
const getMaxPages = (pagination: paginationPropTypes) => {
const { limit, total } = pagination;
if (total > 1) {
return total % limit === 0
? total / limit
: parseInt(`${total / limit}`) + 1;
} else return 1;
};
const getLimits = (pagination: paginationPropTypes) => {
const { total } = pagination;
const limits = [5];
if (total > 10) {
for (let i = 10; i <= total && limits.length <= 10; i += 5) {
limits.push(i);
}
}
return limits;
};
export default function Users() {
const client = useClient();
const toast = useToast();
const [paginationProps, setPaginationProps] =
React.useState<paginationPropTypes>({
limit: 5,
page: 1,
offset: 0,
total: 0,
maxPages: 1,
});
const [userList, setUserList] = React.useState<userDataTypes[]>([]);
const [loading, setLoading] = React.useState<boolean>(false);
const updateUserList = async () => {
setLoading(true);
const { data } = await client
.query(UserDetailsQuery, {
params: {
pagination: {
limit: paginationProps.limit,
page: paginationProps.page,
},
},
})
.toPromise();
if (data?._users) {
const { pagination, users } = data._users;
const maxPages = getMaxPages(pagination);
if (users && users.length > 0) {
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
setUserList(users);
} else {
if (paginationProps.page !== 1) {
setPaginationProps({
...paginationProps,
...pagination,
maxPages,
page: 1,
});
}
}
}
setLoading(false);
};
React.useEffect(() => {
updateUserList();
}, []);
React.useEffect(() => {
updateUserList();
}, [paginationProps.page, paginationProps.limit]);
const paginationHandler = (value: Record<string, number>) => {
setPaginationProps({ ...paginationProps, ...value });
};
const userVerificationHandler = async (user: userDataTypes) => {
const { id, email } = user;
const res = await client
.mutation(UpdateUser, {
params: {
id,
email,
email_verified: true,
},
})
.toPromise();
if (res.error) {
toast({
title: 'User verification failed',
isClosable: true,
status: 'error',
position: 'bottom-right',
});
} else if (res.data?._update_user?.id) {
toast({
title: 'User verification successful',
isClosable: true,
status: 'success',
position: 'bottom-right',
});
}
updateUserList();
};
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
<Text fontSize="md" fontWeight="bold">
Users
</Text>
</Flex>
{!loading ? (
userList.length > 0 ? (
<Table variant="simple">
<Thead>
<Tr>
<Th>Email</Th>
<Th>Created At</Th>
<Th>Signup Methods</Th>
<Th>Roles</Th>
<Th>Verified</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{userList.map((user: userDataTypes) => {
const { email_verified, created_at, ...rest }: any = user;
return (
<Tr key={user.id} style={{ fontSize: 14 }}>
<Td>{user.email}</Td>
<Td>
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
</Td>
<Td>{user.signup_methods}</Td>
<Td>{user.roles.join(', ')}</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={user.email_verified ? 'green' : 'yellow'}
>
{user.email_verified.toString()}
</Tag>
</Td>
<Td>
<Menu>
<MenuButton as={Button} variant="unstyled" size="sm">
<Flex
justifyContent="space-between"
alignItems="center"
>
<Text fontSize="sm" fontWeight="light">
Menu
</Text>
<FaAngleDown style={{ marginLeft: 10 }} />
</Flex>
</MenuButton>
<MenuList>
{!user.email_verified && (
<MenuItem
onClick={() => userVerificationHandler(user)}
>
Verify User
</MenuItem>
)}
<EditUserModal
user={rest}
updateUserList={updateUserList}
/>
<DeleteUserModal
user={rest}
updateUserList={updateUserList}
/>
</MenuList>
</Menu>
</Td>
</Tr>
);
})}
</Tbody>
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{getLimits(paginationProps).map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
) : (
<Flex
flexDirection="column"
minH="25vh"
justifyContent="center"
alignItems="center"
>
<Center w="50px" marginRight="1.5%">
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
</Center>
<Text
fontSize="2xl"
paddingRight="1%"
fontWeight="bold"
color="#d9d9d9"
>
No Data
</Text>
</Flex>
)
) : (
<Center minH="25vh">
<Spinner />
</Center>
)}
</Box>
);
return <Box>Welcome to Users Page</Box>;
}

View File

@@ -26,7 +26,6 @@ export const AppRoutes = () => {
<Route path="/" element={<Home />} />
<Route path="users" element={<Users />} />
<Route path="environment" element={<Environment />} />
<Route path="*" element={<Home />} />
</Route>
</Routes>
</Suspense>
@@ -36,7 +35,6 @@ export const AppRoutes = () => {
<Suspense fallback={<></>}>
<Routes>
<Route path="/" element={<Auth />} />
<Route path="*" element={<Auth />} />
</Routes>
</Suspense>
);

View File

@@ -1,66 +1,6 @@
import _ from 'lodash';
export const hasAdminSecret = () => {
return (<any>window)['__authorizer__'].isOnboardingCompleted === true;
};
export const capitalizeFirstLetter = (data: string): string =>
data.charAt(0).toUpperCase() + data.slice(1);
const fallbackCopyTextToClipboard = (text: string) => {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.top = '0';
textArea.style.left = '0';
textArea.style.position = 'fixed';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand('copy');
const msg = successful ? 'successful' : 'unsuccessful';
console.log('Fallback: Copying text command was ' + msg);
} catch (err) {
console.error('Fallback: Oops, unable to copy', err);
}
document.body.removeChild(textArea);
};
export const copyTextToClipboard = (text: string) => {
if (!navigator.clipboard) {
fallbackCopyTextToClipboard(text);
return;
}
navigator.clipboard.writeText(text).then(
() => {
console.log('Async: Copying to clipboard was successful!');
},
(err) => {
console.error('Async: Could not copy text: ', err);
}
);
};
export const getObjectDiff = (obj1: any, obj2: any) => {
const diff = Object.keys(obj1).reduce((result, key) => {
if (!obj2.hasOwnProperty(key)) {
result.push(key);
} else if (
_.isEqual(obj1[key], obj2[key]) ||
(obj1[key] === null && obj2[key] === '') ||
(obj1[key] &&
Array.isArray(obj1[key]) &&
obj1[key].length === 0 &&
obj2[key] === null)
) {
const resultKeyIndex = result.indexOf(key);
result.splice(resultKeyIndex, 1);
}
return result;
}, Object.keys(obj2));
return diff;
};

View File

@@ -67,7 +67,6 @@
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
"lib": ["esnext", "dom"]
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}

View File

@@ -13,4 +13,6 @@ const (
DbTypeArangodb = "arangodb"
// DbTypeMongodb is the mongodb database type
DbTypeMongodb = "mongodb"
// DbTypeFaunadb is the faunadb database type
DbTypeFaunadb = "faunadb"
)

View File

@@ -16,7 +16,6 @@ const (
// EnvKeyVersion key for build arg version
EnvKeyVersion = "VERSION"
// EnvKeyAuthorizerURL key for env variable AUTHORIZER_URL
// TODO: remove support AUTHORIZER_URL env
EnvKeyAuthorizerURL = "AUTHORIZER_URL"
// EnvKeyPort key for env variable PORT
EnvKeyPort = "PORT"

View File

@@ -1,4 +0,0 @@
package constants
// DefaultLimit is the default limit for pagination
var DefaultLimit = 10

View File

@@ -13,8 +13,7 @@ import (
func SetAdminCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
hostname := utils.GetHost(gc)
host, _ := utils.GetHostParts(hostname)
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), token, 3600, "/", host, secure, httpOnly)
}
@@ -39,8 +38,7 @@ func GetAdminCookie(gc *gin.Context) (string, error) {
func DeleteAdminCookie(gc *gin.Context) {
secure := true
httpOnly := true
hostname := utils.GetHost(gc)
host, _ := utils.GetHostParts(hostname)
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), "", -1, "/", host, secure, httpOnly)
}

View File

@@ -19,9 +19,8 @@ import (
func SetCookie(gc *gin.Context, accessToken, refreshToken, fingerprintHash string) {
secure := true
httpOnly := true
hostname := utils.GetHost(gc)
host, _ := utils.GetHostParts(hostname)
domain := utils.GetDomainName(hostname)
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
domain := utils.GetDomainName(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
if domain != "localhost" {
domain = "." + domain
}
@@ -87,9 +86,9 @@ func GetFingerPrintCookie(gc *gin.Context) (string, error) {
func DeleteCookie(gc *gin.Context) {
secure := true
httpOnly := true
hostname := utils.GetHost(gc)
host, _ := utils.GetHostParts(hostname)
domain := utils.GetDomainName(hostname)
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
domain := utils.GetDomainName(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
if domain != "localhost" {
domain = "." + domain
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/providers"
"github.com/authorizerdev/authorizer/server/db/providers/arangodb"
"github.com/authorizerdev/authorizer/server/db/providers/faunadb"
"github.com/authorizerdev/authorizer/server/db/providers/mongodb"
"github.com/authorizerdev/authorizer/server/db/providers/sql"
"github.com/authorizerdev/authorizer/server/envstore"
@@ -17,9 +18,10 @@ var Provider providers.Provider
func InitDB() {
var err error
isSQL := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeArangodb && envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeMongodb
isSQL := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeArangodb && envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeMongodb && envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeFaunadb
isArangoDB := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeArangodb
isMongoDB := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeMongodb
isFaunaDB := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeFaunadb
if isSQL {
Provider, err = sql.NewProvider()
@@ -41,4 +43,11 @@ func InitDB() {
log.Fatal("=> error setting arangodb provider:", err)
}
}
if isFaunaDB {
Provider, err = faunadb.NewProvider()
if err != nil {
log.Fatal("=> error setting arangodb provider:", err)
}
}
}

View File

@@ -4,8 +4,8 @@ package models
type Env struct {
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
EnvData string `gorm:"type:text" json:"env" bson:"env"`
Hash string `gorm:"type:text" json:"hash" bson:"hash"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at"`
EnvData []byte `gorm:"type:text" json:"env" bson:"env"`
Hash string `gorm:"type:hash" json:"hash" bson:"hash"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
}

View File

@@ -5,9 +5,9 @@ type Session struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
UserID string `gorm:"type:char(36),index:" json:"user_id" bson:"user_id"`
User User `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" bson:"-"`
User User `json:"-" bson:"-"`
UserAgent string `json:"user_agent" bson:"user_agent"`
IP string `json:"ip" bson:"ip"`
CreatedAt int64 `json:"created_at" bson:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
}

View File

@@ -25,8 +25,8 @@ type User struct {
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at"`
Picture *string `gorm:"type:text" json:"picture" bson:"picture"`
Roles string `json:"roles" bson:"roles"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
}
func (user *User) AsAPIUser() *model.User {

View File

@@ -1,7 +1,5 @@
package models
import "github.com/authorizerdev/authorizer/server/graph/model"
// VerificationRequest model for db
type VerificationRequest struct {
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
@@ -9,19 +7,7 @@ type VerificationRequest struct {
Token string `gorm:"type:text" json:"token" bson:"token"`
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
CreatedAt int64 `json:"created_at" bson:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
}
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
return &model.VerificationRequest{
ID: v.ID,
Token: &v.Token,
Identifier: &v.Identifier,
Expires: &v.ExpiresAt,
CreatedAt: &v.CreatedAt,
UpdatedAt: &v.UpdatedAt,
Email: &v.Email,
}
}

View File

@@ -1,7 +1,6 @@
package arangodb
import (
"context"
"fmt"
"log"
"strings"
@@ -12,7 +11,6 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/google/uuid"
)
@@ -68,40 +66,32 @@ func (p *provider) DeleteUser(user models.User) error {
}
// ListUsers to get list of users from database
func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error) {
var users []*model.User
ctx := driver.WithQueryFullCount(context.Background())
func (p *provider) ListUsers() ([]models.User, error) {
var users []models.User
query := fmt.Sprintf("FOR d in %s RETURN d", models.Collections.User)
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.User, pagination.Offset, pagination.Limit)
cursor, err := p.db.Query(ctx, query, nil)
cursor, err := p.db.Query(nil, query, nil)
if err != nil {
return nil, err
return users, err
}
defer cursor.Close()
paginationClone := pagination
paginationClone.Total = cursor.Statistics().FullCount()
for {
var user models.User
meta, err := cursor.ReadDocument(nil, &user)
if arangoDriver.IsNoMoreDocuments(err) {
if driver.IsNoMoreDocuments(err) {
break
} else if err != nil {
return nil, err
return users, err
}
if meta.Key != "" {
users = append(users, user.AsAPIUser())
users = append(users, user)
}
}
return &model.Users{
Pagination: &paginationClone,
Users: users,
}, nil
return users, nil
}
// GetUserByEmail to get user information from database using email address

View File

@@ -1,14 +1,12 @@
package arangodb
import (
"context"
"fmt"
"log"
"time"
"github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/google/uuid"
)
@@ -95,20 +93,17 @@ func (p *provider) GetVerificationRequestByEmail(email string, identifier string
}
// ListVerificationRequests to get list of verification requests from database
func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model.VerificationRequests, error) {
var verificationRequests []*model.VerificationRequest
ctx := driver.WithQueryFullCount(context.Background())
query := fmt.Sprintf("FOR d in %s SORT d.created_at DESC LIMIT %d, %d RETURN d", models.Collections.VerificationRequest, pagination.Offset, pagination.Limit)
func (p *provider) ListVerificationRequests() ([]models.VerificationRequest, error) {
var verificationRequests []models.VerificationRequest
cursor, err := p.db.Query(ctx, query, nil)
query := fmt.Sprintf("FOR d in %s RETURN d", models.Collections.VerificationRequest)
cursor, err := p.db.Query(nil, query, nil)
if err != nil {
return nil, err
return verificationRequests, err
}
defer cursor.Close()
paginationClone := pagination
paginationClone.Total = cursor.Statistics().FullCount()
for {
var verificationRequest models.VerificationRequest
meta, err := cursor.ReadDocument(nil, &verificationRequest)
@@ -116,19 +111,16 @@ func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model
if driver.IsNoMoreDocuments(err) {
break
} else if err != nil {
return nil, err
return verificationRequests, err
}
if meta.Key != "" {
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())
verificationRequests = append(verificationRequests, verificationRequest)
}
}
return &model.VerificationRequests{
VerificationRequests: verificationRequests,
Pagination: &paginationClone,
}, nil
return verificationRequests, nil
}
// DeleteVerificationRequest to delete verification request from database

View File

@@ -0,0 +1,51 @@
package faunadb
import (
"log"
"time"
f "github.com/fauna/faunadb-go/v5/faunadb"
"github.com/google/uuid"
"github.com/authorizerdev/authorizer/server/db/models"
)
// AddEnv to save environment information in database
func (p *provider) AddEnv(env models.Env) (models.Env, error) {
if env.ID == "" {
env.ID = uuid.New().String()
env.Key = env.ID
}
env.CreatedAt = time.Now().Unix()
env.UpdatedAt = time.Now().Unix()
_, err := p.db.Query(
f.Create(
f.Collection(models.Collections.Env),
f.Obj{
"data": env,
},
),
)
if err != nil {
log.Println("error adding env:", err)
return env, err
}
return env, nil
}
// UpdateEnv to update environment information in database
func (p *provider) UpdateEnv(env models.Env) (models.Env, error) {
env.UpdatedAt = time.Now().Unix()
return env, nil
}
// GetEnv to get environment information from database
func (p *provider) GetEnv() (models.Env, error) {
var env models.Env
return env, nil
}

View File

@@ -0,0 +1,164 @@
package faunadb
import (
"errors"
"log"
"strings"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/envstore"
f "github.com/fauna/faunadb-go/v5/faunadb"
)
type provider struct {
db *f.FaunaClient
}
// NewProvider returns a new faunadb provider
func NewProvider() (*provider, error) {
secret := ""
dbURL := "https://db.fauna.com"
// secret,url is stored in DATABASE_URL
dbURLSplit := strings.Split(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL), ":")
secret = dbURLSplit[0]
if len(dbURLSplit) > 1 {
dbURL = dbURLSplit[1]
}
client := f.NewFaunaClient(secret, f.Endpoint(dbURL))
if client == nil {
return nil, errors.New("failed to create faunadb client")
}
_, err := client.Query(
f.CreateCollection(f.Obj{"name": models.Collections.Env}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateIndex(
f.Obj{
"name": "env_id",
"source": f.Collection(models.Collections.Env),
"values": "_id",
"unique": true,
}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateIndex(
f.Obj{
"name": "env_key",
"source": f.Collection(models.Collections.Env),
"values": "_key",
"unique": true,
}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateCollection(f.Obj{"name": models.Collections.User}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateIndex(
f.Obj{
"name": "_id",
"source": f.Collection(models.Collections.User),
"unique": true,
}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateIndex(
f.Obj{
"name": "_key",
"source": f.Collection(models.Collections.User),
"unique": true,
}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateIndex(
f.Obj{
"name": "email",
"source": f.Collection(models.Collections.User),
"unique": true,
}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateCollection(f.Obj{"name": models.Collections.Session}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateIndex(
f.Obj{
"name": "_id",
"source": f.Collection(models.Collections.Session),
"unique": true,
}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateIndex(
f.Obj{
"name": "_key",
"source": f.Collection(models.Collections.Session),
"unique": true,
}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateCollection(f.Obj{"name": models.Collections.VerificationRequest}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateIndex(
f.Obj{
"name": "_id",
"source": f.Collection(models.Collections.VerificationRequest),
"unique": true,
}))
if err != nil {
log.Println("error:", err)
}
_, err = client.Query(
f.CreateIndex(
f.Obj{
"name": "_key",
"source": f.Collection(models.Collections.VerificationRequest),
"unique": true,
}))
if err != nil {
log.Println("error:", err)
}
return &provider{
db: client,
}, nil
}

View File

@@ -0,0 +1,25 @@
package faunadb
import (
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/google/uuid"
)
// AddSession to save session information in database
func (p *provider) AddSession(session models.Session) error {
if session.ID == "" {
session.ID = uuid.New().String()
}
session.CreatedAt = time.Now().Unix()
session.UpdatedAt = time.Now().Unix()
return nil
}
// DeleteSession to delete session information from database
func (p *provider) DeleteSession(userId string) error {
return nil
}

View File

@@ -0,0 +1,60 @@
package faunadb
import (
"strings"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/google/uuid"
)
// AddUser to save user information in database
func (p *provider) AddUser(user models.User) (models.User, error) {
if user.ID == "" {
user.ID = uuid.New().String()
}
if user.Roles == "" {
user.Roles = strings.Join(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
}
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
return user, nil
}
// UpdateUser to update user information in database
func (p *provider) UpdateUser(user models.User) (models.User, error) {
user.UpdatedAt = time.Now().Unix()
return user, nil
}
// DeleteUser to delete user information from database
func (p *provider) DeleteUser(user models.User) error {
return nil
}
// ListUsers to get list of users from database
func (p *provider) ListUsers() ([]models.User, error) {
var users []models.User
return users, nil
}
// GetUserByEmail to get user information from database using email address
func (p *provider) GetUserByEmail(email string) (models.User, error) {
var user models.User
return user, nil
}
// GetUserByID to get user information from database using user ID
func (p *provider) GetUserByID(id string) (models.User, error) {
var user models.User
return user, nil
}

View File

@@ -0,0 +1,46 @@
package faunadb
import (
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/google/uuid"
)
// AddVerification to save verification request in database
func (p *provider) AddVerificationRequest(verificationRequest models.VerificationRequest) (models.VerificationRequest, error) {
if verificationRequest.ID == "" {
verificationRequest.ID = uuid.New().String()
}
verificationRequest.CreatedAt = time.Now().Unix()
verificationRequest.UpdatedAt = time.Now().Unix()
return verificationRequest, nil
}
// GetVerificationRequestByToken to get verification request from database using token
func (p *provider) GetVerificationRequestByToken(token string) (models.VerificationRequest, error) {
var verificationRequest models.VerificationRequest
return verificationRequest, nil
}
// GetVerificationRequestByEmail to get verification request by email from database
func (p *provider) GetVerificationRequestByEmail(email string, identifier string) (models.VerificationRequest, error) {
var verificationRequest models.VerificationRequest
return verificationRequest, nil
}
// ListVerificationRequests to get list of verification requests from database
func (p *provider) ListVerificationRequests() ([]models.VerificationRequest, error) {
var verificationRequests []models.VerificationRequest
return verificationRequests, nil
}
// DeleteVerificationRequest to delete verification request from database
func (p *provider) DeleteVerificationRequest(verificationRequest models.VerificationRequest) error {
return nil
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -61,29 +60,13 @@ func (p *provider) DeleteUser(user models.User) error {
}
// ListUsers to get list of users from database
func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error) {
var users []*model.User
opts := options.Find()
opts.SetLimit(pagination.Limit)
opts.SetSkip(pagination.Offset)
opts.SetSort(bson.M{"created_at": -1})
paginationClone := pagination
// TODO add pagination total
func (p *provider) ListUsers() ([]models.User, error) {
var users []models.User
userCollection := p.db.Collection(models.Collections.User, options.Collection())
count, err := userCollection.CountDocuments(nil, bson.M{}, options.Count())
if err != nil {
log.Println("error getting total users:", err)
return nil, err
}
paginationClone.Total = count
cursor, err := userCollection.Find(nil, bson.M{}, opts)
cursor, err := userCollection.Find(nil, bson.M{}, options.Find())
if err != nil {
log.Println("error getting users:", err)
return nil, err
return users, err
}
defer cursor.Close(nil)
@@ -91,15 +74,12 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
var user models.User
err := cursor.Decode(&user)
if err != nil {
return nil, err
return users, err
}
users = append(users, user.AsAPIUser())
users = append(users, user)
}
return &model.Users{
Pagination: &paginationClone,
Users: users,
}, nil
return users, nil
}
// GetUserByEmail to get user information from database using email address

View File

@@ -5,7 +5,6 @@ import (
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -57,24 +56,13 @@ func (p *provider) GetVerificationRequestByEmail(email string, identifier string
}
// ListVerificationRequests to get list of verification requests from database
func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model.VerificationRequests, error) {
var verificationRequests []*model.VerificationRequest
opts := options.Find()
opts.SetLimit(pagination.Limit)
opts.SetSkip(pagination.Offset)
opts.SetSort(bson.M{"created_at": -1})
func (p *provider) ListVerificationRequests() ([]models.VerificationRequest, error) {
var verificationRequests []models.VerificationRequest
verificationRequestCollection := p.db.Collection(models.Collections.VerificationRequest, options.Collection())
verificationRequestCollectionCount, err := verificationRequestCollection.CountDocuments(nil, bson.M{})
paginationClone := pagination
paginationClone.Total = verificationRequestCollectionCount
cursor, err := verificationRequestCollection.Find(nil, bson.M{}, opts)
cursor, err := verificationRequestCollection.Find(nil, bson.M{}, options.Find())
if err != nil {
log.Println("error getting verification requests:", err)
return nil, err
return verificationRequests, err
}
defer cursor.Close(nil)
@@ -82,15 +70,12 @@ func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model
var verificationRequest models.VerificationRequest
err := cursor.Decode(&verificationRequest)
if err != nil {
return nil, err
return verificationRequests, err
}
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())
verificationRequests = append(verificationRequests, verificationRequest)
}
return &model.VerificationRequests{
VerificationRequests: verificationRequests,
Pagination: &paginationClone,
}, nil
return verificationRequests, nil
}
// DeleteVerificationRequest to delete verification request from database

View File

@@ -1,9 +1,6 @@
package providers
import (
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
)
import "github.com/authorizerdev/authorizer/server/db/models"
type Provider interface {
// AddUser to save user information in database
@@ -13,7 +10,7 @@ type Provider interface {
// DeleteUser to delete user information from database
DeleteUser(user models.User) error
// ListUsers to get list of users from database
ListUsers(pagination model.Pagination) (*model.Users, error)
ListUsers() ([]models.User, error)
// GetUserByEmail to get user information from database using email address
GetUserByEmail(email string) (models.User, error)
// GetUserByID to get user information from database using user ID
@@ -26,7 +23,7 @@ type Provider interface {
// GetVerificationRequestByEmail to get verification request by email from database
GetVerificationRequestByEmail(email string, identifier string) (models.VerificationRequest, error)
// ListVerificationRequests to get list of verification requests from database
ListVerificationRequests(pagination model.Pagination) (*model.VerificationRequests, error)
ListVerificationRequests() ([]models.VerificationRequest, error)
// DeleteVerificationRequest to delete verification request from database
DeleteVerificationRequest(verificationRequest models.VerificationRequest) error

View File

@@ -15,10 +15,8 @@ func (p *provider) AddEnv(env models.Env) (models.Env, error) {
}
env.Key = env.ID
env.CreatedAt = time.Now().Unix()
env.UpdatedAt = time.Now().Unix()
result := p.db.Create(&env)
if result.Error != nil {
log.Println("error adding config:", result.Error)
return env, result.Error

View File

@@ -2,7 +2,6 @@ package sql
import (
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/google/uuid"
@@ -16,8 +15,6 @@ func (p *provider) AddSession(session models.Session) error {
}
session.Key = session.ID
session.CreatedAt = time.Now().Unix()
session.UpdatedAt = time.Now().Unix()
res := p.db.Clauses(
clause.OnConflict{
DoNothing: true,

View File

@@ -8,7 +8,6 @@ import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/google/uuid"
"gorm.io/gorm/clause"
)
@@ -23,8 +22,6 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
user.Roles = strings.Join(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
}
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
user.Key = user.ID
result := p.db.Clauses(
clause.OnConflict{
@@ -67,32 +64,15 @@ func (p *provider) DeleteUser(user models.User) error {
}
// ListUsers to get list of users from database
func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error) {
func (p *provider) ListUsers() ([]models.User, error) {
var users []models.User
result := p.db.Limit(int(pagination.Limit)).Offset(int(pagination.Offset)).Order("created_at DESC").Find(&users)
result := p.db.Find(&users)
if result.Error != nil {
log.Println("error getting users:", result.Error)
return nil, result.Error
return users, result.Error
}
responseUsers := []*model.User{}
for _, user := range users {
responseUsers = append(responseUsers, user.AsAPIUser())
}
var total int64
totalRes := p.db.Model(&models.User{}).Count(&total)
if totalRes.Error != nil {
return nil, totalRes.Error
}
paginationClone := pagination
paginationClone.Total = total
return &model.Users{
Pagination: &paginationClone,
Users: responseUsers,
}, nil
return users, nil
}
// GetUserByEmail to get user information from database using email address

View File

@@ -2,10 +2,8 @@ package sql
import (
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/google/uuid"
"gorm.io/gorm/clause"
)
@@ -17,8 +15,6 @@ func (p *provider) AddVerificationRequest(verificationRequest models.Verificatio
}
verificationRequest.Key = verificationRequest.ID
verificationRequest.CreatedAt = time.Now().Unix()
verificationRequest.UpdatedAt = time.Now().Unix()
result := p.db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "email"}, {Name: "identifier"}},
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at"}),
@@ -60,33 +56,15 @@ func (p *provider) GetVerificationRequestByEmail(email string, identifier string
}
// ListVerificationRequests to get list of verification requests from database
func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model.VerificationRequests, error) {
func (p *provider) ListVerificationRequests() ([]models.VerificationRequest, error) {
var verificationRequests []models.VerificationRequest
result := p.db.Limit(int(pagination.Limit)).Offset(int(pagination.Offset)).Order("created_at DESC").Find(&verificationRequests)
result := p.db.Find(&verificationRequests)
if result.Error != nil {
log.Println("error getting verification requests:", result.Error)
return nil, result.Error
return verificationRequests, result.Error
}
responseVerificationRequests := []*model.VerificationRequest{}
for _, v := range verificationRequests {
responseVerificationRequests = append(responseVerificationRequests, v.AsAPIVerificationRequest())
}
var total int64
totalRes := p.db.Model(&models.VerificationRequest{}).Count(&total)
if totalRes.Error != nil {
return nil, totalRes.Error
}
paginationClone := pagination
paginationClone.Total = total
return &model.VerificationRequests{
VerificationRequests: responseVerificationRequests,
Pagination: &paginationClone,
}, nil
return verificationRequests, nil
}
// DeleteVerificationRequest to delete verification request from database

View File

@@ -6,10 +6,10 @@ import (
)
// SendForgotPasswordMail to send forgot password email
func SendForgotPasswordMail(toEmail, token, hostname string) error {
func SendForgotPasswordMail(toEmail, token, host string) error {
resetPasswordUrl := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
if resetPasswordUrl == "" {
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyResetPasswordURL, hostname+"/app/reset-password")
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyResetPasswordURL, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL)+"/app/reset-password")
}
// The receiver needs to be in slice as the receive supports multiple receiver

View File

@@ -6,7 +6,7 @@ import (
)
// SendVerificationMail to send verification email
func SendVerificationMail(toEmail, token, hostname string) error {
func SendVerificationMail(toEmail, token string) error {
// The receiver needs to be in slice as the receive supports multiple receiver
Receiver := []string{toEmail}
@@ -99,7 +99,7 @@ func SendVerificationMail(toEmail, token, hostname string) error {
data := make(map[string]interface{}, 3)
data["org_logo"] = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
data["org_name"] = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
data["verification_url"] = hostname + "/verify_email?token=" + token
data["verification_url"] = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/verify_email?token=" + token
message = addEmailTemplate(message, data, "verify_email.tmpl")
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)

9
server/env/env.go vendored
View File

@@ -31,6 +31,8 @@ func InitEnv() {
}
}
// set authorizer url to empty string so that fresh url is obtained with every server start
envData.StringEnv[constants.EnvKeyAuthorizerURL] = ""
if envData.StringEnv[constants.EnvKeyAppURL] == "" {
envData.StringEnv[constants.EnvKeyAppURL] = os.Getenv(constants.EnvKeyAppURL)
}
@@ -244,9 +246,10 @@ func InitEnv() {
trimVal := strings.TrimSpace(val)
if trimVal != "" {
roles = append(roles, trimVal)
if utils.StringSliceContains(defaultRoleSplit, trimVal) {
defaultRoles = append(defaultRoles, trimVal)
}
}
if utils.StringSliceContains(defaultRoleSplit, trimVal) {
defaultRoles = append(defaultRoles, trimVal)
}
}

View File

@@ -25,19 +25,15 @@ func PersistEnv() error {
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, hash)
encodedHash := utils.EncryptB64(hash)
encryptedConfig, err := utils.EncryptEnvData(envstore.EnvInMemoryStoreObj.GetEnvStoreClone())
configData, err := json.Marshal(envstore.EnvInMemoryStoreObj.GetEnvStoreClone())
if err != nil {
return err
}
// configData, err := json.Marshal()
// if err != nil {
// return err
// }
// encryptedConfig, err := utils.EncryptAES(configData)
// if err != nil {
// return err
// }
encryptedConfig, err := utils.EncryptAES(configData)
if err != nil {
return err
}
env = models.Env{
Hash: encodedHash,
@@ -55,12 +51,7 @@ func PersistEnv() error {
}
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
b64DecryptedConfig, err := utils.DecryptB64(env.EnvData)
if err != nil {
return err
}
decryptedConfigs, err := utils.DecryptAES([]byte(b64DecryptedConfig))
decryptedConfigs, err := utils.DecryptAES(env.EnvData)
if err != nil {
return err
}

View File

@@ -35,7 +35,7 @@ var EnvInMemoryStoreObj = &EnvInMemoryStore{
constants.EnvKeyAdminCookieName: "authorizer-admin",
constants.EnvKeyJwtRoleClaim: "role",
constants.EnvKeyOrganizationName: "Authorizer",
constants.EnvKeyOrganizationLogo: "https://www.authorizer.dev/images/logo.png",
constants.EnvKeyOrganizationLogo: "https://www.authorizer.io/images/logo.png",
},
BoolEnv: map[string]bool{
constants.EnvKeyDisableBasicAuthentication: false,

View File

@@ -6,6 +6,8 @@ require (
github.com/99designs/gqlgen v0.14.0
github.com/arangodb/go-driver v1.2.1
github.com/coreos/go-oidc/v3 v3.1.0
github.com/fauna/faunadb-go/v5 v5.0.0-beta // indirect
github.com/gin-contrib/location v0.0.2
github.com/gin-gonic/gin v1.7.2
github.com/go-playground/validator/v10 v10.8.0 // indirect
github.com/go-redis/redis/v8 v8.11.0

View File

@@ -79,11 +79,16 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fauna/faunadb-go/v5 v5.0.0-beta h1:qjig7OPEsDPH/DJuHWIrOboreYd5aLQnSCzLgHDpnck=
github.com/fauna/faunadb-go/v5 v5.0.0-beta/go.mod h1:eoEA8JUERBnzK5/8Rxnetzx326ImTZ8c++wi2GQwrEU=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gin-contrib/location v0.0.2 h1:QZKh1+K/LLR4KG/61eIO3b7MLuKi8tytQhV6texLgP4=
github.com/gin-contrib/location v0.0.2/go.mod h1:NGoidiRlf0BlA/VKSVp+g3cuSMeTmip/63PhEjRhUAc=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -97,6 +102,7 @@ github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8c
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.8.0 h1:1kAa0fCrnpv+QYdkdcRzrRM7AyYs5o8+jZdJCz9xj6k=
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
@@ -452,6 +458,7 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=

File diff suppressed because it is too large Load Diff

View File

@@ -35,6 +35,7 @@ type Env struct {
JwtType *string `json:"JWT_TYPE"`
JwtSecret *string `json:"JWT_SECRET"`
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
AuthorizerURL *string `json:"AUTHORIZER_URL"`
AppURL *string `json:"APP_URL"`
RedisURL *string `json:"REDIS_URL"`
CookieName *string `json:"COOKIE_NAME"`
@@ -92,22 +93,6 @@ type Meta struct {
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
}
type PaginatedInput struct {
Pagination *PaginationInput `json:"pagination"`
}
type Pagination struct {
Limit int64 `json:"limit"`
Page int64 `json:"page"`
Offset int64 `json:"offset"`
Total int64 `json:"total"`
}
type PaginationInput struct {
Limit *int64 `json:"limit"`
Page *int64 `json:"page"`
}
type ResendVerifyEmailInput struct {
Email string `json:"email"`
Identifier string `json:"identifier"`
@@ -148,12 +133,12 @@ type UpdateEnvInput struct {
OldAdminSecret *string `json:"OLD_ADMIN_SECRET"`
SMTPHost *string `json:"SMTP_HOST"`
SMTPPort *string `json:"SMTP_PORT"`
SMTPUsername *string `json:"SMTP_USERNAME"`
SMTPPassword *string `json:"SMTP_PASSWORD"`
SenderEmail *string `json:"SENDER_EMAIL"`
SenderPassword *string `json:"SENDER_PASSWORD"`
JwtType *string `json:"JWT_TYPE"`
JwtSecret *string `json:"JWT_SECRET"`
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
AuthorizerURL *string `json:"AUTHORIZER_URL"`
AppURL *string `json:"APP_URL"`
RedisURL *string `json:"REDIS_URL"`
CookieName *string `json:"COOKIE_NAME"`
@@ -226,11 +211,6 @@ type User struct {
UpdatedAt *int64 `json:"updated_at"`
}
type Users struct {
Pagination *Pagination `json:"pagination"`
Users []*User `json:"users"`
}
type ValidJWTResponse struct {
Valid bool `json:"valid"`
Message string `json:"message"`
@@ -246,11 +226,6 @@ type VerificationRequest struct {
UpdatedAt *int64 `json:"updated_at"`
}
type VerificationRequests struct {
Pagination *Pagination `json:"pagination"`
VerificationRequests []*VerificationRequest `json:"verification_requests"`
}
type VerifyEmailInput struct {
Token string `json:"token"`
}

View File

@@ -5,13 +5,6 @@ scalar Int64
scalar Map
scalar Any
type Pagination {
limit: Int64!
page: Int64!
offset: Int64!
total: Int64!
}
type Meta {
version: String!
is_google_login_enabled: Boolean!
@@ -43,11 +36,6 @@ type User {
updated_at: Int64
}
type Users {
pagination: Pagination!
users: [User!]!
}
type VerificationRequest {
id: ID!
identifier: String
@@ -58,11 +46,6 @@ type VerificationRequest {
updated_at: Int64
}
type VerificationRequests {
pagination: Pagination!
verification_requests: [VerificationRequest!]!
}
type Error {
message: String!
reason: String!
@@ -98,6 +81,7 @@ type Env {
JWT_TYPE: String
JWT_SECRET: String
ALLOWED_ORIGINS: [String!]
AUTHORIZER_URL: String
APP_URL: String
REDIS_URL: String
COOKIE_NAME: String
@@ -126,12 +110,12 @@ input UpdateEnvInput {
OLD_ADMIN_SECRET: String
SMTP_HOST: String
SMTP_PORT: String
SMTP_USERNAME: String
SMTP_PASSWORD: String
SENDER_EMAIL: String
SENDER_PASSWORD: String
JWT_TYPE: String
JWT_SECRET: String
ALLOWED_ORIGINS: [String!]
AUTHORIZER_URL: String
APP_URL: String
REDIS_URL: String
COOKIE_NAME: String
@@ -250,15 +234,6 @@ input IsValidJWTQueryInput {
roles: [String!]
}
input PaginationInput {
limit: Int64
page: Int64
}
input PaginatedInput {
pagination: PaginationInput
}
type Mutation {
signup(params: SignUpInput!): AuthResponse!
login(params: LoginInput!): AuthResponse!
@@ -284,8 +259,8 @@ type Query {
is_valid_jwt(params: IsValidJWTQueryInput): ValidJWTResponse!
profile: User!
# admin only apis
_users(params: PaginatedInput): Users!
_verification_requests(params: PaginatedInput): VerificationRequests!
_users: [User!]!
_verification_requests: [VerificationRequest!]!
_admin_session: Response!
_env: Env!
}

View File

@@ -87,12 +87,12 @@ func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
return resolvers.ProfileResolver(ctx)
}
func (r *queryResolver) Users(ctx context.Context, params *model.PaginatedInput) (*model.Users, error) {
return resolvers.UsersResolver(ctx, params)
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
return resolvers.UsersResolver(ctx)
}
func (r *queryResolver) VerificationRequests(ctx context.Context, params *model.PaginatedInput) (*model.VerificationRequests, error) {
return resolvers.VerificationRequestsResolver(ctx, params)
func (r *queryResolver) VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error) {
return resolvers.VerificationRequestsResolver(ctx)
}
func (r *queryResolver) AdminSession(ctx context.Context) (*model.Response, error) {

View File

@@ -22,19 +22,14 @@ type State struct {
// AppHandler is the handler for the /app route
func AppHandler() gin.HandlerFunc {
return func(c *gin.Context) {
hostname := utils.GetHost(c)
if envstore.EnvInMemoryStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableLoginPage) {
c.JSON(400, gin.H{"error": "login page is not enabled"})
return
}
state := c.Query("state")
var stateObj State
if state == "" {
stateObj.AuthorizerURL = hostname
stateObj.RedirectURL = hostname + "/app"
stateObj.AuthorizerURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL)
stateObj.RedirectURL = stateObj.AuthorizerURL + "/app"
} else {
decodedState, err := utils.DecryptB64(state)
if err != nil {
@@ -62,7 +57,7 @@ func AppHandler() gin.HandlerFunc {
}
// validate host and domain of authorizer url
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != hostname {
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) {
c.JSON(400, gin.H{"error": "invalid host url"})
return
}

View File

@@ -99,11 +99,6 @@ func OAuthCallbackHandler() gin.HandlerFunc {
user.SignupMethods = signupMethod
user.Password = existingUser.Password
if user.EmailVerifiedAt == nil {
now := time.Now().Unix()
user.EmailVerifiedAt = &now
}
// There multiple scenarios with roles here in social login
// 1. user has access to protected roles + roles and trying to login
// 2. user has not signed up for one of the available role but trying to signup.

View File

@@ -16,7 +16,7 @@ import (
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
func OAuthLoginHandler() gin.HandlerFunc {
return func(c *gin.Context) {
hostname := utils.GetHost(c)
// TODO validate redirect URL
redirectURL := c.Query("redirectURL")
roles := c.Query("roles")
@@ -56,7 +56,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
}
sessionstore.SetSocailLoginState(oauthStateString, constants.SignupMethodGoogle)
// during the init of OAuthProvider authorizer url might be empty
oauth.OAuthProviders.GoogleConfig.RedirectURL = hostname + "/oauth_callback/google"
oauth.OAuthProviders.GoogleConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/google"
url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
case constants.SignupMethodGithub:
@@ -65,7 +65,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
break
}
sessionstore.SetSocailLoginState(oauthStateString, constants.SignupMethodGithub)
oauth.OAuthProviders.GithubConfig.RedirectURL = hostname + "/oauth_callback/github"
oauth.OAuthProviders.GithubConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/github"
url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
case constants.SignupMethodFacebook:
@@ -74,7 +74,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
break
}
sessionstore.SetSocailLoginState(oauthStateString, constants.SignupMethodFacebook)
oauth.OAuthProviders.FacebookConfig.RedirectURL = hostname + "/oauth_callback/facebook"
oauth.OAuthProviders.FacebookConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/facebook"
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
default:

View File

@@ -3,12 +3,19 @@ package middlewares
import (
"context"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
)
// GinContextToContextMiddleware is a middleware to add gin context in context
func GinContextToContextMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) == "" {
url := location.Get(c)
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyAuthorizerURL, url.Scheme+"://"+c.Request.Host)
}
ctx := context.WithValue(c.Request.Context(), "GinContextKey", c)
c.Request = c.Request.WithContext(ctx)
c.Next()

View File

@@ -43,7 +43,7 @@ func InitOAuth() {
OAuthProviders.GoogleConfig = &oauth2.Config{
ClientID: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyGoogleClientID),
ClientSecret: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyGoogleClientSecret),
RedirectURL: "/oauth_callback/google",
RedirectURL: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/google",
Endpoint: OIDCProviders.GoogleOIDC.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
@@ -52,7 +52,7 @@ func InitOAuth() {
OAuthProviders.GithubConfig = &oauth2.Config{
ClientID: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyGithubClientID),
ClientSecret: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyGithubClientSecret),
RedirectURL: "/oauth_callback/github",
RedirectURL: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/github",
Endpoint: githubOAuth2.Endpoint,
}
}
@@ -60,7 +60,7 @@ func InitOAuth() {
OAuthProviders.FacebookConfig = &oauth2.Config{
ClientID: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyFacebookClientID),
ClientSecret: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyFacebookClientSecret),
RedirectURL: "/oauth_callback/facebook",
RedirectURL: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/facebook",
Endpoint: facebookOAuth2.Endpoint,
Scopes: []string{"public_profile", "email"},
}

View File

@@ -41,6 +41,7 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
jwtSecret := store.StringEnv[constants.EnvKeyJwtSecret]
jwtRoleClaim := store.StringEnv[constants.EnvKeyJwtRoleClaim]
allowedOrigins := store.SliceEnv[constants.EnvKeyAllowedOrigins]
authorizerURL := store.StringEnv[constants.EnvKeyAuthorizerURL]
appURL := store.StringEnv[constants.EnvKeyAppURL]
redisURL := store.StringEnv[constants.EnvKeyRedisURL]
cookieName := store.StringEnv[constants.EnvKeyCookieName]
@@ -76,6 +77,7 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
JwtSecret: &jwtSecret,
JwtRoleClaim: &jwtRoleClaim,
AllowedOrigins: allowedOrigins,
AuthorizerURL: &authorizerURL,
AppURL: &appURL,
RedisURL: &redisURL,
CookieName: &cookieName,

View File

@@ -27,6 +27,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
if envstore.EnvInMemoryStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication) {
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
}
host := gc.Request.Host
params.Email = strings.ToLower(params.Email)
if !utils.IsValidEmail(params.Email) {
@@ -38,8 +39,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
return res, fmt.Errorf(`user with this email not found`)
}
hostname := utils.GetHost(gc)
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname)
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword)
if err != nil {
log.Println(`error generating token`, err)
}
@@ -52,7 +52,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
// exec it as go routin so that we can reduce the api latency
go func() {
email.SendForgotPasswordMail(params.Email, verificationToken, hostname)
email.SendForgotPasswordMail(params.Email, verificationToken, host)
}()
res = &model.Response{

View File

@@ -20,10 +20,6 @@ import (
// MagicLinkLoginResolver is a resolver for magic link login mutation
func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) {
var res *model.Response
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
return res, err
}
if envstore.EnvInMemoryStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableMagicLinkLogin) {
return res, fmt.Errorf(`magic link login is disabled for this instance`)
@@ -106,11 +102,10 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
}
}
hostname := utils.GetHost(gc)
if !envstore.EnvInMemoryStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
// insert verification request
verificationType := constants.VerificationTypeMagicLinkLogin
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname)
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType)
if err != nil {
log.Println(`error generating token`, err)
}
@@ -123,7 +118,7 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
// exec it as go routin so that we can reduce the api latency
go func() {
email.SendVerificationMail(params.Email, verificationToken, hostname)
email.SendVerificationMail(params.Email, verificationToken)
}()
}

View File

@@ -18,10 +18,6 @@ import (
// ResendVerifyEmailResolver is a resolver for resend verify email mutation
func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error) {
var res *model.Response
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
return res, err
}
params.Email = strings.ToLower(params.Email)
if !utils.IsValidEmail(params.Email) {
@@ -43,8 +39,7 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma
log.Println("error deleting verification request:", err)
}
hostname := utils.GetHost(gc)
verificationToken, err := token.CreateVerificationToken(params.Email, params.Identifier, hostname)
verificationToken, err := token.CreateVerificationToken(params.Email, params.Identifier)
if err != nil {
log.Println(`error generating token`, err)
}
@@ -57,7 +52,7 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma
// exec it as go routin so that we can reduce the api latency
go func() {
email.SendVerificationMail(params.Email, verificationToken, hostname)
email.SendVerificationMail(params.Email, verificationToken)
}()
res = &model.Response{

View File

@@ -119,11 +119,10 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
roles := strings.Split(user.Roles, ",")
userToReturn := user.AsAPIUser()
hostname := utils.GetHost(gc)
if !envstore.EnvInMemoryStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
// insert verification request
verificationType := constants.VerificationTypeBasicAuthSignup
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname)
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType)
if err != nil {
log.Println(`error generating token`, err)
}
@@ -136,7 +135,7 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
// exec it as go routin so that we can reduce the api latency
go func() {
email.SendVerificationMail(params.Email, verificationToken, hostname)
email.SendVerificationMail(params.Email, verificationToken)
}()
res = &model.AuthResponse{

View File

@@ -13,10 +13,9 @@ import (
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
"golang.org/x/crypto/bcrypt"
)
// UpdateEnvResolver is a resolver for update config mutation
@@ -44,23 +43,6 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
return res, fmt.Errorf("error un-marshalling params: %t", err)
}
// in case of admin secret change update the cookie with new hash
if params.AdminSecret != nil {
if params.OldAdminSecret == nil {
return res, errors.New("admin secret and old admin secret are required for secret change")
}
if *params.OldAdminSecret != envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret) {
return res, errors.New("old admin secret is not correct")
}
if len(*params.AdminSecret) < 6 {
err = fmt.Errorf("admin secret must be at least 6 characters")
return res, err
}
}
updatedData := envstore.EnvInMemoryStoreObj.GetEnvStoreClone()
for key, value := range data {
if value != nil {
@@ -117,8 +99,6 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
// Update local store
envstore.EnvInMemoryStoreObj.UpdateEnvStore(updatedData)
sessionstore.InitSession()
oauth.InitOAuth()
// Fetch the current db store and update it
env, err := db.Provider.GetEnv()
@@ -126,7 +106,22 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
return res, err
}
encryptedConfig, err := utils.EncryptEnvData(updatedData)
if err != nil {
return res, err
}
// in case of admin secret change update the cookie with new hash
if params.AdminSecret != nil {
if params.OldAdminSecret == nil {
return res, errors.New("admin secret and old admin secret are required for secret change")
}
err := bcrypt.CompareHashAndPassword([]byte(*params.OldAdminSecret), []byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret)))
if err != nil {
return res, errors.New("old admin secret is not correct")
}
hashedKey, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret))
if err != nil {
return res, err
@@ -134,11 +129,6 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
cookie.SetAdminCookie(gc, hashedKey)
}
encryptedConfig, err := utils.EncryptEnvData(updatedData)
if err != nil {
return res, err
}
env.EnvData = encryptedConfig
_, err = db.Provider.UpdateEnv(env)
if err != nil {

View File

@@ -116,13 +116,12 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
cookie.DeleteCookie(gc)
hostname := utils.GetHost(gc)
user.Email = newEmail
user.EmailVerifiedAt = nil
hasEmailChanged = true
// insert verification request
verificationType := constants.VerificationTypeUpdateEmail
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname)
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType)
if err != nil {
log.Println(`error generating token`, err)
}
@@ -135,7 +134,7 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
// exec it as go routin so that we can reduce the api latency
go func() {
email.SendVerificationMail(newEmail, verificationToken, hostname)
email.SendVerificationMail(newEmail, verificationToken)
}()
}

View File

@@ -98,12 +98,11 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
cookie.DeleteCookie(gc)
hostname := utils.GetHost(gc)
user.Email = newEmail
user.EmailVerifiedAt = nil
// insert verification request
verificationType := constants.VerificationTypeUpdateEmail
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname)
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType)
if err != nil {
log.Println(`error generating token`, err)
}
@@ -116,7 +115,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
// exec it as go routin so that we can reduce the api latency
go func() {
email.SendVerificationMail(newEmail, verificationToken, hostname)
email.SendVerificationMail(newEmail, verificationToken)
}()
}
@@ -128,7 +127,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
inputRoles = append(inputRoles, *item)
}
if !utils.IsValidRoles(inputRoles, append([]string{}, append(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyRoles), envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyProtectedRoles)...)...)) {
if !utils.IsValidRoles(append([]string{}, append(envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyRoles), envstore.EnvInMemoryStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyProtectedRoles)...)...), inputRoles) {
return res, fmt.Errorf("invalid list of roles")
}

View File

@@ -12,21 +12,24 @@ import (
// UsersResolver is a resolver for users query
// This is admin only query
func UsersResolver(ctx context.Context, params *model.PaginatedInput) (*model.Users, error) {
func UsersResolver(ctx context.Context) ([]*model.User, error) {
gc, err := utils.GinContextFromContext(ctx)
var res []*model.User
if err != nil {
return nil, err
return res, err
}
if !token.IsSuperAdmin(gc) {
return nil, fmt.Errorf("unauthorized")
return res, fmt.Errorf("unauthorized")
}
pagination := utils.GetPagination(params)
res, err := db.Provider.ListUsers(pagination)
users, err := db.Provider.ListUsers()
if err != nil {
return nil, err
return res, err
}
for i := 0; i < len(users); i++ {
res = append(res, users[i].AsAPIUser())
}
return res, nil

View File

@@ -12,21 +12,32 @@ import (
// VerificationRequestsResolver is a resolver for verification requests query
// This is admin only query
func VerificationRequestsResolver(ctx context.Context, params *model.PaginatedInput) (*model.VerificationRequests, error) {
func VerificationRequestsResolver(ctx context.Context) ([]*model.VerificationRequest, error) {
gc, err := utils.GinContextFromContext(ctx)
var res []*model.VerificationRequest
if err != nil {
return nil, err
return res, err
}
if !token.IsSuperAdmin(gc) {
return nil, fmt.Errorf("unauthorized")
return res, fmt.Errorf("unauthorized")
}
pagination := utils.GetPagination(params)
res, err := db.Provider.ListVerificationRequests(pagination)
verificationRequests, err := db.Provider.ListVerificationRequests()
if err != nil {
return nil, err
return res, err
}
for i := 0; i < len(verificationRequests); i++ {
res = append(res, &model.VerificationRequest{
ID: fmt.Sprintf("%v", verificationRequests[i].ID),
Email: &verificationRequests[i].Email,
Token: &verificationRequests[i].Token,
Identifier: &verificationRequests[i].Identifier,
Expires: &verificationRequests[i].ExpiresAt,
CreatedAt: &verificationRequests[i].CreatedAt,
UpdatedAt: &verificationRequests[i].UpdatedAt,
})
}
return res, nil

View File

@@ -1,15 +1,18 @@
package routes
import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/handlers"
"github.com/authorizerdev/authorizer/server/middlewares"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
)
// InitRouter initializes gin router
func InitRouter() *gin.Engine {
router := gin.Default()
// router.Use(location.Default())
router.Use(location.Default())
router.Use(middlewares.GinContextToContextMiddleware())
router.Use(middlewares.CORSMiddleware())
@@ -22,21 +25,21 @@ func InitRouter() *gin.Engine {
router.LoadHTMLGlob("templates/*")
// login page app related routes.
app := router.Group("/app")
{
app.Static("/favicon_io", "app/favicon_io")
app.Static("/build", "app/build")
app.GET("/", handlers.AppHandler())
app.GET("/:page", handlers.AppHandler())
if !envstore.EnvInMemoryStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableLoginPage) {
app := router.Group("/app")
{
app.Static("/build", "app/build")
app.GET("/", handlers.AppHandler())
app.GET("/reset-password", handlers.AppHandler())
}
}
// dashboard related routes
dashboard := router.Group("/dashboard")
app := router.Group("/dashboard")
{
dashboard.Static("/favicon_io", "dashboard/favicon_io")
dashboard.Static("/build", "dashboard/build")
dashboard.GET("/", handlers.DashboardHandler())
dashboard.GET("/:page", handlers.DashboardHandler())
app.Static("/build", "dashboard/build")
app.GET("/", handlers.DashboardHandler())
app.GET("/:page", handlers.DashboardHandler())
}
return router
}

View File

@@ -7,12 +7,13 @@ import (
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func adminSignupTests(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should complete admin signup`, func(t *testing.T) {
t.Run(`should complete admin login`, func(t *testing.T) {
_, ctx := createContext(s)
_, err := resolvers.AdminSignupResolver(ctx, model.AdminSignupInput{
AdminSecret: "admin",
@@ -23,7 +24,7 @@ func adminSignupTests(t *testing.T, s TestSetup) {
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyAdminSecret, "")
_, err = resolvers.AdminSignupResolver(ctx, model.AdminSignupInput{
AdminSecret: "admin123",
AdminSecret: uuid.New().String(),
})
assert.Nil(t, err)

View File

@@ -14,13 +14,16 @@ func TestEnvs(t *testing.T) {
env.InitEnv()
store := envstore.EnvInMemoryStoreObj.GetEnvStoreClone()
assert.Equal(t, store.StringEnv[constants.EnvKeyAdminSecret], "admin")
assert.Equal(t, store.StringEnv[constants.EnvKeyEnv], "production")
assert.False(t, store.BoolEnv[constants.EnvKeyDisableEmailVerification])
assert.False(t, store.BoolEnv[constants.EnvKeyDisableMagicLinkLogin])
assert.False(t, store.BoolEnv[constants.EnvKeyDisableBasicAuthentication])
assert.Equal(t, store.StringEnv[constants.EnvKeyJwtType], "HS256")
assert.Equal(t, store.StringEnv[constants.EnvKeyJwtSecret], "random_string")
assert.Equal(t, store.StringEnv[constants.EnvKeyJwtRoleClaim], "role")
assert.EqualValues(t, store.SliceEnv[constants.EnvKeyRoles], []string{"user"})
assert.EqualValues(t, store.SliceEnv[constants.EnvKeyDefaultRoles], []string{"user"})
assert.EqualValues(t, store.SliceEnv[constants.EnvKeyProtectedRoles], []string{"admin"})
assert.EqualValues(t, store.SliceEnv[constants.EnvKeyAllowedOrigins], []string{"*"})
}

View File

@@ -1,7 +1,6 @@
package test
import (
"log"
"testing"
"github.com/authorizerdev/authorizer/server/constants"
@@ -12,29 +11,29 @@ import (
func TestResolvers(t *testing.T) {
databases := map[string]string{
constants.DbTypeSqlite: "../../data.db",
constants.DbTypeArangodb: "http://localhost:8529",
constants.DbTypeMongodb: "mongodb://localhost:27017",
constants.DbTypeSqlite: "../../data.db",
// constants.DbTypeArangodb: "http://localhost:8529",
// constants.DbTypeMongodb: "mongodb://localhost:27017",
}
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyVersion, "test")
for dbType, dbURL := range databases {
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseURL, dbURL)
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseType, dbType)
s := testSetup()
defer s.Server.Close()
env.InitEnv()
db.InitDB()
// clean the persisted config for test to use fresh config
envData, err := db.Provider.GetEnv()
log.Println("=> envData:", envstore.EnvInMemoryStoreObj.GetEnvStoreClone())
if err == nil {
envData.EnvData = ""
envData.EnvData = []byte{}
db.Provider.UpdateEnv(envData)
}
env.PersistEnv()
s := testSetup()
defer s.Server.Close()
t.Run("should pass tests for "+dbType, func(t *testing.T) {
// admin tests
adminSignupTests(t, s)

View File

@@ -14,6 +14,7 @@ import (
"github.com/authorizerdev/authorizer/server/handlers"
"github.com/authorizerdev/authorizer/server/middlewares"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
)
@@ -72,17 +73,13 @@ func testSetup() TestSetup {
}
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEnvPath, "../../.env.sample")
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpHost, "smtp.yopmail.com")
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpPort, "2525")
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpUsername, "lakhan@yopmail.com")
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpPassword, "test")
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySenderEmail, "info@yopmail.com")
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.SliceStoreIdentifier, constants.EnvKeyProtectedRoles, []string{"admin"})
env.InitEnv()
sessionstore.InitSession()
w := httptest.NewRecorder()
c, r := gin.CreateTestContext(w)
r.Use(location.Default())
r.Use(middlewares.GinContextToContextMiddleware())
r.Use(middlewares.CORSMiddleware())

View File

@@ -24,7 +24,7 @@ func updateUserTest(t *testing.T, s TestSetup) {
})
user := *signupRes.User
adminRole := "supplier"
adminRole := "admin"
userRole := "user"
newRoles := []*string{&adminRole, &userRole}
_, err := resolvers.UpdateUserResolver(ctx, model.UpdateUserInput{

View File

@@ -8,9 +8,9 @@ import (
)
func TestGetHostName(t *testing.T) {
url := "http://test.herokuapp.com:80"
authorizer_url := "http://test.herokuapp.com:80"
host, port := utils.GetHostParts(url)
host, port := utils.GetHostParts(authorizer_url)
expectedHost := "test.herokuapp.com"
assert.Equal(t, host, expectedHost, "hostname should be equal")
@@ -18,9 +18,9 @@ func TestGetHostName(t *testing.T) {
}
func TestGetDomainName(t *testing.T) {
url := "http://test.herokuapp.com"
authorizer_url := "http://test.herokuapp.com"
got := utils.GetDomainName(url)
got := utils.GetDomainName(authorizer_url)
want := "herokuapp.com"
assert.Equal(t, got, want, "domain name should be equal")

View File

@@ -2,7 +2,6 @@ package test
import (
"fmt"
"log"
"testing"
"github.com/authorizerdev/authorizer/server/constants"
@@ -24,26 +23,15 @@ func usersTest(t *testing.T, s TestSetup) {
ConfirmPassword: s.TestInfo.Password,
})
limit := int64(10)
page := int64(1)
pagination := &model.PaginatedInput{
Pagination: &model.PaginationInput{
Limit: &limit,
Page: &page,
},
}
usersRes, err := resolvers.UsersResolver(ctx, pagination)
users, err := resolvers.UsersResolver(ctx)
assert.NotNil(t, err, "unauthorized")
h, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret))
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), h))
usersRes, err = resolvers.UsersResolver(ctx, pagination)
users, err = resolvers.UsersResolver(ctx)
assert.Nil(t, err)
log.Println("=> userRes:", usersRes)
rLen := len(usersRes.Users)
rLen := len(users)
assert.GreaterOrEqual(t, rLen, 1)
cleanData(email)

View File

@@ -25,26 +25,16 @@ func verificationRequestsTest(t *testing.T, s TestSetup) {
ConfirmPassword: s.TestInfo.Password,
})
limit := int64(10)
page := int64(1)
pagination := &model.PaginatedInput{
Pagination: &model.PaginationInput{
Limit: &limit,
Page: &page,
},
}
requests, err := resolvers.VerificationRequestsResolver(ctx, pagination)
requests, err := resolvers.VerificationRequestsResolver(ctx)
assert.NotNil(t, err, "unauthorized")
h, err := utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret))
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), h))
requests, err = resolvers.VerificationRequestsResolver(ctx, pagination)
requests, err = resolvers.VerificationRequestsResolver(ctx)
assert.Nil(t, err)
rLen := len(requests.VerificationRequests)
rLen := len(requests)
assert.GreaterOrEqual(t, rLen, 1)
cleanData(email)

View File

@@ -2,6 +2,7 @@ package token
import (
"fmt"
"log"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
@@ -24,7 +25,7 @@ func GetAdminAuthToken(gc *gin.Context) (string, error) {
}
err = bcrypt.CompareHashAndPassword([]byte(token), []byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret)))
log.Println("error comparing hash:", err)
if err != nil {
return "", fmt.Errorf(`unauthorized`)
}

View File

@@ -23,7 +23,7 @@ type CustomClaim struct {
}
// CreateVerificationToken creates a verification JWT token
func CreateVerificationToken(email, tokenType, hostname string) (string, error) {
func CreateVerificationToken(email string, tokenType string) (string, error) {
t := jwt.New(jwt.GetSigningMethod(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtType)))
t.Claims = &CustomClaim{
@@ -31,7 +31,7 @@ func CreateVerificationToken(email, tokenType, hostname string) (string, error)
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
},
tokenType,
VerificationRequestToken{Email: email, Host: hostname, RedirectURL: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)},
VerificationRequestToken{Email: email, Host: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL), RedirectURL: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)},
}
return t.SignedString([]byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)))

View File

@@ -34,16 +34,3 @@ func SaveSessionInDB(userId string, c *gin.Context) {
log.Println("=> session saved in db:", sessionData)
}
}
// RemoveDuplicateString removes duplicate strings from a string slice
func RemoveDuplicateString(strSlice []string) []string {
allKeys := make(map[string]bool)
list := []string{}
for _, item := range strSlice {
if _, value := allKeys[item]; !value {
allKeys[item] = true
list = append(list, item)
}
}
return list
}

View File

@@ -90,29 +90,29 @@ func DecryptAES(ciphertext []byte) ([]byte, error) {
}
// EncryptEnvData is used to encrypt the env data
func EncryptEnvData(data envstore.Store) (string, error) {
func EncryptEnvData(data envstore.Store) ([]byte, error) {
jsonBytes, err := json.Marshal(data)
if err != nil {
return "", err
return []byte{}, err
}
envStoreObj := envstore.EnvInMemoryStoreObj.GetEnvStoreClone()
err = json.Unmarshal(jsonBytes, &envStoreObj)
if err != nil {
return "", err
return []byte{}, err
}
configData, err := json.Marshal(envStoreObj)
if err != nil {
return "", err
return []byte{}, err
}
encryptedConfig, err := EncryptAES(configData)
if err != nil {
return "", err
return []byte{}, err
}
return EncryptB64(string(encryptedConfig)), nil
return encryptedConfig, nil
}
// EncryptPassword is used for encrypting password

View File

@@ -1,29 +0,0 @@
package utils
import (
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/graph/model"
)
// GetPagination helps getting pagination data from paginated input
// also returns default limit and offset if pagination data is not present
func GetPagination(paginatedInput *model.PaginatedInput) model.Pagination {
limit := int64(constants.DefaultLimit)
page := int64(1)
if paginatedInput != nil && paginatedInput.Pagination != nil {
if paginatedInput.Pagination.Limit != nil {
limit = *paginatedInput.Pagination.Limit
}
if paginatedInput.Pagination.Page != nil {
page = *paginatedInput.Pagination.Page
}
}
return model.Pagination{
Limit: limit,
Offset: (page - 1) * limit,
Page: page,
}
}

View File

@@ -1,24 +1,10 @@
package utils
import (
"log"
"net/url"
"strings"
"github.com/gin-gonic/gin"
)
// GetHost returns hostname from request context
func GetHost(c *gin.Context) string {
scheme := c.Request.Header.Get("X-Forwarded-Proto")
if scheme != "https" {
scheme = "http"
}
host := c.Request.Host
log.Println("=> host:", scheme+"://"+host)
return scheme + "://" + host
}
// GetHostName function returns hostname and port
func GetHostParts(uri string) (string, string) {
tempURI := uri

View File

@@ -2,12 +2,8 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{{.data.organizationName}}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="apple-touch-icon" sizes="180x180" href="/app/favicon_io/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/app/favicon_io/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/app/favicon_io/favicon-16x16.png">
<title>Document</title>
<link rel="stylesheet" href="/app/build/index.css">
<script>

View File

@@ -2,12 +2,8 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Authorizer | Dashboard</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="apple-touch-icon" sizes="180x180" href="/dashboard/favicon_io/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/dashboard/favicon_io/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/dashboard/favicon_io/favicon-16x16.png">
<title>Document</title>
<script>
window.__authorizer__ = {{.data}}