Compare commits

...

52 Commits

Author SHA1 Message Date
Lakhan Samani
7b13034081 fix: dashboard 2022-05-25 16:34:54 +05:30
Lakhan Samani
7c16900618 Merge pull request #168 from anik-ghosh-au7/fix/app-routes
update: separate routes for login and signup added
2022-05-25 16:07:15 +05:30
Lakhan Samani
d722fe258d Merge pull request #173 from authorizerdev/feat/logging
feat: add logging system
2022-05-25 15:07:28 +05:30
Lakhan Samani
99dc5ee572 fix: remove unused code 2022-05-25 15:05:51 +05:30
Lakhan Samani
8bee421d0a feat: add flag for log level 2022-05-25 15:04:26 +05:30
Lakhan Samani
714b79e4ab fix: format logs 2022-05-25 12:30:22 +05:30
Lakhan Samani
d886d780b4 fix: replace all logs 2022-05-24 12:50:33 +05:30
Lakhan Samani
d7bb10fd21 feat: add loggging to all resolvers 2022-05-24 12:42:29 +05:30
Lakhan Samani
f5515bec28 fix: merge conflict 2022-05-23 11:54:46 +05:30
Lakhan Samani
b35d86fd40 feat: add logs for http handlers 2022-05-23 11:52:51 +05:30
anik-ghosh-au7
a638f02014 update: seperate routes for login and signup added 2022-05-18 20:04:29 +05:30
Lakhan Samani
2c4bc9adb6 Merge pull request #161 from akash-dutta-au7/fix/env-page-new
Update Dashboard UI
2022-05-15 17:11:13 +05:30
Lakhan Samani
5884802e60 Merge pull request #166 from Vicg853/fix/role-update
Fix/role update
2022-05-15 17:04:42 +05:30
Vicg853
241f977b2a Fixing related files 2022-05-14 23:21:45 -03:00
anik-ghosh-au7
ed855a274a fix: app style remove unnecessary code 2022-05-14 20:20:21 +05:30
Vicg853
049ea64475 Merge branch 'main' of github.com:Vicg853/authorizer into fix/role-update 2022-05-14 05:07:13 -03:00
Vicg853
5e4f34c889 Fixing login isValidRole usage 2022-05-14 04:37:36 -03:00
Lakhan Samani
ab717d956a fix: update role test 2022-05-13 07:49:45 +05:30
Lakhan Samani
6209c4d506 Merge pull request #165 from Vicg853/fix/role-update
Unable to update user role fix
2022-05-13 07:38:45 +05:30
Lakhan Samani
2bc4c74930 fix: remove old logs 2022-05-13 07:28:31 +05:30
Vicg853
1efa419cdf Clean up 2022-05-12 16:43:07 -03:00
Vicg853
4ceb6db4ba Adding possible test error cause comment 2022-05-12 16:40:49 -03:00
Vicg853
9edc8d0fb5 Inverted userRoles by role fix. Roles can now be updated 2022-05-12 16:40:19 -03:00
Lakhan Samani
da0fcb109b feat: setup logours for logging 2022-05-13 00:47:01 +05:30
akash.dutta
3e51a7bd01 login page vertically responsive 2022-05-12 17:06:18 +05:30
akash.dutta
28bed69b2e login page vertically responsive 2022-05-12 15:10:24 +05:30
anik-ghosh-au7
0433d64737 fix: dom nesting bugs 2022-05-12 12:27:25 +05:30
Lakhan Samani
773213e5a4 fix: clean test data 2022-05-11 20:25:57 +05:30
akash.dutta
de44c40de5 login page fixed 2022-05-09 15:46:42 +05:30
akash.dutta
a7fa988bf0 tooltiip added to Invite member button 2022-05-08 12:48:26 +05:30
akash.dutta
538a2d0b59 tooltiip added to Invite member button 2022-05-08 12:43:52 +05:30
akash.dutta
f519f0eb0e tooltiip added to Invite member button 2022-05-08 09:35:50 +05:30
akash.dutta
d5ad4a6e55 blue border on navlink removed 2022-05-07 22:34:26 +05:30
akash.dutta
d9b49ca932 login page responsive 2022-05-07 22:18:04 +05:30
akash.dutta
7c5aab7bf3 all components updated, uncontrolled input error handled 2022-05-07 22:10:29 +05:30
akash.dutta
c783e101d5 test-pull req 2022-05-03 19:53:21 +05:30
akash.dutta
ebccfb18cd test-pull req 2022-05-03 19:50:01 +05:30
Lakhan Samani
b7aeff57af fixes #160 2022-04-30 12:45:08 +05:30
Lakhan Samani
075c287f34 feat: add support for database cert, key, ca-cert 2022-04-23 17:52:02 +05:30
Lakhan Samani
4778827545 Merge pull request #144 from authorizerdev/feat/casandra-db
feat: add support for cassandra db
2022-04-22 21:26:10 +05:30
Lakhan Samani
39c2c364d9 feat: add support for db username, password, host, port 2022-04-22 21:24:39 +05:30
Lakhan Samani
961f2271c1 fix: tests 2022-04-22 19:56:55 +05:30
Lakhan Samani
aaf0831793 feat: add users queries 2022-04-22 16:45:49 +05:30
Lakhan Samani
27cb41c54c feat: add verification_request queries 2022-04-22 11:52:15 +05:30
Lakhan Samani
718b2d535f feat: add session queries 2022-04-21 18:11:15 +05:30
Lakhan Samani
ed6a1ceccc feat: add env queries 2022-04-21 17:54:33 +05:30
Lakhan Samani
fd52d6e5d3 feat: add casandradb provider 2022-04-21 12:36:22 +05:30
Lakhan Samani
325aa88368 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/casandra-db 2022-04-20 23:32:02 +05:30
Lakhan Samani
75e44ff698 fix: cors error for x-authorizer-url 2022-04-10 14:43:19 +05:30
Lakhan Samani
d5f1c5a5eb Resolves #156 2022-04-02 17:34:50 +05:30
Lakhan Samani
39947f1753 Merge pull request #155 from authorizerdev/fix/gateway-based-setup
fix: setting the cookie for proxy setup
2022-03-30 11:51:20 +05:30
Lakhan Samani
a6b743465f feat: add provider template 2022-03-19 17:41:27 +05:30
127 changed files with 3520 additions and 1036 deletions

View File

@@ -9,3 +9,4 @@ build
data.db
app/node_modules
app/build
certs/

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@ data.db
.vscode/
.yalc
yalc.lock
certs/

46
app/package-lock.json generated
View File

@@ -17,10 +17,12 @@
"react-dom": "^17.0.2",
"react-is": "^17.0.2",
"react-router-dom": "^5.2.0",
"styled-components": "^5.3.0",
"typescript": "^4.3.5"
},
"devDependencies": {
"@types/react-router-dom": "^5.1.8"
"@types/react-router-dom": "^5.1.8",
"@types/styled-components": "^5.1.11"
}
},
"node_modules/@authorizerdev/authorizer-js": {
@@ -271,6 +273,16 @@
"integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==",
"dev": true
},
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"dev": true,
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.4",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
@@ -320,6 +332,17 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
},
"node_modules/@types/styled-components": {
"version": "5.1.25",
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz",
"integrity": "sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==",
"dev": true,
"dependencies": {
"@types/hoist-non-react-statics": "*",
"@types/react": "*",
"csstype": "^3.0.2"
}
},
"node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
@@ -1016,6 +1039,16 @@
"integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==",
"dev": true
},
"@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"dev": true,
"requires": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/prop-types": {
"version": "15.7.4",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
@@ -1065,6 +1098,17 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
},
"@types/styled-components": {
"version": "5.1.25",
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz",
"integrity": "sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==",
"dev": true,
"requires": {
"@types/hoist-non-react-statics": "*",
"@types/react": "*",
"csstype": "^3.0.2"
}
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",

View File

@@ -19,9 +19,11 @@
"react-dom": "^17.0.2",
"react-is": "^17.0.2",
"react-router-dom": "^5.2.0",
"typescript": "^4.3.5"
"typescript": "^4.3.5",
"styled-components": "^5.3.0"
},
"devDependencies": {
"@types/react-router-dom": "^5.1.8"
"@types/react-router-dom": "^5.1.8",
"@types/styled-components": "^5.1.11"
}
}

View File

@@ -1,11 +1,28 @@
import React, { useEffect, lazy, Suspense } from 'react';
import { Switch, Route } from 'react-router-dom';
import { useAuthorizer } from '@authorizerdev/authorizer-react';
import styled, { ThemeProvider } from 'styled-components';
import SetupPassword from './pages/setup-password';
import { hasWindow, createRandomString } from './utils/common';
import { theme } from './theme';
const ResetPassword = lazy(() => import('./pages/rest-password'));
const Login = lazy(() => import('./pages/login'));
const Dashboard = lazy(() => import('./pages/dashboard'));
const SignUp = lazy(() => import('./pages/signup'));
const Wrapper = styled.div`
font-family: ${(props) => props.theme.fonts.fontStack};
color: ${(props) => props.theme.colors.textColor};
font-size: ${(props) => props.theme.fonts.mediumText};
box-sizing: border-box;
*,
*:before,
*:after {
box-sizing: inherit;
}
`;
export default function Root({
globalState,
@@ -14,6 +31,29 @@ export default function Root({
}) {
const { token, loading, config } = useAuthorizer();
const searchParams = new URLSearchParams(
hasWindow() ? window.location.search : ``
);
const state = searchParams.get('state') || createRandomString();
const scope = searchParams.get('scope')
? searchParams.get('scope')?.toString().split(' ')
: ['openid', 'profile', 'email'];
const urlProps: Record<string, any> = {
state,
scope,
};
const redirectURL =
searchParams.get('redirect_uri') || searchParams.get('redirectURL');
if (redirectURL) {
urlProps.redirectURL = redirectURL;
} else {
urlProps.redirectURL = hasWindow() ? window.location.origin : redirectURL;
}
urlProps.redirect_uri = urlProps.redirectURL;
useEffect(() => {
if (token) {
let redirectURL = config.redirectURL || '/app';
@@ -54,9 +94,14 @@ export default function Root({
return (
<Suspense fallback={<></>}>
<ThemeProvider theme={theme}>
<Wrapper>
<Switch>
<Route path="/app" exact>
<Login />
<Login urlProps={urlProps} />
</Route>
<Route path="/app/signup" exact>
<SignUp urlProps={urlProps} />
</Route>
<Route path="/app/reset-password">
<ResetPassword />
@@ -65,6 +110,8 @@ export default function Root({
<SetupPassword />
</Route>
</Switch>
</Wrapper>
</ThemeProvider>
</Suspense>
);
}

View File

@@ -1,10 +1,82 @@
import React, { Fragment } from 'react';
import { Authorizer } from '@authorizerdev/authorizer-react';
import React, { Fragment, useState } from 'react';
import {
AuthorizerBasicAuthLogin,
AuthorizerForgotPassword,
AuthorizerMagicLinkLogin,
AuthorizerSocialLogin,
useAuthorizer,
} from '@authorizerdev/authorizer-react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
export default function Login() {
const enum VIEW_TYPES {
LOGIN = 'login',
FORGOT_PASSWORD = 'forgot-password',
}
const Footer = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 15px;
`;
const FooterContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
`;
export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
const { config } = useAuthorizer();
const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN);
return (
<Fragment>
<Authorizer />
{view === VIEW_TYPES.LOGIN && (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Login</h1>
<br />
<AuthorizerSocialLogin urlProps={urlProps} />
{config.is_basic_authentication_enabled &&
!config.is_magic_link_login_enabled && (
<AuthorizerBasicAuthLogin urlProps={urlProps} />
)}
{config.is_magic_link_login_enabled && (
<AuthorizerMagicLinkLogin urlProps={urlProps} />
)}
<Footer>
<Link
to="#"
onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)}
style={{ marginBottom: 10 }}
>
Forgot Password?
</Link>
</Footer>
</Fragment>
)}
{view === VIEW_TYPES.FORGOT_PASSWORD && (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
<AuthorizerForgotPassword urlProps={urlProps} />
<Footer>
<Link
to="#"
onClick={() => setView(VIEW_TYPES.LOGIN)}
style={{ marginBottom: 10 }}
>
Back
</Link>
</Footer>
</Fragment>
)}
{config.is_sign_up_enabled && (
<FooterContent>
Don't have an account? <Link to="/app/signup"> Sign Up</Link>
</FooterContent>
)}
</Fragment>
);
}

28
app/src/pages/signup.tsx Normal file
View File

@@ -0,0 +1,28 @@
import React, { Fragment } from 'react';
import { AuthorizerSignup } from '@authorizerdev/authorizer-react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
const FooterContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
`;
export default function SignUp({
urlProps,
}: {
urlProps: Record<string, any>;
}) {
return (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
<br />
<AuthorizerSignup urlProps={urlProps} />
<FooterContent>
Already have an account? <Link to="/app"> Login</Link>
</FooterContent>
</Fragment>
);
}

28
app/src/theme.ts Normal file
View File

@@ -0,0 +1,28 @@
// colors: https://tailwindcss.com/docs/customizing-colors
export const theme = {
colors: {
primary: '#3B82F6',
primaryDisabled: '#60A5FA',
gray: '#D1D5DB',
danger: '#DC2626',
success: '#10B981',
textColor: '#374151',
},
fonts: {
// typography
fontStack: '-apple-system, system-ui, sans-serif',
// font sizes
largeText: '18px',
mediumText: '14px',
smallText: '12px',
tinyText: '10px',
},
radius: {
card: '5px',
button: '5px',
input: '5px',
},
};

View File

@@ -20,3 +20,5 @@ export const createQueryParams = (params: any) => {
.map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
.join('&');
};
export const hasWindow = (): boolean => typeof window !== 'undefined';

View File

@@ -10,6 +10,7 @@
"license": "ISC",
"dependencies": {
"@chakra-ui/react": "^1.7.3",
"@emotion/core": "^11.0.0",
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@types/react": "^17.0.38",
@@ -17,6 +18,7 @@
"@types/react-router-dom": "^5.3.2",
"dayjs": "^1.10.7",
"esbuild": "^0.14.9",
"focus-visible": "^5.2.0",
"framer-motion": "^5.5.5",
"graphql": "^16.2.0",
"lodash": "^4.17.21",
@@ -978,6 +980,11 @@
"stylis": "4.0.13"
}
},
"node_modules/@emotion/core": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/@emotion/core/-/core-11.0.0.tgz",
"integrity": "sha512-w4sE3AmHmyG6RDKf6mIbtHpgJUSJ2uGvPQb8VXFL7hFjMPibE8IiehG8cMX3Ztm4svfCQV6KqusQbeIOkurBcA=="
},
"node_modules/@emotion/hash": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
@@ -1667,6 +1674,11 @@
"node": ">=10"
}
},
"node_modules/focus-visible": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.0.tgz",
"integrity": "sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ=="
},
"node_modules/framer-motion": {
"version": "5.5.5",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-5.5.5.tgz",
@@ -2517,8 +2529,7 @@
"@chakra-ui/css-reset": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-1.1.1.tgz",
"integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg==",
"requires": {}
"integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg=="
},
"@chakra-ui/descendant": {
"version": "2.1.1",
@@ -3038,6 +3049,11 @@
"stylis": "4.0.13"
}
},
"@emotion/core": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/@emotion/core/-/core-11.0.0.tgz",
"integrity": "sha512-w4sE3AmHmyG6RDKf6mIbtHpgJUSJ2uGvPQb8VXFL7hFjMPibE8IiehG8cMX3Ztm4svfCQV6KqusQbeIOkurBcA=="
},
"@emotion/hash": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
@@ -3117,8 +3133,7 @@
"@graphql-typed-document-node/core": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz",
"integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==",
"requires": {}
"integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg=="
},
"@popperjs/core": {
"version": "2.11.0",
@@ -3540,6 +3555,11 @@
"tslib": "^2.0.3"
}
},
"focus-visible": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.0.tgz",
"integrity": "sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ=="
},
"framer-motion": {
"version": "5.5.5",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-5.5.5.tgz",
@@ -3823,8 +3843,7 @@
"react-icons": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz",
"integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==",
"requires": {}
"integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ=="
},
"react-is": {
"version": "16.13.1",
@@ -4010,8 +4029,7 @@
"use-callback-ref": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz",
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==",
"requires": {}
"integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg=="
},
"use-sidecar": {
"version": "1.0.5",

View File

@@ -12,6 +12,7 @@
"license": "ISC",
"dependencies": {
"@chakra-ui/react": "^1.7.3",
"@emotion/core": "^11.0.0",
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@types/react": "^17.0.38",
@@ -19,6 +20,7 @@
"@types/react-router-dom": "^5.3.2",
"dayjs": "^1.10.7",
"esbuild": "^0.14.9",
"focus-visible": "^5.2.0",
"framer-motion": "^5.5.5",
"graphql": "^16.2.0",
"lodash": "^4.17.21",

View File

@@ -1,4 +1,5 @@
import * as React from 'react';
import { Fragment } from 'react';
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
import { BrowserRouter } from 'react-router-dom';
import { createClient, Provider } from 'urql';
@@ -22,8 +23,8 @@ const theme = extendTheme({
styles: {
global: {
'html, body, #root': {
fontFamily: 'Avenir, Helvetica, Arial, sans-serif',
height: '100%',
outline: 'none',
},
},
},
@@ -36,6 +37,7 @@ const theme = extendTheme({
export default function App() {
return (
<Fragment>
<ChakraProvider theme={theme}>
<Provider value={queryClient}>
<BrowserRouter basename="/dashboard">
@@ -45,5 +47,6 @@ export default function App() {
</BrowserRouter>
</Provider>
</ChakraProvider>
</Fragment>
);
}

View File

@@ -0,0 +1,65 @@
import React from "react";
import { Flex, Stack, Text, useMediaQuery } from "@chakra-ui/react";
import InputField from "../../components/InputField";
import { TextInputType, TextAreaInputType } from "../../constants";
const AccessToken = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return (
<div>
{" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Access Token
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex
w={isNotSmallerScreen ? "30%" : "50%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Access Token Expiry Time:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.ACCESS_TOKEN_EXPIRY_TIME}
placeholder="0h15m0s"
/>
</Flex>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex
w={isNotSmallerScreen ? "30%" : "60%"}
justifyContent="start"
direction="column"
>
<Text fontSize="sm">Custom Scripts:</Text>
<Text fontSize="xs" color="blackAlpha.500">
(Used to add custom fields in ID token)
</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
placeholder="Add script here"
minH="25vh"
/>
</Flex>
</Flex>
</Stack>
</div>
);
};
export default AccessToken;

View File

@@ -0,0 +1,88 @@
import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import InputField from "../../components/InputField";
import { TextInputType } from "../../constants";
const DatabaseCredentials = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return (
<div>
{" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Database Credentials
</Text>
<Stack spacing={6} padding="3% 0">
<Text fontStyle="italic" fontSize="sm" color="blackAlpha.500" mt={3}>
Note: Database related environment variables cannot be updated from
dashboard :(
</Text>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex
w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">DataBase Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.DATABASE_NAME}
isDisabled={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex
w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">DataBase Type:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.DATABASE_TYPE}
isDisabled={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex
w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">DataBase URL:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.DATABASE_URL}
isDisabled={true}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default DatabaseCredentials;

View File

@@ -0,0 +1,35 @@
import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import InputField from "../../components/InputField";
import { ArrayInputType} from "../../constants";
const DomainWhiteListing = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return (
<div>
{" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Domain White Listing
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Allowed Origins:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.ALLOWED_ORIGINS}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default DomainWhiteListing;

View File

@@ -0,0 +1,114 @@
import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import InputField from "../../components/InputField";
import { TextInputType, HiddenInputType} from "../../constants";
const EmailConfigurations = ({
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return (
<div>
{" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Email Configurations
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">SMTP Host:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_HOST}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">SMTP Port:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_PORT}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex
w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">SMTP Username:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_USERNAME}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex
w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">SMTP Password:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.SMTP_PASSWORD}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">From Email:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SENDER_EMAIL}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default EmailConfigurations;

View File

@@ -0,0 +1,154 @@
import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import {
HiddenInputType,
TextInputType,
TextAreaInputType,
} from "../../constants";
import GenerateKeysModal from "../GenerateKeysModal";
import InputField from "../InputField";
const JSTConfigurations = ({
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
SelectInputType,
getData,
HMACEncryptionType,
RSAEncryptionType,
ECDSAEncryptionType,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return (
<div>
{" "}
<Flex
borderRadius={5}
width="100%"
justifyContent="space-between"
alignItems="center"
paddingTop="2%"
>
<Text
fontSize={isNotSmallerScreen ? "md" : "sm"}
fontWeight="bold"
mb={5}
>
JWT (JSON Web Tokens) Configurations
</Text>
<Flex mb={7}>
<GenerateKeysModal jwtType={variables.JWT_TYPE} getData={getData} />
</Flex>
</Flex>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Type:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "2"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={SelectInputType}
value={SelectInputType}
options={{
...HMACEncryptionType,
...RSAEncryptionType,
...ECDSAEncryptionType,
}}
/>
</Flex>
</Flex>
{Object.values(HMACEncryptionType).includes(variables.JWT_TYPE) ? (
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Secret</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "2"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.JWT_SECRET}
/>
</Center>
</Flex>
) : (
<>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Public Key</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "2"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
placeholder="Add public key here"
minH="25vh"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Private Key</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "2"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
placeholder="Add private key here"
minH="25vh"
/>
</Center>
</Flex>
</>
)}
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex
w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm" orientation="vertical">
JWT Role Claim:
</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "2"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.JWT_ROLE_CLAIM}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default JSTConfigurations;

View File

@@ -0,0 +1,191 @@
import React from 'react';
import InputField from '../InputField';
import {
Flex,
Stack,
Center,
Text,
Box,
Divider,
useMediaQuery,
} from '@chakra-ui/react';
import { FaGoogle, FaGithub, FaFacebookF } from 'react-icons/fa';
import { TextInputType, HiddenInputType } from '../../constants';
const OAuthConfig = ({
envVariables,
setVariables,
fieldVisibility,
setFieldVisibility,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:667px)');
return (
<div>
<Box>
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={6}>
Authorizer Config
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Client ID</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
variables={envVariables}
setVariables={() => {}}
inputType={TextInputType.CLIENT_ID}
placeholder="Client ID"
readOnly={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Client Secret</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.CLIENT_SECRET}
placeholder="Client Secret"
readOnly={true}
/>
</Center>
</Flex>
</Stack>
<Divider mt={5} mb={2} color="blackAlpha.700" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={4}>
Social Media Logins
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #ff3e30"
borderRadius="5px"
>
<FaGoogle style={{ color: '#ff3e30' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.GOOGLE_CLIENT_ID}
placeholder="Google Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.GOOGLE_CLIENT_SECRET}
placeholder="Google Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #171515"
borderRadius="5px"
>
<FaGithub style={{ color: '#171515' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.GITHUB_CLIENT_ID}
placeholder="Github Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.GITHUB_CLIENT_SECRET}
placeholder="Github Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaFacebookF style={{ color: '#3b5998' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.FACEBOOK_CLIENT_ID}
placeholder="Facebook Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.FACEBOOK_CLIENT_SECRET}
placeholder="Facebook Secret"
/>
</Center>
</Flex>
</Stack>
</Box>
</div>
);
};
export default OAuthConfig;

View File

@@ -0,0 +1,60 @@
import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import InputField from "../InputField";
import { TextInputType } from "../../constants";
const OrganizationInfo = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return (
<div>
{" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Organization Information
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex
w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Organization Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.ORGANIZATION_NAME}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex
w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Organization Logo:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.ORGANIZATION_LOGO}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default OrganizationInfo;

View File

@@ -0,0 +1,67 @@
import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import { ArrayInputType } from "../../constants";
import InputField from "../InputField";
const Roles = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return (
<div>
{" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Roles
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Roles:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "2"}
overflow="hidden"
>
<InputField
borderRadius={7}
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.ROLES}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Roles:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "2"}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.DEFAULT_ROLES}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Protected Roles:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "2"}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.PROTECTED_ROLES}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default Roles;

View File

@@ -0,0 +1,138 @@
import React from "react";
import {
Flex,
Stack,
Center,
Text,
Input,
InputGroup,
InputRightElement,
useMediaQuery,
} from "@chakra-ui/react";
import { FaRegEyeSlash, FaRegEye } from "react-icons/fa";
import InputField from "../InputField";
import { HiddenInputType } from "../../constants";
const SecurityAdminSecret = ({
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
validateAdminSecretHandler,
adminSecret,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return (
<div>
{" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Security (Admin Secret)
</Text>
<Stack
spacing={6}
padding="0 5%"
marginTop="3%"
border="1px solid #ff7875"
borderRadius="5px"
>
<Flex
marginTop={isNotSmallerScreen ? "3%" : "5%"}
direction={isNotSmallerScreen ? "row" : "column"}
>
<Flex
mt={3}
w={isNotSmallerScreen ? "30%" : "40%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Old Admin Secret:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputGroup size="sm">
<Input
borderRadius={5}
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%"
direction={isNotSmallerScreen ? "row" : "column"}
>
<Flex
w={isNotSmallerScreen ? "30%" : "50%"}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">New Admin Secret:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
mb={3}
variables={variables}
setVariables={setVariables}
inputType={HiddenInputType.ADMIN_SECRET}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
isDisabled={adminSecret.disableInputField}
placeholder="Enter New Admin Secret"
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default SecurityAdminSecret;

View File

@@ -0,0 +1,36 @@
import React from "react";
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
import InputField from "../InputField";
const SessionStorage = ({ variables, setVariables, RedisURL }: any) => {
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
return (
<div>
{" "}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Session Storage
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Redis URL:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? "70%" : "100%"}
mt={isNotSmallerScreen ? "0" : "3"}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={RedisURL}
placeholder="Redis URL"
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default SessionStorage;

View File

@@ -0,0 +1,79 @@
import React from 'react';
import { Flex, Stack, Text } from '@chakra-ui/react';
import InputField from '../InputField';
import { SwitchInputType } from '../../constants';
const UICustomization = ({ variables, setVariables }: any) => {
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Disable Features
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Login Page:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Email Verification:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Magic Login Link:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Basic Authentication:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Sign Up:</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_SIGN_UP}
/>
</Flex>
</Flex>
</Stack>
</div>
);
};
export default UICustomization;

View File

@@ -13,6 +13,7 @@ import {
Textarea,
Switch,
Code,
Text,
} from '@chakra-ui/react';
import {
FaRegClone,
@@ -116,7 +117,7 @@ const InputField = ({
<InputGroup size="sm">
<Input
{...props}
value={variables[inputType]}
value={variables[inputType] ?? ''}
onChange={(
event: Event & {
target: HTMLInputElement;
@@ -181,8 +182,9 @@ const InputField = ({
<Flex
border="1px solid #e2e8f0"
w="100%"
borderRadius={5}
paddingTop="0.5%"
overflowX="scroll"
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
overflowY="hidden"
justifyContent="start"
alignItems="center"
@@ -220,7 +222,7 @@ const InputField = ({
size="xs"
minW="150px"
placeholder="add a new value"
value={inputData[inputType]}
value={inputData[inputType] ?? ''}
onChange={(e: any) => {
setInputData({ ...inputData, [inputType]: e.target.value });
}}
@@ -300,7 +302,9 @@ const InputField = ({
if (Object.values(SwitchInputType).includes(inputType)) {
return (
<Flex w="25%" justifyContent="space-between">
<Code h="75%">Off</Code>
<Text h="75%" fontWeight="bold" marginRight="2">
Off
</Text>
<Switch
size="md"
isChecked={variables[inputType]}
@@ -311,7 +315,9 @@ const InputField = ({
});
}}
/>
<Code h="75%">On</Code>
<Text h="75%" fontWeight="bold" marginLeft="2">
On
</Text>
</Flex>
);
}

View File

@@ -22,6 +22,7 @@ import {
InputRightElement,
Text,
Link,
Tooltip
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaUserPlus, FaMinusCircle, FaPlus, FaUpload } from 'react-icons/fa';
@@ -186,7 +187,22 @@ const InviteMembersModal = ({
isDisabled={disabled}
size="sm"
>
<Center h="100%">Invite Members</Center>
<Center h="100%">
{disabled ? (
<Tooltip
mr={8}
mt={1}
hasArrow
bg="gray.300"
color="black"
label="Email verification is disabled, refer to 'UI Customization' tab within 'Environment' to enable it."
>
Invite Members
</Tooltip>
) : (
"Invite Members"
)}
</Center>{" "}
</Button>
<Modal isOpen={isOpen} onClose={closeModalHandler} size="xl">
<ModalOverlay />

View File

@@ -1,4 +1,4 @@
import React, { ReactNode } from 'react';
import React, { Fragment, ReactNode } from 'react';
import {
IconButton,
Box,
@@ -17,16 +17,27 @@ import {
MenuButton,
MenuItem,
MenuList,
Accordion,
AccordionButton,
AccordionPanel,
AccordionItem,
useMediaQuery,
} from '@chakra-ui/react';
import {
FiHome,
FiUser,
FiCode,
FiSettings,
FiMenu,
FiUser,
FiUsers,
FiChevronDown,
} from 'react-icons/fi';
import { BiCustomize } from 'react-icons/bi';
import { AiOutlineKey } from 'react-icons/ai';
import { SiOpenaccess, SiJsonwebtokens } from 'react-icons/si';
import { MdSecurity } from 'react-icons/md';
import { RiDatabase2Line } from 'react-icons/ri';
import { BsCheck2Circle } from 'react-icons/bs';
import { HiOutlineMail, HiOutlineOfficeBuilding } from 'react-icons/hi';
import { IconType } from 'react-icons';
import { ReactText } from 'react';
import { useMutation, useQuery } from 'urql';
@@ -35,14 +46,70 @@ import { useAuthContext } from '../contexts/AuthContext';
import { AdminLogout } from '../graphql/mutation';
import { MetaQuery } from '../graphql/queries';
interface LinkItemProps {
interface SubRoutes {
name: string;
icon: IconType;
route: string;
}
interface LinkItemProps {
name: string;
icon: IconType;
route: string;
subRoutes?: SubRoutes[];
}
const LinkItems: Array<LinkItemProps> = [
// { name: 'Home', icon: FiHome, route: '/' },
{ name: 'Environment Variables', icon: FiSettings, route: '/' },
{
name: 'Environment ',
icon: FiSettings,
route: '/',
subRoutes: [
{
name: 'OAuth Config',
icon: AiOutlineKey,
route: '/oauth-setting',
},
{ name: 'Roles', icon: FiUser, route: '/roles' },
{
name: 'JWT Secrets',
icon: SiJsonwebtokens,
route: '/jwt-config',
},
{
name: 'Session Storage',
icon: RiDatabase2Line,
route: '/session-storage',
},
{
name: 'Email Configurations',
icon: HiOutlineMail,
route: '/email-config',
},
{
name: 'Domain White Listing',
icon: BsCheck2Circle,
route: '/whitelist-variables',
},
{
name: 'Organization Info',
icon: HiOutlineOfficeBuilding,
route: '/organization-info',
},
{ name: 'Access Token', icon: SiOpenaccess, route: '/access-token' },
{
name: 'UI Customization',
icon: BiCustomize,
route: '/ui-customization',
},
{ name: 'Database', icon: RiDatabase2Line, route: '/db-cred' },
{
name: ' Security',
icon: MdSecurity,
route: '/admin-secret',
},
],
},
{ name: 'Users', icon: FiUsers, route: '/users' },
];
@@ -52,21 +119,28 @@ interface SidebarProps extends BoxProps {
export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
const { pathname } = useLocation();
const [{ fetching, data }] = useQuery({ query: MetaQuery });
const [{ data }] = useQuery({ query: MetaQuery });
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<Box
transition="3s ease"
bg={useColorModeValue('white', 'gray.900')}
borderRight="1px"
borderRightColor={useColorModeValue('gray.200', 'gray.700')}
w={{ base: 'full', md: 60 }}
w={{ base: 'full', md: '64' }}
pos="fixed"
h="full"
{...rest}
>
<Flex h="20" alignItems="center" mx="8" justifyContent="space-between">
<Flex
h="20"
alignItems="center"
mx="18"
justifyContent="space-between"
flexDirection="row"
>
<NavLink to="/">
<Flex alignItems="center">
<Flex alignItems="center" mt="6">
<Image
src="https://authorizer.dev/images/logo.png"
alt="logo"
@@ -79,17 +153,69 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
</NavLink>
<CloseButton display={{ base: 'flex', md: 'none' }} onClick={onClose} />
</Flex>
{LinkItems.map((link) => (
<NavLink key={link.name} to={link.route}>
<Accordion defaultIndex={[0]} allowMultiple>
<AccordionItem textAlign="center" border="none" w="100%">
{LinkItems.map((link) =>
link?.subRoutes ? (
<div key={link.name}>
<AccordionButton _focus={{ boxShadow: 'none' }}>
<Text as="div" fontSize="md">
<NavItem
icon={link.icon}
color={pathname === link.route ? 'blue.500' : ''}
style={{ outline: 'unset' }}
height={12}
ml={-1}
mb={isNotSmallerScreen ? -1 : -4}
w={isNotSmallerScreen ? '100%' : '310%'}
>
<Fragment>
{link.name}
<Box display={{ base: 'none', md: 'flex' }} ml={12}>
<FiChevronDown />
</Box>
</Fragment>
</NavItem>
</Text>
</AccordionButton>
<AccordionPanel>
{link.subRoutes?.map((sublink) => (
<NavLink
key={sublink.name}
to={sublink.route}
onClick={onClose}
>
{' '}
<Text as="div" fontSize="xs" ml={2}>
<NavItem
icon={sublink.icon}
color={pathname === sublink.route ? 'blue.500' : ''}
height={8}
>
{sublink.name}
</NavItem>{' '}
</Text>
</NavLink>
))}
</AccordionPanel>
</div>
) : (
<NavLink key={link.name} to={link.route}>
{' '}
<Text as="div" fontSize="md" w="100%" mt={-2}>
<NavItem
icon={link.icon}
color={pathname === link.route ? 'blue.500' : ''}
height={12}
onClick={onClose}
>
{link.name}
</NavItem>{' '}
</Text>
</NavLink>
)
)}
<Link
href="/playground"
target="_blank"
@@ -100,10 +226,14 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
>
<NavItem icon={FiCode}>API Playground</NavItem>
</Link>
</AccordionItem>
</Accordion>
{data?.meta?.version && (
<Flex alignContent="center">
{' '}
<Text
color="gray.600"
color="gray.400"
fontSize="sm"
textAlign="center"
position="absolute"
@@ -112,6 +242,7 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
>
Current Version: {data.meta.version}
</Text>
</Flex>
)}
</Box>
);
@@ -119,7 +250,7 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
interface NavItemProps extends FlexProps {
icon: IconType;
children: ReactText;
children: ReactText | JSX.Element | JSX.Element[];
}
export const NavItem = ({ icon, children, ...rest }: NavItemProps) => {
return (
@@ -167,7 +298,7 @@ export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
return (
<Flex
ml={{ base: 0, md: 60 }}
ml={{ base: 0, md: 64 }}
px={{ base: 4, md: 4 }}
height="20"
position="fixed"
@@ -204,7 +335,7 @@ export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
transition="all 0.3s"
_focus={{ boxShadow: 'none' }}
>
<HStack>
<HStack mr={5}>
<FiUser />
<VStack
display={{ base: 'none', md: 'flex' }}

View File

@@ -128,3 +128,17 @@ export interface envVarTypes {
DATABASE_URL: string;
ACCESS_TOKEN_EXPIRY_TIME: string;
}
export const envSubViews = {
INSTANCE_INFO: 'instance-info',
ROLES: 'roles',
JWT_CONFIG: 'jwt-config',
SESSION_STORAGE: 'session-storage',
EMAIL_CONFIG: 'email-config',
WHITELIST_VARIABLES: 'whitelist-variables',
ORGANIZATION_INFO: 'organization-info',
ACCESS_TOKEN: 'access-token',
UI_CUSTOMIZATION: 'ui-customization',
ADMIN_SECRET: 'admin-secret',
DB_CRED: 'db-cred',
};

View File

@@ -2,4 +2,9 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.render(
<div>
<App />
</div>,
document.getElementById('root')
);

View File

@@ -1,20 +1,28 @@
import { Box, Flex, Image, Text, Spinner } from '@chakra-ui/react';
import {
Box,
Flex,
Image,
Text,
Spinner,
useMediaQuery,
} from '@chakra-ui/react';
import React from 'react';
import { useQuery } from 'urql';
import { MetaQuery } from '../graphql/queries';
export function AuthLayout({ children }: { children: React.ReactNode }) {
const [{ fetching, data }] = useQuery({ query: MetaQuery });
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<Flex
flexWrap="wrap"
h="100%"
h="100vh"
bg="gray.100"
alignItems="center"
justifyContent="center"
flexDirection="column"
direction={['column', 'column']}
padding={['2%', '2%', '2%', '2%']}
>
<Flex alignItems="center">
<Flex alignItems="center" maxW="100%">
<Image
src="https://authorizer.dev/images/logo.png"
alt="logo"
@@ -29,7 +37,15 @@ export function AuthLayout({ children }: { children: React.ReactNode }) {
<Spinner />
) : (
<>
<Box p="6" m="5" rounded="5" bg="white" w="500px" shadow="xl">
<Box
p="6"
m="5"
rounded="5"
bg="white"
w={isNotSmallerScreen ? '500px' : '450px'}
shadow="xl"
maxW="100%"
>
{children}
</Box>
<Text color="gray.600" fontSize="sm">

View File

@@ -31,7 +31,7 @@ export function DashboardLayout({ children }: { children: ReactNode }) {
</Drawer>
{/* mobilenav */}
<MobileNav onOpen={onOpen} />
<Box ml={{ base: 0, md: 60 }} p="4" pt="24">
<Box ml={{ base: 0, md: '64' }} p="4" pt="24">
{children}
</Box>
</Box>

View File

@@ -101,10 +101,10 @@ export default function Auth() {
</FormControl>
<Button
isLoading={signUpResult.fetching || loginResult.fetching}
loadingText="Submitting"
colorScheme="blue"
size="lg"
w="100%"
d="block"
type="submit"
>
{isLogin ? 'Login' : 'Sign up'}

View File

@@ -1,46 +1,35 @@
import React, { useEffect } from 'react';
import {
Box,
Divider,
Flex,
Stack,
Center,
Text,
Button,
Input,
InputGroup,
InputRightElement,
useToast,
} from '@chakra-ui/react';
import { useParams } from 'react-router-dom';
import { Box, Flex, Stack, Button, useToast } from '@chakra-ui/react';
import { useClient } from 'urql';
import {
FaGoogle,
FaGithub,
FaFacebookF,
FaSave,
FaRegEyeSlash,
FaRegEye,
} from 'react-icons/fa';
import { FaSave } from 'react-icons/fa';
import _ from 'lodash';
import InputField from '../components/InputField';
import { EnvVariablesQuery } from '../graphql/queries';
import {
ArrayInputType,
SelectInputType,
HiddenInputType,
TextInputType,
TextAreaInputType,
SwitchInputType,
HMACEncryptionType,
RSAEncryptionType,
ECDSAEncryptionType,
envVarTypes,
envSubViews,
} from '../constants';
import { UpdateEnvVariables } from '../graphql/mutation';
import { getObjectDiff, capitalizeFirstLetter } from '../utils';
import GenerateKeysModal from '../components/GenerateKeysModal';
import OAuthConfig from '../components/EnvComponents/OAuthConfig';
import Roles from '../components/EnvComponents/Roles';
import JWTConfigurations from '../components/EnvComponents/JWTConfiguration';
import SessionStorage from '../components/EnvComponents/SessionStorage';
import EmailConfigurations from '../components/EnvComponents/EmailConfiguration';
import DomainWhiteListing from '../components/EnvComponents/DomainWhitelisting';
import OrganizationInfo from '../components/EnvComponents/OrganizationInfo';
import AccessToken from '../components/EnvComponents/AccessToken';
import UICustomization from '../components/EnvComponents/UICustomization';
import SecurityAdminSecret from '../components/EnvComponents/SecurityAdminSecret';
import DatabaseCredentials from '../components/EnvComponents/DatabaseCredentials';
export default function Environment() {
const Environment = () => {
const client = useClient();
const toast = useToast();
const [adminSecret, setAdminSecret] = React.useState<
@@ -100,11 +89,14 @@ export default function Environment() {
OLD_ADMIN_SECRET: false,
});
const { sec } = useParams();
async function getData() {
const {
data: { _env: envData },
} = await client.query(EnvVariablesQuery).toPromise();
setLoading(false);
setEnvVariables({
...envData,
OLD_ADMIN_SECRET: envData.ADMIN_SECRET,
@@ -118,7 +110,7 @@ export default function Environment() {
useEffect(() => {
getData();
}, []);
}, [sec]);
const validateAdminSecretHandler = (event: any) => {
if (envVariables.OLD_ADMIN_SECRET === event.target.value) {
@@ -200,636 +192,113 @@ export default function Environment() {
});
};
const renderComponent = (tab: any) => {
switch (tab) {
case envSubViews.INSTANCE_INFO:
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Your instance information
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Client ID</Text>
</Flex>
<Center w="70%">
<InputField
variables={envVariables}
setVariables={() => {}}
inputType={TextInputType.CLIENT_ID}
placeholder="Client ID"
readOnly={true}
<OAuthConfig
envVariables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Client Secret</Text>
</Flex>
<Center w="70%">
<InputField
);
case envSubViews.ROLES:
return (
<Roles variables={envVariables} setVariables={setEnvVariables} />
);
case envSubViews.JWT_CONFIG:
return (
<JWTConfigurations
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.CLIENT_SECRET}
placeholder="Client Secret"
readOnly={true}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<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%" />
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
paddingTop="2%"
>
<Text fontSize="md" fontWeight="bold">
JWT (JSON Web Tokens) Configurations
</Text>
<Flex>
<GenerateKeysModal
jwtType={envVariables.JWT_TYPE}
SelectInputType={SelectInputType.JWT_TYPE}
HMACEncryptionType={HMACEncryptionType}
RSAEncryptionType={RSAEncryptionType}
ECDSAEncryptionType={ECDSAEncryptionType}
getData={getData}
/>
</Flex>
</Flex>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Type:</Text>
</Flex>
<Flex w="70%">
<InputField
);
case envSubViews.SESSION_STORAGE:
return (
<SessionStorage
variables={envVariables}
setVariables={setEnvVariables}
inputType={SelectInputType.JWT_TYPE}
value={SelectInputType.JWT_TYPE}
options={{
...HMACEncryptionType,
...RSAEncryptionType,
...ECDSAEncryptionType,
}}
RedisURL={TextInputType.REDIS_URL}
/>
</Flex>
</Flex>
{Object.values(HMACEncryptionType).includes(envVariables.JWT_TYPE) ? (
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Secret</Text>
</Flex>
<Center w="70%">
<InputField
);
case envSubViews.EMAIL_CONFIG:
return (
<EmailConfigurations
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">Public Key</Text>
</Flex>
<Center w="70%">
<InputField
);
case envSubViews.WHITELIST_VARIABLES:
return (
<DomainWhiteListing
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
placeholder="Add public key here"
minH="25vh"
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Private Key</Text>
</Flex>
<Center w="70%">
<InputField
);
case envSubViews.ORGANIZATION_INFO:
return (
<OrganizationInfo
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
placeholder="Add private key here"
minH="25vh"
/>
</Center>
</Flex>
</>
)}
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Role Claim:</Text>
</Flex>
<Center w="70%">
<InputField
);
case envSubViews.ACCESS_TOKEN:
return (
<AccessToken
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
);
case envSubViews.UI_CUSTOMIZATION:
return (
<UICustomization
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">SMTP 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">SMTP 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">SMTP Password:</Text>
</Flex>
<Center w="70%">
<InputField
);
case envSubViews.ADMIN_SECRET:
return (
<SecurityAdminSecret
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.SMTP_PASSWORD}
validateAdminSecretHandler={validateAdminSecretHandler}
adminSecret={adminSecret}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">From Email:</Text>
</Flex>
<Center w="70%">
<InputField
);
case envSubViews.DB_CRED:
return (
<DatabaseCredentials
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}
);
default:
return (
<OAuthConfig
envVariables={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">
Access Token
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Access Token Expiry Time:</Text>
</Flex>
<Flex w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextInputType.ACCESS_TOKEN_EXPIRY_TIME}
placeholder="0h15m0s"
/>
</Flex>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" direction="column">
<Text fontSize="sm">Custom Scripts:</Text>
<Text fontSize="sm">Used to add custom fields in ID token</Text>
</Flex>
<Flex w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
placeholder="Add script here"
minH="25vh"
/>
</Flex>
</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>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Sign Up:</Text>
</Flex>
<Flex justifyContent="start" w="70%">
<InputField
variables={envVariables}
setVariables={setEnvVariables}
inputType={SwitchInputType.DISABLE_SIGN_UP}
/>
</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">
);
}
};
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
{renderComponent(sec)}
<Stack spacing={6} padding="1% 0" mt={4}>
<Flex justifyContent="end" alignItems="center">
<Button
leftIcon={<FaSave />}
@@ -844,4 +313,6 @@ export default function Environment() {
</Stack>
</Box>
);
}
};
export default Environment;

View File

@@ -14,6 +14,7 @@ export const AppRoutes = () => {
if (isLoggedIn) {
return (
<div>
<Suspense fallback={<></>}>
<Routes>
<Route
@@ -23,13 +24,16 @@ export const AppRoutes = () => {
</DashboardLayout>
}
>
<Route path="/" element={<Environment />} />
<Route path="/" element={<Outlet />}>
<Route index element={<Environment />} />
<Route path="/:sec" element={<Environment />} />
</Route>
<Route path="users" element={<Users />} />
<Route path="environment" element={<Environment />} />
<Route path="*" element={<Home />} />
</Route>
</Routes>
</Suspense>
</div>
);
}
return (

View File

@@ -17,4 +17,6 @@ const (
DbTypeYugabyte = "yugabyte"
// DbTypeMariaDB is the mariadb database type
DbTypeMariaDB = "mariadb"
// DbTypeCassandra is the cassandra database type
DbTypeCassandraDB = "cassandradb"
)

View File

@@ -30,6 +30,20 @@ const (
EnvKeyDatabaseURL = "DATABASE_URL"
// EnvKeyDatabaseName key for env variable DATABASE_NAME
EnvKeyDatabaseName = "DATABASE_NAME"
// EnvKeyDatabaseUsername key for env variable DATABASE_USERNAME
EnvKeyDatabaseUsername = "DATABASE_USERNAME"
// EnvKeyDatabasePassword key for env variable DATABASE_PASSWORD
EnvKeyDatabasePassword = "DATABASE_PASSWORD"
// EnvKeyDatabasePort key for env variable DATABASE_PORT
EnvKeyDatabasePort = "DATABASE_PORT"
// EnvKeyDatabaseHost key for env variable DATABASE_HOST
EnvKeyDatabaseHost = "DATABASE_HOST"
// EnvKeyDatabaseCert key for env variable DATABASE_CERT
EnvKeyDatabaseCert = "DATABASE_CERT"
// EnvKeyDatabaseCertKey key for env variable DATABASE_KEY
EnvKeyDatabaseCertKey = "DATABASE_CERT_KEY"
// EnvKeyDatabaseCACert key for env variable DATABASE_CA_CERT
EnvKeyDatabaseCACert = "DATABASE_CA_CERT"
// EnvKeySmtpHost key for env variable SMTP_HOST
EnvKeySmtpHost = "SMTP_HOST"
// EnvKeySmtpPort key for env variable SMTP_PORT

View File

@@ -1,9 +1,12 @@
package db
import (
log "github.com/sirupsen/logrus"
"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/cassandradb"
"github.com/authorizerdev/authorizer/server/db/providers/mongodb"
"github.com/authorizerdev/authorizer/server/db/providers/sql"
"github.com/authorizerdev/authorizer/server/envstore"
@@ -15,27 +18,43 @@ var Provider providers.Provider
func InitDB() error {
var err error
isSQL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeArangodb && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeMongodb
isSQL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeArangodb && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeMongodb && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) != constants.DbTypeCassandraDB
isArangoDB := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeArangodb
isMongoDB := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeMongodb
isCassandra := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType) == constants.DbTypeCassandraDB
if isSQL {
log.Info("Initializing SQL Driver for: ", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseType))
Provider, err = sql.NewProvider()
if err != nil {
log.Fatal("Failed to initialize SQL driver: ", err)
return err
}
}
if isArangoDB {
log.Info("Initializing ArangoDB Driver")
Provider, err = arangodb.NewProvider()
if err != nil {
log.Fatal("Failed to initialize ArangoDB driver: ", err)
return err
}
}
if isMongoDB {
log.Info("Initializing MongoDB Driver")
Provider, err = mongodb.NewProvider()
if err != nil {
log.Fatal("Failed to initialize MongoDB driver: ", err)
return err
}
}
if isCassandra {
log.Info("Initializing CassandraDB Driver")
Provider, err = cassandradb.NewProvider()
if err != nil {
log.Fatal("Failed to initialize CassandraDB driver: ", err)
return err
}
}

View File

@@ -1,11 +1,13 @@
package models
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
// Env model for db
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"`
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
EnvData string `gorm:"type:text" json:"env" bson:"env" cql:"env"`
Hash string `gorm:"type:text" json:"hash" bson:"hash" cql:"hash"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
}

View File

@@ -1,13 +1,15 @@
package models
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
// Session model for db
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:"-"`
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"`
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
UserID string `gorm:"type:char(36),index:" json:"user_id" bson:"user_id" cql:"user_id"`
User User `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" bson:"-" cql:"-"`
UserAgent string `json:"user_agent" bson:"user_agent" cql:"user_agent"`
IP string `json:"ip" bson:"ip" cql:"ip"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
}

View File

@@ -6,28 +6,30 @@ import (
"github.com/authorizerdev/authorizer/server/graph/model"
)
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
// User model for db
type User struct {
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
Email string `gorm:"unique" json:"email" bson:"email"`
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at"`
Password *string `gorm:"type:text" json:"password" bson:"password"`
SignupMethods string `json:"signup_methods" bson:"signup_methods"`
GivenName *string `json:"given_name" bson:"given_name"`
FamilyName *string `json:"family_name" bson:"family_name"`
MiddleName *string `json:"middle_name" bson:"middle_name"`
Nickname *string `json:"nickname" bson:"nickname"`
Gender *string `json:"gender" bson:"gender"`
Birthdate *string `json:"birthdate" bson:"birthdate"`
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number"`
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"`
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp"`
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at"`
Password *string `gorm:"type:text" json:"password" bson:"password" cql:"password"`
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name"`
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname"`
Gender *string `json:"gender" bson:"gender" cql:"gender"`
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number"`
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"`
Picture *string `gorm:"type:text" json:"picture" bson:"picture" cql:"picture"`
Roles string `json:"roles" bson:"roles" cql:"roles"`
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
}
func (user *User) AsAPIUser() *model.User {
@@ -53,8 +55,8 @@ func (user *User) AsAPIUser() *model.User {
PhoneNumberVerified: &isPhoneVerified,
Picture: user.Picture,
Roles: strings.Split(user.Roles, ","),
RevokedTimestamp: revokedTimestamp,
CreatedAt: &createdAt,
UpdatedAt: &updatedAt,
RevokedTimestamp: revokedTimestamp,
}
}

View File

@@ -2,18 +2,20 @@ package models
import "github.com/authorizerdev/authorizer/server/graph/model"
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
// VerificationRequest model for db
type VerificationRequest struct {
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
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"`
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
Nonce string `gorm:"type:text" json:"nonce" bson:"nonce"`
RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri"`
Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
Token string `gorm:"type:text" json:"token" bson:"token" cql:"jwt_token"` // token is reserved keyword in cassandra
Identifier string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(64)" json:"identifier" bson:"identifier" cql:"identifier"`
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"`
Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email"`
Nonce string `gorm:"type:text" json:"nonce" bson:"nonce" cql:"nonce"`
RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
}
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
@@ -30,10 +32,10 @@ func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequ
Token: &token,
Identifier: &identifier,
Expires: &expires,
CreatedAt: &createdAt,
UpdatedAt: &updatedAt,
Email: &email,
Nonce: &nonce,
RedirectURI: &redirectURI,
CreatedAt: &createdAt,
UpdatedAt: &updatedAt,
}
}

View File

@@ -2,7 +2,6 @@ package arangodb
import (
"fmt"
"log"
"time"
arangoDriver "github.com/arangodb/go-driver"
@@ -22,7 +21,6 @@ func (p *provider) AddEnv(env models.Env) (models.Env, error) {
configCollection, _ := p.db.Collection(nil, models.Collections.Env)
meta, err := configCollection.CreateDocument(arangoDriver.WithOverwrite(nil), env)
if err != nil {
log.Println("error adding config:", err)
return env, err
}
env.Key = meta.Key
@@ -36,7 +34,6 @@ func (p *provider) UpdateEnv(env models.Env) (models.Env, error) {
collection, _ := p.db.Collection(nil, models.Collections.Env)
meta, err := collection.UpdateDocument(nil, env.Key, env)
if err != nil {
log.Println("error updating config:", err)
return env, err
}

View File

@@ -2,7 +2,6 @@ package arangodb
import (
"context"
"log"
"github.com/arangodb/go-driver"
arangoDriver "github.com/arangodb/go-driver"
@@ -42,7 +41,6 @@ func NewProvider() (*provider, error) {
arangodb_exists, err := arangoClient.DatabaseExists(nil, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName))
if arangodb_exists {
log.Println(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName) + " db exists already")
arangodb, err = arangoClient.Database(nil, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName))
if err != nil {
return nil, err
@@ -55,12 +53,10 @@ func NewProvider() (*provider, error) {
}
userCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.User)
if userCollectionExists {
log.Println(models.Collections.User + " collection exists already")
} else {
if !userCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.User, nil)
if err != nil {
log.Println("error creating collection("+models.Collections.User+"):", err)
return nil, err
}
}
userCollection, _ := arangodb.Collection(nil, models.Collections.User)
@@ -74,12 +70,10 @@ func NewProvider() (*provider, error) {
})
verificationRequestCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.VerificationRequest)
if verificationRequestCollectionExists {
log.Println(models.Collections.VerificationRequest + " collection exists already")
} else {
if !verificationRequestCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.VerificationRequest, nil)
if err != nil {
log.Println("error creating collection("+models.Collections.VerificationRequest+"):", err)
return nil, err
}
}
@@ -93,12 +87,10 @@ func NewProvider() (*provider, error) {
})
sessionCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Session)
if sessionCollectionExists {
log.Println(models.Collections.Session + " collection exists already")
} else {
if !sessionCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.Session, nil)
if err != nil {
log.Println("error creating collection("+models.Collections.Session+"):", err)
return nil, err
}
}
@@ -108,12 +100,10 @@ func NewProvider() (*provider, error) {
})
configCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Env)
if configCollectionExists {
log.Println(models.Collections.Env + " collection exists already")
} else {
if !configCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.Env, nil)
if err != nil {
log.Println("error creating collection("+models.Collections.Env+"):", err)
return nil, err
}
}

View File

@@ -2,7 +2,6 @@ package arangodb
import (
"fmt"
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -20,7 +19,6 @@ func (p *provider) AddSession(session models.Session) error {
sessionCollection, _ := p.db.Collection(nil, models.Collections.Session)
_, err := sessionCollection.CreateDocument(nil, session)
if err != nil {
log.Println(`error saving session`, err)
return err
}
return nil

View File

@@ -3,7 +3,6 @@ package arangodb
import (
"context"
"fmt"
"log"
"strings"
"time"
@@ -31,7 +30,6 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
userCollection, _ := p.db.Collection(nil, models.Collections.User)
meta, err := userCollection.CreateDocument(arangoDriver.WithOverwrite(nil), user)
if err != nil {
log.Println("error adding user:", err)
return user, err
}
user.Key = meta.Key
@@ -46,7 +44,6 @@ func (p *provider) UpdateUser(user models.User) (models.User, error) {
collection, _ := p.db.Collection(nil, models.Collections.User)
meta, err := collection.UpdateDocument(nil, user.Key, user)
if err != nil {
log.Println("error updating user:", err)
return user, err
}
@@ -60,7 +57,6 @@ func (p *provider) DeleteUser(user models.User) error {
collection, _ := p.db.Collection(nil, models.Collections.User)
_, err := collection.RemoveDocument(nil, user.Key)
if err != nil {
log.Println(`error deleting user:`, err)
return err
}

View File

@@ -3,7 +3,6 @@ package arangodb
import (
"context"
"fmt"
"log"
"time"
"github.com/arangodb/go-driver"
@@ -23,7 +22,6 @@ func (p *provider) AddVerificationRequest(verificationRequest models.Verificatio
verificationRequestRequestCollection, _ := p.db.Collection(nil, models.Collections.VerificationRequest)
meta, err := verificationRequestRequestCollection.CreateDocument(nil, verificationRequest)
if err != nil {
log.Println("error saving verificationRequest record:", err)
return verificationRequest, err
}
verificationRequest.Key = meta.Key
@@ -136,7 +134,6 @@ func (p *provider) DeleteVerificationRequest(verificationRequest models.Verifica
collection, _ := p.db.Collection(nil, models.Collections.VerificationRequest)
_, err := collection.RemoveDocument(nil, verificationRequest.Key)
if err != nil {
log.Println(`error deleting verification request:`, err)
return err
}
return nil

View File

@@ -0,0 +1,52 @@
package cassandradb
import (
"fmt"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/gocql/gocql"
"github.com/google/uuid"
)
// 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.CreatedAt = time.Now().Unix()
env.UpdatedAt = time.Now().Unix()
insertEnvQuery := fmt.Sprintf("INSERT INTO %s (id, env, hash, created_at, updated_at) VALUES ('%s', '%s', '%s', %d, %d)", KeySpace+"."+models.Collections.Env, env.ID, env.EnvData, env.Hash, env.CreatedAt, env.UpdatedAt)
err := p.db.Query(insertEnvQuery).Exec()
if err != nil {
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()
updateEnvQuery := fmt.Sprintf("UPDATE %s SET env = '%s', updated_at = %d WHERE id = '%s'", KeySpace+"."+models.Collections.Env, env.EnvData, env.UpdatedAt, env.ID)
err := p.db.Query(updateEnvQuery).Exec()
if err != nil {
return env, err
}
return env, nil
}
// GetEnv to get environment information from database
func (p *provider) GetEnv() (models.Env, error) {
var env models.Env
query := fmt.Sprintf("SELECT id, env, hash, created_at, updated_at FROM %s LIMIT 1", KeySpace+"."+models.Collections.Env)
err := p.db.Query(query).Consistency(gocql.One).Scan(&env.ID, &env.EnvData, &env.Hash, &env.CreatedAt, &env.UpdatedAt)
if err != nil {
return env, err
}
return env, nil
}

View File

@@ -0,0 +1,168 @@
package cassandradb
import (
"crypto/tls"
"crypto/x509"
"fmt"
"strings"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/crypto"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/gocql/gocql"
cansandraDriver "github.com/gocql/gocql"
)
type provider struct {
db *cansandraDriver.Session
}
// KeySpace for the cassandra database
var KeySpace string
// NewProvider to initialize arangodb connection
func NewProvider() (*provider, error) {
dbURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseURL)
if dbURL == "" {
dbURL = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseHost)
if envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabasePort) != "" {
dbURL = fmt.Sprintf("%s:%s", dbURL, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabasePort))
}
}
KeySpace = envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseName)
clusterURL := []string{}
if strings.Contains(dbURL, ",") {
clusterURL = strings.Split(dbURL, ",")
} else {
clusterURL = append(clusterURL, dbURL)
}
cassandraClient := cansandraDriver.NewCluster(clusterURL...)
if envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseUsername) != "" && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabasePassword) != "" {
cassandraClient.Authenticator = &cansandraDriver.PasswordAuthenticator{
Username: envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseUsername),
Password: envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabasePassword),
}
}
if envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseCert) != "" && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseCACert) != "" && envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseCertKey) != "" {
certString, err := crypto.DecryptB64(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseCert))
if err != nil {
return nil, err
}
keyString, err := crypto.DecryptB64(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseCertKey))
if err != nil {
return nil, err
}
caString, err := crypto.DecryptB64(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyDatabaseCACert))
if err != nil {
return nil, err
}
cert, err := tls.X509KeyPair([]byte(certString), []byte(keyString))
if err != nil {
return nil, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM([]byte(caString))
cassandraClient.SslOpts = &cansandraDriver.SslOptions{
Config: &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
InsecureSkipVerify: true,
},
EnableHostVerification: false,
}
}
cassandraClient.RetryPolicy = &cansandraDriver.SimpleRetryPolicy{
NumRetries: 3,
}
cassandraClient.Consistency = gocql.LocalQuorum
session, err := cassandraClient.CreateSession()
if err != nil {
return nil, err
}
// Note for astra keyspaces can only be created from there console
// https://docs.datastax.com/en/astra/docs/datastax-astra-faq.html#_i_am_trying_to_create_a_keyspace_in_the_cql_shell_and_i_am_running_into_an_error_how_do_i_fix_this
getKeyspaceQuery := fmt.Sprintf("SELECT keyspace_name FROM system_schema.keyspaces;")
scanner := session.Query(getKeyspaceQuery).Iter().Scanner()
hasAuthorizerKeySpace := false
for scanner.Next() {
var keySpace string
err := scanner.Scan(&keySpace)
if err != nil {
return nil, err
}
if keySpace == KeySpace {
hasAuthorizerKeySpace = true
break
}
}
if !hasAuthorizerKeySpace {
createKeySpaceQuery := fmt.Sprintf("CREATE KEYSPACE %s WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1};", KeySpace)
err = session.Query(createKeySpaceQuery).Exec()
if err != nil {
return nil, err
}
}
// make sure collections are present
envCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, env text, hash text, updated_at bigint, created_at bigint, PRIMARY KEY (id))",
KeySpace, models.Collections.Env)
err = session.Query(envCollectionQuery).Exec()
if err != nil {
return nil, err
}
sessionCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, user_id text, user_agent text, ip text, updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.Session)
err = session.Query(sessionCollectionQuery).Exec()
if err != nil {
return nil, err
}
userCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, email text, email_verified_at bigint, password text, signup_methods text, given_name text, family_name text, middle_name text, nickname text, gender text, birthdate text, phone_number text, phone_number_verified_at bigint, picture text, roles text, updated_at bigint, created_at bigint, revoked_timestamp bigint, PRIMARY KEY (id))", KeySpace, models.Collections.User)
err = session.Query(userCollectionQuery).Exec()
if err != nil {
return nil, err
}
userIndexQuery := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_user_email ON %s.%s (email)", KeySpace, models.Collections.User)
err = session.Query(userIndexQuery).Exec()
if err != nil {
return nil, err
}
// token is reserved keyword in cassandra, hence we need to use jwt_token
verificationRequestCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, jwt_token text, identifier text, expires_at bigint, email text, nonce text, redirect_uri text, created_at bigint, updated_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.VerificationRequest)
err = session.Query(verificationRequestCollectionQuery).Exec()
if err != nil {
return nil, err
}
verificationRequestIndexQuery := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_verification_request_email ON %s.%s (email)", KeySpace, models.Collections.VerificationRequest)
err = session.Query(verificationRequestIndexQuery).Exec()
if err != nil {
return nil, err
}
verificationRequestIndexQuery = fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_verification_request_identifier ON %s.%s (identifier)", KeySpace, models.Collections.VerificationRequest)
err = session.Query(verificationRequestIndexQuery).Exec()
if err != nil {
return nil, err
}
verificationRequestIndexQuery = fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_verification_request_jwt_token ON %s.%s (jwt_token)", KeySpace, models.Collections.VerificationRequest)
err = session.Query(verificationRequestIndexQuery).Exec()
if err != nil {
return nil, err
}
return &provider{
db: session,
}, err
}

View File

@@ -0,0 +1,36 @@
package cassandradb
import (
"fmt"
"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()
insertSessionQuery := fmt.Sprintf("INSERT INTO %s (id, user_id, user_agent, ip, created_at, updated_at) VALUES ('%s', '%s', '%s', '%s', %d, %d)", KeySpace+"."+models.Collections.Session, session.ID, session.UserID, session.UserAgent, session.IP, session.CreatedAt, session.UpdatedAt)
err := p.db.Query(insertSessionQuery).Exec()
if err != nil {
return err
}
return nil
}
// DeleteSession to delete session information from database
func (p *provider) DeleteSession(userId string) error {
deleteSessionQuery := fmt.Sprintf("DELETE FROM %s WHERE user_id = '%s'", KeySpace+"."+models.Collections.Session, userId)
err := p.db.Query(deleteSessionQuery).Exec()
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,189 @@
package cassandradb
import (
"encoding/json"
"fmt"
"reflect"
"strings"
"time"
"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/gocql/gocql"
"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.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
}
user.CreatedAt = time.Now().Unix()
user.UpdatedAt = time.Now().Unix()
bytes, err := json.Marshal(user)
if err != nil {
return user, err
}
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
decoder.UseNumber()
userMap := map[string]interface{}{}
err = decoder.Decode(&userMap)
if err != nil {
return user, err
}
fields := "("
values := "("
for key, value := range userMap {
if value != nil {
if key == "_id" {
fields += "id,"
} else {
fields += key + ","
}
valueType := reflect.TypeOf(value)
if valueType.Name() == "string" {
values += fmt.Sprintf("'%s',", value.(string))
} else {
values += fmt.Sprintf("%v,", value)
}
}
}
fields = fields[:len(fields)-1] + ")"
values = values[:len(values)-1] + ")"
query := fmt.Sprintf("INSERT INTO %s %s VALUES %s IF NOT EXISTS", KeySpace+"."+models.Collections.User, fields, values)
err = p.db.Query(query).Exec()
if err != nil {
return user, err
}
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()
bytes, err := json.Marshal(user)
if err != nil {
return user, err
}
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
decoder.UseNumber()
userMap := map[string]interface{}{}
err = decoder.Decode(&userMap)
if err != nil {
return user, err
}
updateFields := ""
for key, value := range userMap {
if value != nil && key != "_id" {
}
if key == "_id" {
continue
}
if value == nil {
updateFields += fmt.Sprintf("%s = null,", key)
continue
}
valueType := reflect.TypeOf(value)
if valueType.Name() == "string" {
updateFields += fmt.Sprintf("%s = '%s', ", key, value.(string))
} else {
updateFields += fmt.Sprintf("%s = %v, ", key, value)
}
}
updateFields = strings.Trim(updateFields, " ")
updateFields = strings.TrimSuffix(updateFields, ",")
query := fmt.Sprintf("UPDATE %s SET %s WHERE id = '%s'", KeySpace+"."+models.Collections.User, updateFields, user.ID)
err = p.db.Query(query).Exec()
if err != nil {
return user, err
}
return user, nil
}
// DeleteUser to delete user information from database
func (p *provider) DeleteUser(user models.User) error {
query := fmt.Sprintf("DELETE FROM %s WHERE id = '%s'", KeySpace+"."+models.Collections.User, user.ID)
err := p.db.Query(query).Exec()
return err
}
// ListUsers to get list of users from database
func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error) {
responseUsers := []*model.User{}
paginationClone := pagination
totalCountQuery := fmt.Sprintf(`SELECT COUNT(*) FROM %s`, KeySpace+"."+models.Collections.User)
err := p.db.Query(totalCountQuery).Consistency(gocql.One).Scan(&paginationClone.Total)
if err != nil {
return nil, err
}
// there is no offset in cassandra
// so we fetch till limit + offset
// and return the results from offset to limit
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.User, pagination.Limit+pagination.Offset)
scanner := p.db.Query(query).Iter().Scanner()
counter := int64(0)
for scanner.Next() {
if counter >= pagination.Offset {
var user models.User
err := scanner.Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.CreatedAt, &user.UpdatedAt)
if err != nil {
return nil, err
}
responseUsers = append(responseUsers, user.AsAPIUser())
}
counter++
}
return &model.Users{
Users: responseUsers,
Pagination: &paginationClone,
}, nil
}
// GetUserByEmail to get user information from database using email address
func (p *provider) GetUserByEmail(email string) (models.User, error) {
var user models.User
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1", KeySpace+"."+models.Collections.User, email)
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.CreatedAt, &user.UpdatedAt)
if err != nil {
return user, err
}
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
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1", KeySpace+"."+models.Collections.User, id)
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.CreatedAt, &user.UpdatedAt)
if err != nil {
return user, err
}
return user, nil
}

View File

@@ -0,0 +1,99 @@
package cassandradb
import (
"fmt"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/gocql/gocql"
"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()
query := fmt.Sprintf("INSERT INTO %s (id, jwt_token, identifier, expires_at, email, nonce, redirect_uri, created_at, updated_at) VALUES ('%s', '%s', '%s', %d, '%s', '%s', '%s', %d, %d)", KeySpace+"."+models.Collections.VerificationRequest, verificationRequest.ID, verificationRequest.Token, verificationRequest.Identifier, verificationRequest.ExpiresAt, verificationRequest.Email, verificationRequest.Nonce, verificationRequest.RedirectURI, verificationRequest.CreatedAt, verificationRequest.UpdatedAt)
err := p.db.Query(query).Exec()
if err != nil {
return verificationRequest, err
}
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
query := fmt.Sprintf(`SELECT id, jwt_token, identifier, expires_at, email, nonce, redirect_uri, created_at, updated_at FROM %s WHERE jwt_token = '%s' LIMIT 1`, KeySpace+"."+models.Collections.VerificationRequest, token)
err := p.db.Query(query).Consistency(gocql.One).Scan(&verificationRequest.ID, &verificationRequest.Token, &verificationRequest.Identifier, &verificationRequest.ExpiresAt, &verificationRequest.Email, &verificationRequest.Nonce, &verificationRequest.RedirectURI, &verificationRequest.CreatedAt, &verificationRequest.UpdatedAt)
if err != nil {
return verificationRequest, err
}
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
query := fmt.Sprintf(`SELECT id, jwt_token, identifier, expires_at, email, nonce, redirect_uri, created_at, updated_at FROM %s WHERE email = '%s' AND identifier = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.VerificationRequest, email, identifier)
err := p.db.Query(query).Consistency(gocql.One).Scan(&verificationRequest.ID, &verificationRequest.Token, &verificationRequest.Identifier, &verificationRequest.ExpiresAt, &verificationRequest.Email, &verificationRequest.Nonce, &verificationRequest.RedirectURI, &verificationRequest.CreatedAt, &verificationRequest.UpdatedAt)
if err != nil {
return verificationRequest, err
}
return verificationRequest, nil
}
// ListVerificationRequests to get list of verification requests from database
func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model.VerificationRequests, error) {
var verificationRequests []*model.VerificationRequest
paginationClone := pagination
totalCountQuery := fmt.Sprintf(`SELECT COUNT(*) FROM %s`, KeySpace+"."+models.Collections.VerificationRequest)
err := p.db.Query(totalCountQuery).Consistency(gocql.One).Scan(&paginationClone.Total)
if err != nil {
return nil, err
}
// there is no offset in cassandra
// so we fetch till limit + offset
// and return the results from offset to limit
query := fmt.Sprintf(`SELECT id, jwt_token, identifier, expires_at, email, nonce, redirect_uri, created_at, updated_at FROM %s LIMIT %d`, KeySpace+"."+models.Collections.VerificationRequest, pagination.Limit+pagination.Offset)
scanner := p.db.Query(query).Iter().Scanner()
counter := int64(0)
for scanner.Next() {
if counter >= pagination.Offset {
var verificationRequest models.VerificationRequest
err := scanner.Scan(&verificationRequest.ID, &verificationRequest.Token, &verificationRequest.Identifier, &verificationRequest.ExpiresAt, &verificationRequest.Email, &verificationRequest.Nonce, &verificationRequest.RedirectURI, &verificationRequest.CreatedAt, &verificationRequest.UpdatedAt)
if err != nil {
return nil, err
}
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())
}
counter++
}
return &model.VerificationRequests{
VerificationRequests: verificationRequests,
Pagination: &paginationClone,
}, nil
}
// DeleteVerificationRequest to delete verification request from database
func (p *provider) DeleteVerificationRequest(verificationRequest models.VerificationRequest) error {
query := fmt.Sprintf("DELETE FROM %s WHERE id = '%s'", KeySpace+"."+models.Collections.VerificationRequest, verificationRequest.ID)
err := p.db.Query(query).Exec()
if err != nil {
return err
}
return nil
}

View File

@@ -2,7 +2,6 @@ package mongodb
import (
"fmt"
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -23,7 +22,6 @@ func (p *provider) AddEnv(env models.Env) (models.Env, error) {
configCollection := p.db.Collection(models.Collections.Env, options.Collection())
_, err := configCollection.InsertOne(nil, env)
if err != nil {
log.Println("error adding config:", err)
return env, err
}
return env, nil
@@ -35,7 +33,6 @@ func (p *provider) UpdateEnv(env models.Env) (models.Env, error) {
configCollection := p.db.Collection(models.Collections.Env, options.Collection())
_, err := configCollection.UpdateOne(nil, bson.M{"_id": bson.M{"$eq": env.ID}}, bson.M{"$set": env}, options.MergeUpdateOptions())
if err != nil {
log.Println("error updating config:", err)
return env, err
}
return env, nil

View File

@@ -1,7 +1,6 @@
package mongodb
import (
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -22,7 +21,6 @@ func (p *provider) AddSession(session models.Session) error {
sessionCollection := p.db.Collection(models.Collections.Session, options.Collection())
_, err := sessionCollection.InsertOne(nil, session)
if err != nil {
log.Println(`error saving session`, err)
return err
}
return nil
@@ -33,7 +31,6 @@ func (p *provider) DeleteSession(userId string) error {
sessionCollection := p.db.Collection(models.Collections.Session, options.Collection())
_, err := sessionCollection.DeleteMany(nil, bson.M{"user_id": userId}, options.Delete())
if err != nil {
log.Println("error deleting session:", err)
return err
}
return nil

View File

@@ -1,7 +1,6 @@
package mongodb
import (
"log"
"strings"
"time"
@@ -29,7 +28,6 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
userCollection := p.db.Collection(models.Collections.User, options.Collection())
_, err := userCollection.InsertOne(nil, user)
if err != nil {
log.Println("error adding user:", err)
return user, err
}
@@ -42,7 +40,6 @@ func (p *provider) UpdateUser(user models.User) (models.User, error) {
userCollection := p.db.Collection(models.Collections.User, options.Collection())
_, err := userCollection.UpdateOne(nil, bson.M{"_id": bson.M{"$eq": user.ID}}, bson.M{"$set": user}, options.MergeUpdateOptions())
if err != nil {
log.Println("error updating user:", err)
return user, err
}
return user, nil
@@ -53,7 +50,6 @@ func (p *provider) DeleteUser(user models.User) error {
userCollection := p.db.Collection(models.Collections.User, options.Collection())
_, err := userCollection.DeleteOne(nil, bson.M{"_id": user.ID}, options.Delete())
if err != nil {
log.Println("error deleting user:", err)
return err
}
@@ -74,7 +70,6 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
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
}
@@ -82,7 +77,6 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
cursor, err := userCollection.Find(nil, bson.M{}, opts)
if err != nil {
log.Println("error getting users:", err)
return nil, err
}
defer cursor.Close(nil)

View File

@@ -1,7 +1,6 @@
package mongodb
import (
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -22,7 +21,6 @@ func (p *provider) AddVerificationRequest(verificationRequest models.Verificatio
verificationRequestCollection := p.db.Collection(models.Collections.VerificationRequest, options.Collection())
_, err := verificationRequestCollection.InsertOne(nil, verificationRequest)
if err != nil {
log.Println("error saving verification record:", err)
return verificationRequest, err
}
}
@@ -73,7 +71,6 @@ func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model
cursor, err := verificationRequestCollection.Find(nil, bson.M{}, opts)
if err != nil {
log.Println("error getting verification requests:", err)
return nil, err
}
defer cursor.Close(nil)
@@ -98,7 +95,6 @@ func (p *provider) DeleteVerificationRequest(verificationRequest models.Verifica
verificationRequestCollection := p.db.Collection(models.Collections.VerificationRequest, options.Collection())
_, err := verificationRequestCollection.DeleteOne(nil, bson.M{"_id": verificationRequest.ID}, options.Delete())
if err != nil {
log.Println("error deleting verification request::", err)
return err
}

View File

@@ -0,0 +1,32 @@
package provider_template
import (
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/google/uuid"
)
// 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.CreatedAt = time.Now().Unix()
env.UpdatedAt = time.Now().Unix()
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,20 @@
package provider_template
import (
"gorm.io/gorm"
)
// TODO change following provider to new db provider
type provider struct {
db *gorm.DB
}
// NewProvider returns a new SQL provider
// TODO change following provider to new db provider
func NewProvider() (*provider, error) {
var sqlDB *gorm.DB
return &provider{
db: sqlDB,
}, nil
}

View File

@@ -0,0 +1,24 @@
package provider_template
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,58 @@
package provider_template
import (
"strings"
"time"
"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"
)
// 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.EnvStoreObj.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(pagination model.Pagination) (*model.Users, error) {
return nil, 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,45 @@
package provider_template
import (
"time"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"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(pagination model.Pagination) (*model.VerificationRequests, error) {
return nil, nil
}
// DeleteVerificationRequest to delete verification request from database
func (p *provider) DeleteVerificationRequest(verificationRequest models.VerificationRequest) error {
return nil
}

View File

@@ -1,7 +1,6 @@
package sql
import (
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -20,7 +19,6 @@ func (p *provider) AddEnv(env models.Env) (models.Env, error) {
result := p.db.Create(&env)
if result.Error != nil {
log.Println("error adding config:", result.Error)
return env, result.Error
}
return env, nil
@@ -32,7 +30,6 @@ func (p *provider) UpdateEnv(env models.Env) (models.Env, error) {
result := p.db.Save(&env)
if result.Error != nil {
log.Println("error updating config:", result.Error)
return env, result.Error
}
return env, nil

View File

@@ -1,7 +1,6 @@
package sql
import (
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -23,7 +22,6 @@ func (p *provider) AddSession(session models.Session) error {
DoNothing: true,
}).Create(&session)
if res.Error != nil {
log.Println(`error saving session`, res.Error)
return res.Error
}
return nil
@@ -34,7 +32,6 @@ func (p *provider) DeleteSession(userId string) error {
result := p.db.Where("user_id = ?", userId).Delete(&models.Session{})
if result.Error != nil {
log.Println(`error deleting session:`, result.Error)
return result.Error
}
return nil

View File

@@ -1,7 +1,6 @@
package sql
import (
"log"
"strings"
"time"
@@ -33,7 +32,6 @@ func (p *provider) AddUser(user models.User) (models.User, error) {
}).Create(&user)
if result.Error != nil {
log.Println("error adding user:", result.Error)
return user, result.Error
}
@@ -47,7 +45,6 @@ func (p *provider) UpdateUser(user models.User) (models.User, error) {
result := p.db.Save(&user)
if result.Error != nil {
log.Println("error updating user:", result.Error)
return user, result.Error
}
@@ -59,7 +56,6 @@ func (p *provider) DeleteUser(user models.User) error {
result := p.db.Delete(&user)
if result.Error != nil {
log.Println(`error deleting user:`, result.Error)
return result.Error
}
@@ -71,7 +67,6 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
var users []models.User
result := p.db.Limit(int(pagination.Limit)).Offset(int(pagination.Offset)).Order("created_at DESC").Find(&users)
if result.Error != nil {
log.Println("error getting users:", result.Error)
return nil, result.Error
}

View File

@@ -1,7 +1,6 @@
package sql
import (
"log"
"time"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -25,7 +24,6 @@ func (p *provider) AddVerificationRequest(verificationRequest models.Verificatio
}).Create(&verificationRequest)
if result.Error != nil {
log.Println(`error saving verification request record`, result.Error)
return verificationRequest, result.Error
}
@@ -38,7 +36,6 @@ func (p *provider) GetVerificationRequestByToken(token string) (models.Verificat
result := p.db.Where("token = ?", token).First(&verificationRequest)
if result.Error != nil {
log.Println(`error getting verification request:`, result.Error)
return verificationRequest, result.Error
}
@@ -52,7 +49,6 @@ func (p *provider) GetVerificationRequestByEmail(email string, identifier string
result := p.db.Where("email = ? AND identifier = ?", email, identifier).First(&verificationRequest)
if result.Error != nil {
log.Println(`error getting verification token:`, result.Error)
return verificationRequest, result.Error
}
@@ -65,7 +61,6 @@ func (p *provider) ListVerificationRequests(pagination model.Pagination) (*model
result := p.db.Limit(int(pagination.Limit)).Offset(int(pagination.Offset)).Order("created_at DESC").Find(&verificationRequests)
if result.Error != nil {
log.Println("error getting verification requests:", result.Error)
return nil, result.Error
}
@@ -94,7 +89,6 @@ func (p *provider) DeleteVerificationRequest(verificationRequest models.Verifica
result := p.db.Delete(&verificationRequest)
if result.Error != nil {
log.Println(`error deleting verification request:`, result.Error)
return result.Error
}

View File

@@ -4,13 +4,14 @@ import (
"bytes"
"crypto/tls"
"encoding/json"
"log"
"strconv"
"text/template"
log "github.com/sirupsen/logrus"
gomail "gopkg.in/mail.v2"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
gomail "gopkg.in/mail.v2"
)
// addEmailTemplate is used to add html template in email body
@@ -46,7 +47,7 @@ func SendMail(to []string, Subject, bodyMessage string) error {
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
if err := d.DialAndSend(m); err != nil {
log.Printf("smtp error: %s", err)
log.Debug("SMTP Failed: ", err)
return err
}
return nil

View File

@@ -1,7 +1,7 @@
package email
import (
"log"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
@@ -107,7 +107,7 @@ func InviteEmail(toEmail, token, verificationURL, redirectURI string) error {
err := SendMail(Receiver, Subject, message)
if err != nil {
log.Println("=> error sending email:", err)
log.Warn("error sending email: ", err)
}
return err
}

View File

@@ -1,7 +1,7 @@
package email
import (
"log"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
@@ -107,7 +107,7 @@ func SendVerificationMail(toEmail, token, hostname string) error {
err := SendMail(Receiver, Subject, message)
if err != nil {
log.Println("=> error sending email:", err)
log.Warn("error sending email: ", err)
}
return err
}

35
server/env/env.go vendored
View File

@@ -2,17 +2,17 @@ package env
import (
"errors"
"log"
"os"
"strings"
"github.com/google/uuid"
"github.com/joho/godotenv"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/crypto"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/joho/godotenv"
)
// InitRequiredEnv to initialize EnvData and through error if required env are not present
@@ -29,15 +29,23 @@ func InitRequiredEnv() error {
if envstore.ARG_ENV_FILE != nil && *envstore.ARG_ENV_FILE != "" {
envPath = *envstore.ARG_ENV_FILE
}
log.Info("env path: ", envPath)
err := godotenv.Load(envPath)
if err != nil {
log.Printf("using OS env instead of %s file", envPath)
log.Info("using OS env instead of %s file", envPath)
}
dbURL := os.Getenv(constants.EnvKeyDatabaseURL)
dbType := os.Getenv(constants.EnvKeyDatabaseType)
dbName := os.Getenv(constants.EnvKeyDatabaseName)
dbPort := os.Getenv(constants.EnvKeyDatabasePort)
dbHost := os.Getenv(constants.EnvKeyDatabaseHost)
dbUsername := os.Getenv(constants.EnvKeyDatabaseUsername)
dbPassword := os.Getenv(constants.EnvKeyDatabasePassword)
dbCert := os.Getenv(constants.EnvKeyDatabaseCert)
dbCertKey := os.Getenv(constants.EnvKeyDatabaseCertKey)
dbCACert := os.Getenv(constants.EnvKeyDatabaseCACert)
if strings.TrimSpace(dbType) == "" {
if envstore.ARG_DB_TYPE != nil && *envstore.ARG_DB_TYPE != "" {
@@ -45,6 +53,7 @@ func InitRequiredEnv() error {
}
if dbType == "" {
log.Debug("DATABASE_TYPE is not set")
return errors.New("invalid database type. DATABASE_TYPE is empty")
}
}
@@ -54,7 +63,8 @@ func InitRequiredEnv() error {
dbURL = strings.TrimSpace(*envstore.ARG_DB_URL)
}
if dbURL == "" {
if dbURL == "" && dbPort == "" && dbHost == "" && dbUsername == "" && dbPassword == "" {
log.Debug("DATABASE_URL is not set")
return errors.New("invalid database url. DATABASE_URL is required")
}
}
@@ -69,6 +79,14 @@ func InitRequiredEnv() error {
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseURL, dbURL)
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseType, dbType)
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseName, dbName)
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseHost, dbHost)
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabasePort, dbPort)
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseUsername, dbUsername)
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabasePassword, dbPassword)
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseCert, dbCert)
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseCertKey, dbCertKey)
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseCACert, dbCACert)
return nil
}
@@ -76,7 +94,7 @@ func InitRequiredEnv() error {
func InitAllEnv() error {
envData, err := GetEnvData()
if err != nil {
log.Println("No env data found in db, using local clone of env data")
log.Info("No env data found in db, using local clone of env data")
// get clone of current store
envData = envstore.EnvStoreObj.GetEnvStoreClone()
}
@@ -103,7 +121,6 @@ func InitAllEnv() error {
if envData.StringEnv[constants.EnvKeyEnv] == "production" {
envData.BoolEnv[constants.EnvKeyIsProd] = true
gin.SetMode(gin.ReleaseMode)
} else {
envData.BoolEnv[constants.EnvKeyIsProd] = false
}
@@ -164,6 +181,7 @@ func InitAllEnv() error {
} else {
algo = envData.StringEnv[constants.EnvKeyJwtType]
if !crypto.IsHMACA(algo) && !crypto.IsRSA(algo) && !crypto.IsECDSA(algo) {
log.Debug("Invalid JWT Algorithm")
return errors.New("invalid JWT_TYPE")
}
}
@@ -369,6 +387,7 @@ func InitAllEnv() error {
}
if len(roles) > 0 && len(defaultRoles) == 0 && len(defaultRolesEnv) > 0 {
log.Debug("Default roles not found in roles list. It can be one from ROLES only")
return errors.New(`invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`)
}

View File

@@ -2,12 +2,12 @@ package env
import (
"encoding/json"
"log"
"os"
"strconv"
"strings"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/crypto"
@@ -23,12 +23,14 @@ func GetEnvData() (envstore.Store, error) {
env, err := db.Provider.GetEnv()
// config not found in db
if err != nil {
log.Debug("Error while getting env data from db: ", err)
return result, err
}
encryptionKey := env.Hash
decryptedEncryptionKey, err := crypto.DecryptB64(encryptionKey)
if err != nil {
log.Debug("Error while decrypting encryption key: ", err)
return result, err
}
@@ -36,16 +38,19 @@ func GetEnvData() (envstore.Store, error) {
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
if err != nil {
log.Debug("Error while decrypting env data from B64: ", err)
return result, err
}
decryptedConfigs, err := crypto.DecryptAESEnv([]byte(b64DecryptedConfig))
if err != nil {
log.Debug("Error while decrypting env data from AES: ", err)
return result, err
}
err = json.Unmarshal(decryptedConfigs, &result)
if err != nil {
log.Debug("Error while unmarshalling env data: ", err)
return result, err
}
@@ -64,6 +69,7 @@ func PersistEnv() error {
encryptedConfig, err := crypto.EncryptEnvData(envstore.EnvStoreObj.GetEnvStoreClone())
if err != nil {
log.Debug("Error while encrypting env data: ", err)
return err
}
@@ -74,6 +80,7 @@ func PersistEnv() error {
env, err = db.Provider.AddEnv(env)
if err != nil {
log.Debug("Error while persisting env data to db: ", err)
return err
}
} else {
@@ -82,6 +89,7 @@ func PersistEnv() error {
encryptionKey := env.Hash
decryptedEncryptionKey, err := crypto.DecryptB64(encryptionKey)
if err != nil {
log.Debug("Error while decrypting encryption key: ", err)
return err
}
@@ -89,11 +97,13 @@ func PersistEnv() error {
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
if err != nil {
log.Debug("Error while decrypting env data from B64: ", err)
return err
}
decryptedConfigs, err := crypto.DecryptAESEnv([]byte(b64DecryptedConfig))
if err != nil {
log.Debug("Error while decrypting env data from AES: ", err)
return err
}
@@ -102,6 +112,7 @@ func PersistEnv() error {
err = json.Unmarshal(decryptedConfigs, &storeData)
if err != nil {
log.Debug("Error while unmarshalling env data: ", err)
return err
}
@@ -169,6 +180,7 @@ func PersistEnv() error {
envstore.EnvStoreObj.UpdateEnvStore(storeData)
jwk, err := crypto.GenerateJWKBasedOnEnv()
if err != nil {
log.Debug("Error while generating JWK: ", err)
return err
}
// updating jwk
@@ -177,13 +189,14 @@ func PersistEnv() error {
if hasChanged {
encryptedConfig, err := crypto.EncryptEnvData(storeData)
if err != nil {
log.Debug("Error while encrypting env data: ", err)
return err
}
env.EnvData = encryptedConfig
_, err = db.Provider.UpdateEnv(env)
if err != nil {
log.Println("error updating config:", err)
log.Debug("Failed to Update Config: ", err)
return err
}
}

View File

@@ -13,6 +13,8 @@ var (
ARG_DB_TYPE *string
// ARG_ENV_FILE is the cli arg variable for the env file
ARG_ENV_FILE *string
// ARG_LOG_LEVEL is the cli arg variable for the log level
ARG_LOG_LEVEL *string
)
// Store data structure

View File

@@ -9,6 +9,7 @@ require (
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
github.com/gocql/gocql v1.0.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0
@@ -19,6 +20,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/stretchr/testify v1.7.0
github.com/ugorji/go v1.2.6 // indirect
github.com/vektah/gqlparser/v2 v2.2.0

View File

@@ -48,6 +48,10 @@ github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e h1:Xg+hGrY2
github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:mq7Shfa/CaixoDxiyAAc5jZ6CVBAyPaNQCGS7mkj4Ho=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -106,6 +110,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gocql/gocql v1.0.0 h1:UnbTERpP72VZ/viKE1Q1gPtmLvyTZTvuAstvSRydw/c=
github.com/gocql/gocql v1.0.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -140,8 +146,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -175,6 +182,8 @@ github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -322,6 +331,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -677,6 +688,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ=

View File

@@ -61,9 +61,13 @@ type ComplexityRoot struct {
ClientSecret func(childComplexity int) int
CookieName func(childComplexity int) int
CustomAccessTokenScript func(childComplexity int) int
DatabaseHost func(childComplexity int) int
DatabaseName func(childComplexity int) int
DatabasePassword func(childComplexity int) int
DatabasePort func(childComplexity int) int
DatabaseType func(childComplexity int) int
DatabaseURL func(childComplexity int) int
DatabaseUsername func(childComplexity int) int
DefaultRoles func(childComplexity int) int
DisableBasicAuthentication func(childComplexity int) int
DisableEmailVerification func(childComplexity int) int
@@ -356,6 +360,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Env.CustomAccessTokenScript(childComplexity), true
case "Env.DATABASE_HOST":
if e.complexity.Env.DatabaseHost == nil {
break
}
return e.complexity.Env.DatabaseHost(childComplexity), true
case "Env.DATABASE_NAME":
if e.complexity.Env.DatabaseName == nil {
break
@@ -363,6 +374,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Env.DatabaseName(childComplexity), true
case "Env.DATABASE_PASSWORD":
if e.complexity.Env.DatabasePassword == nil {
break
}
return e.complexity.Env.DatabasePassword(childComplexity), true
case "Env.DATABASE_PORT":
if e.complexity.Env.DatabasePort == nil {
break
}
return e.complexity.Env.DatabasePort(childComplexity), true
case "Env.DATABASE_TYPE":
if e.complexity.Env.DatabaseType == nil {
break
@@ -377,6 +402,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Env.DatabaseURL(childComplexity), true
case "Env.DATABASE_USERNAME":
if e.complexity.Env.DatabaseUsername == nil {
break
}
return e.complexity.Env.DatabaseUsername(childComplexity), true
case "Env.DEFAULT_ROLES":
if e.complexity.Env.DefaultRoles == nil {
break
@@ -1394,6 +1426,10 @@ type Env {
DATABASE_NAME: String!
DATABASE_URL: String!
DATABASE_TYPE: String!
DATABASE_USERNAME: String!
DATABASE_PASSWORD: String!
DATABASE_HOST: String!
DATABASE_PORT: String!
CLIENT_ID: String!
CLIENT_SECRET: String!
CUSTOM_ACCESS_TOKEN_SCRIPT: String
@@ -2400,6 +2436,146 @@ func (ec *executionContext) _Env_DATABASE_TYPE(ctx context.Context, field graphq
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _Env_DATABASE_USERNAME(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Env",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.DatabaseUsername, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _Env_DATABASE_PASSWORD(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Env",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.DatabasePassword, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _Env_DATABASE_HOST(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Env",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.DatabaseHost, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _Env_DATABASE_PORT(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Env",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.DatabasePort, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _Env_CLIENT_ID(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@@ -8780,6 +8956,26 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj
if out.Values[i] == graphql.Null {
invalids++
}
case "DATABASE_USERNAME":
out.Values[i] = ec._Env_DATABASE_USERNAME(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "DATABASE_PASSWORD":
out.Values[i] = ec._Env_DATABASE_PASSWORD(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "DATABASE_HOST":
out.Values[i] = ec._Env_DATABASE_HOST(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "DATABASE_PORT":
out.Values[i] = ec._Env_DATABASE_PORT(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "CLIENT_ID":
out.Values[i] = ec._Env_CLIENT_ID(ctx, field, obj)
if out.Values[i] == graphql.Null {

View File

@@ -29,6 +29,10 @@ type Env struct {
DatabaseName string `json:"DATABASE_NAME"`
DatabaseURL string `json:"DATABASE_URL"`
DatabaseType string `json:"DATABASE_TYPE"`
DatabaseUsername string `json:"DATABASE_USERNAME"`
DatabasePassword string `json:"DATABASE_PASSWORD"`
DatabaseHost string `json:"DATABASE_HOST"`
DatabasePort string `json:"DATABASE_PORT"`
ClientID string `json:"CLIENT_ID"`
ClientSecret string `json:"CLIENT_SECRET"`
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`

View File

@@ -92,6 +92,10 @@ type Env {
DATABASE_NAME: String!
DATABASE_URL: String!
DATABASE_TYPE: String!
DATABASE_USERNAME: String!
DATABASE_PASSWORD: String!
DATABASE_HOST: String!
DATABASE_PORT: String!
CLIENT_ID: String!
CLIENT_SECRET: String!
CUSTOM_ACCESS_TOKEN_SCRIPT: String

View File

@@ -1,14 +1,15 @@
package handlers
import (
"log"
"net/http"
"strings"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
)
// State is the struct that holds authorizer url and redirect url
@@ -23,6 +24,7 @@ func AppHandler() gin.HandlerFunc {
return func(c *gin.Context) {
hostname := utils.GetHost(c)
if envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableLoginPage) {
log.Debug("Login page is disabled")
c.JSON(400, gin.H{"error": "login page is not enabled"})
return
}
@@ -43,6 +45,7 @@ func AppHandler() gin.HandlerFunc {
} else {
// validate redirect url with allowed origins
if !utils.IsValidOrigin(redirect_uri) {
log.Debug("Invalid redirect_uri")
c.JSON(400, gin.H{"error": "invalid redirect url"})
return
}
@@ -52,7 +55,7 @@ func AppHandler() gin.HandlerFunc {
if pusher := c.Writer.Pusher(); pusher != nil {
// use pusher.Push() to do server push
if err := pusher.Push("/app/build/bundle.js", nil); err != nil {
log.Printf("Failed to push: %v", err)
log.Debug("Failed to push file path: ", err)
}
}
c.HTML(http.StatusOK, "app.tmpl", gin.H{

View File

@@ -6,14 +6,16 @@ import (
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
// AuthorizeHandler is the handler for the /authorize route
@@ -48,6 +50,7 @@ func AuthorizeHandler() gin.HandlerFunc {
}
if responseMode != "query" && responseMode != "web_message" {
log.Debug("Invalid response_mode: ", responseMode)
gc.JSON(400, gin.H{"error": "invalid response mode"})
}
@@ -63,6 +66,7 @@ func AuthorizeHandler() gin.HandlerFunc {
if isQuery {
gc.Redirect(http.StatusFound, loginURL)
} else {
log.Debug("Failed to get client_id: ", clientID)
gc.HTML(http.StatusOK, template, gin.H{
"target_origin": redirectURI,
"authorization_response": map[string]interface{}{
@@ -80,6 +84,7 @@ func AuthorizeHandler() gin.HandlerFunc {
if isQuery {
gc.Redirect(http.StatusFound, loginURL)
} else {
log.Debug("Invalid client_id: ", clientID)
gc.HTML(http.StatusOK, template, gin.H{
"target_origin": redirectURI,
"authorization_response": map[string]interface{}{
@@ -97,6 +102,7 @@ func AuthorizeHandler() gin.HandlerFunc {
if isQuery {
gc.Redirect(http.StatusFound, loginURL)
} else {
log.Debug("Failed to get state: ", state)
gc.HTML(http.StatusOK, template, gin.H{
"target_origin": redirectURI,
"authorization_response": map[string]interface{}{
@@ -121,6 +127,7 @@ func AuthorizeHandler() gin.HandlerFunc {
if isQuery {
gc.Redirect(http.StatusFound, loginURL)
} else {
log.Debug("Invalid response_type: ", responseType)
gc.HTML(http.StatusOK, template, gin.H{
"target_origin": redirectURI,
"authorization_response": map[string]interface{}{
@@ -139,6 +146,7 @@ func AuthorizeHandler() gin.HandlerFunc {
if isQuery {
gc.Redirect(http.StatusFound, loginURL)
} else {
log.Debug("Failed to get code_challenge: ", codeChallenge)
gc.HTML(http.StatusBadRequest, template, gin.H{
"target_origin": redirectURI,
"authorization_response": map[string]interface{}{

View File

@@ -3,9 +3,11 @@ package handlers
import (
"encoding/json"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/gin-gonic/gin"
)
func JWKsHandler() gin.HandlerFunc {
@@ -14,6 +16,7 @@ func JWKsHandler() gin.HandlerFunc {
jwk := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJWK)
err := json.Unmarshal([]byte(jwk), &data)
if err != nil {
log.Debug("Failed to parse JWK: ", err)
c.JSON(500, gin.H{
"error": err.Error(),
})

View File

@@ -4,10 +4,12 @@ import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/crypto"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/gin-gonic/gin"
)
// Handler to logout user
@@ -17,6 +19,7 @@ func LogoutHandler() gin.HandlerFunc {
// get fingerprint hash
fingerprintHash, err := cookie.GetSession(gc)
if err != nil {
log.Debug("Failed to get session: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": err.Error(),
})
@@ -25,6 +28,7 @@ func LogoutHandler() gin.HandlerFunc {
decryptedFingerPrint, err := crypto.DecryptAES(fingerprintHash)
if err != nil {
log.Debug("Failed to decrypt fingerprint: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": err.Error(),
})

View File

@@ -5,12 +5,16 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strconv"
"strings"
"time"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db"
@@ -20,9 +24,6 @@ import (
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/gin-gonic/gin"
"golang.org/x/oauth2"
)
// OAuthCallbackHandler handles the OAuth callback for various oauth providers
@@ -33,6 +34,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
sessionState := sessionstore.GetState(state)
if sessionState == "" {
log.Debug("Invalid oauth state: ", state)
c.JSON(400, gin.H{"error": "invalid oauth state"})
}
sessionstore.GetState(state)
@@ -40,6 +42,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
sessionSplit := strings.Split(state, "___")
if len(sessionSplit) < 3 {
log.Debug("Unable to get redirect url from state: ", state)
c.JSON(400, gin.H{"error": "invalid redirect url"})
return
}
@@ -60,18 +63,22 @@ func OAuthCallbackHandler() gin.HandlerFunc {
case constants.SignupMethodFacebook:
user, err = processFacebookUserInfo(code)
default:
log.Info("Invalid oauth provider")
err = fmt.Errorf(`invalid oauth provider`)
}
if err != nil {
log.Debug("Failed to process user info: ", err)
c.JSON(400, gin.H{"error": err.Error()})
return
}
existingUser, err := db.Provider.GetUserByEmail(user.Email)
log := log.WithField("user", user.Email)
if err != nil {
if envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableSignUp) {
log.Debug("Failed to signup as disabled")
c.JSON(400, gin.H{"error": "signup is disabled for this instance"})
return
}
@@ -86,6 +93,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
}
if hasProtectedRole {
log.Debug("Signup is not allowed with protected roles:", inputRoles)
c.JSON(400, gin.H{"error": "invalid role"})
return
}
@@ -96,6 +104,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
user, _ = db.Provider.AddUser(user)
} else {
if user.RevokedTimestamp != nil {
log.Debug("User access revoked at: ", user.RevokedTimestamp)
c.JSON(400, gin.H{"error": "user access has been revoked"})
}
@@ -137,6 +146,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
}
if hasProtectedRole {
log.Debug("Invalid role. User is using protected unassigned role")
c.JSON(400, gin.H{"error": "invalid role"})
return
} else {
@@ -148,6 +158,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
user, err = db.Provider.UpdateUser(user)
if err != nil {
log.Debug("Failed to update user: ", err)
c.JSON(500, gin.H{"error": err.Error()})
return
}
@@ -155,6 +166,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
authToken, err := token.CreateAuthToken(c, user, inputRoles, scopes)
if err != nil {
log.Debug("Failed to create auth token: ", err)
c.JSON(500, gin.H{"error": err.Error()})
}
@@ -174,7 +186,11 @@ func OAuthCallbackHandler() gin.HandlerFunc {
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
}
go utils.SaveSessionInDB(c, user.ID)
go db.Provider.AddSession(models.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(c.Request),
IP: utils.GetIP(c.Request),
})
if strings.Contains(redirectURL, "?") {
redirectURL = redirectURL + "&" + params
} else {
@@ -190,6 +206,7 @@ func processGoogleUserInfo(code string) (models.User, error) {
ctx := context.Background()
oauth2Token, err := oauth.OAuthProviders.GoogleConfig.Exchange(ctx, code)
if err != nil {
log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid google exchange code: %s", err.Error())
}
@@ -198,16 +215,19 @@ func processGoogleUserInfo(code string) (models.User, error) {
// Extract the ID Token from OAuth2 token.
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok {
log.Debug("Failed to extract ID Token from OAuth2 token")
return user, fmt.Errorf("unable to extract id_token")
}
// Parse and verify ID Token payload.
idToken, err := verifier.Verify(ctx, rawIDToken)
if err != nil {
log.Debug("Failed to verify ID Token: ", err)
return user, fmt.Errorf("unable to verify id_token: %s", err.Error())
}
if err := idToken.Claims(&user); err != nil {
log.Debug("Failed to parse ID Token claims: ", err)
return user, fmt.Errorf("unable to extract claims")
}
@@ -218,11 +238,13 @@ func processGithubUserInfo(code string) (models.User, error) {
user := models.User{}
token, err := oauth.OAuthProviders.GithubConfig.Exchange(oauth2.NoContext, code)
if err != nil {
log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid github exchange code: %s", err.Error())
}
client := http.Client{}
req, err := http.NewRequest("GET", constants.GithubUserInfoURL, nil)
if err != nil {
log.Debug("Failed to create github user info request: ", err)
return user, fmt.Errorf("error creating github user info request: %s", err.Error())
}
req.Header = http.Header{
@@ -231,12 +253,14 @@ func processGithubUserInfo(code string) (models.User, error) {
response, err := client.Do(req)
if err != nil {
log.Debug("Failed to request github user info: ", err)
return user, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read github user info response body: ", err)
return user, fmt.Errorf("failed to read github response body: %s", err.Error())
}
@@ -259,7 +283,7 @@ func processGithubUserInfo(code string) (models.User, error) {
GivenName: &firstName,
FamilyName: &lastName,
Picture: &picture,
Email: userRawData["sub"],
Email: userRawData["email"],
}
return user, nil
@@ -269,23 +293,26 @@ func processFacebookUserInfo(code string) (models.User, error) {
user := models.User{}
token, err := oauth.OAuthProviders.FacebookConfig.Exchange(oauth2.NoContext, code)
if err != nil {
log.Debug("Invalid facebook exchange code: ", err)
return user, fmt.Errorf("invalid facebook exchange code: %s", err.Error())
}
client := http.Client{}
req, err := http.NewRequest("GET", constants.FacebookUserInfoURL+token.AccessToken, nil)
if err != nil {
log.Debug("Error creating facebook user info request: ", err)
return user, fmt.Errorf("error creating facebook user info request: %s", err.Error())
}
response, err := client.Do(req)
if err != nil {
log.Println("error processing facebook user info:", err)
log.Debug("Failed to process facebook user: ", err)
return user, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Debug("Failed to read facebook response: ", err)
return user, fmt.Errorf("failed to read facebook response body: %s", err.Error())
}

View File

@@ -4,12 +4,14 @@ import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
)
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
@@ -26,6 +28,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
scopeString := strings.TrimSpace(c.Query("scope"))
if redirectURI == "" {
log.Debug("redirect_uri is empty")
c.JSON(400, gin.H{
"error": "invalid redirect uri",
})
@@ -33,6 +36,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
}
if state == "" {
log.Debug("state is empty")
c.JSON(400, gin.H{
"error": "invalid state",
})
@@ -52,7 +56,8 @@ func OAuthLoginHandler() gin.HandlerFunc {
// use protected roles verification for admin login only.
// though if not associated with user, it will be rejected from oauth_callback
if !utils.IsValidRoles(append([]string{}, append(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyRoles), envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyProtectedRoles)...)...), rolesSplit) {
if !utils.IsValidRoles(rolesSplit, append([]string{}, append(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyRoles), envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyProtectedRoles)...)...)) {
log.Debug("Invalid roles: ", roles)
c.JSON(400, gin.H{
"error": "invalid role",
})
@@ -69,6 +74,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
switch provider {
case constants.SignupMethodGoogle:
if oauth.OAuthProviders.GoogleConfig == nil {
log.Debug("Google OAuth provider is not configured")
isProviderConfigured = false
break
}
@@ -79,6 +85,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
c.Redirect(http.StatusTemporaryRedirect, url)
case constants.SignupMethodGithub:
if oauth.OAuthProviders.GithubConfig == nil {
log.Debug("Github OAuth provider is not configured")
isProviderConfigured = false
break
}
@@ -88,6 +95,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
c.Redirect(http.StatusTemporaryRedirect, url)
case constants.SignupMethodFacebook:
if oauth.OAuthProviders.FacebookConfig == nil {
log.Debug("Facebook OAuth provider is not configured")
isProviderConfigured = false
break
}
@@ -96,6 +104,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url)
default:
log.Debug("Invalid oauth provider: ", provider)
c.JSON(422, gin.H{
"message": "Invalid oauth provider",
})

View File

@@ -4,10 +4,12 @@ import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/gin-gonic/gin"
)
// Revoke handler to revoke refresh token
@@ -15,6 +17,7 @@ func RevokeHandler() gin.HandlerFunc {
return func(gc *gin.Context) {
var reqBody map[string]string
if err := gc.BindJSON(&reqBody); err != nil {
log.Debug("Error binding JSON: ", err)
gc.JSON(http.StatusBadRequest, gin.H{
"error": "error_binding_json",
"error_description": err.Error(),
@@ -26,6 +29,7 @@ func RevokeHandler() gin.HandlerFunc {
clientID := strings.TrimSpace(reqBody["client_id"])
if clientID == "" {
log.Debug("Client ID is empty")
gc.JSON(http.StatusBadRequest, gin.H{
"error": "client_id_required",
"error_description": "The client id is required",
@@ -34,6 +38,7 @@ func RevokeHandler() gin.HandlerFunc {
}
if clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
log.Debug("Client ID is invalid: ", clientID)
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_client_id",
"error_description": "The client id is invalid",

View File

@@ -7,13 +7,15 @@ import (
"strings"
"time"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/gin-gonic/gin"
)
// TokenHandler to handle /oauth/token requests
@@ -22,6 +24,7 @@ func TokenHandler() gin.HandlerFunc {
return func(gc *gin.Context) {
var reqBody map[string]string
if err := gc.BindJSON(&reqBody); err != nil {
log.Debug("Error binding JSON: ", err)
gc.JSON(http.StatusBadRequest, gin.H{
"error": "error_binding_json",
"error_description": err.Error(),
@@ -43,6 +46,7 @@ func TokenHandler() gin.HandlerFunc {
isAuthorizationCodeGrant := grantType == "authorization_code"
if !isRefreshTokenGrant && !isAuthorizationCodeGrant {
log.Debug("Invalid grant type: ", grantType)
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_grant_type",
"error_description": "grant_type is invalid",
@@ -50,6 +54,7 @@ func TokenHandler() gin.HandlerFunc {
}
if clientID == "" {
log.Debug("Client ID is empty")
gc.JSON(http.StatusBadRequest, gin.H{
"error": "client_id_required",
"error_description": "The client id is required",
@@ -58,6 +63,7 @@ func TokenHandler() gin.HandlerFunc {
}
if clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
log.Debug("Client ID is invalid: ", clientID)
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_client_id",
"error_description": "The client id is invalid",
@@ -70,6 +76,7 @@ func TokenHandler() gin.HandlerFunc {
if isAuthorizationCodeGrant {
if codeVerifier == "" {
log.Debug("Code verifier is empty")
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_code_verifier",
"error_description": "The code verifier is required",
@@ -78,6 +85,7 @@ func TokenHandler() gin.HandlerFunc {
}
if code == "" {
log.Debug("Code is empty")
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_code",
"error_description": "The code is required",
@@ -92,6 +100,7 @@ func TokenHandler() gin.HandlerFunc {
encryptedCode = strings.ReplaceAll(encryptedCode, "=", "")
sessionData := sessionstore.GetState(encryptedCode)
if sessionData == "" {
log.Debug("Session data is empty")
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_code_verifier",
"error_description": "The code verifier is invalid",
@@ -104,6 +113,7 @@ func TokenHandler() gin.HandlerFunc {
sessionDataSplit := strings.Split(sessionData, "@")
if sessionDataSplit[0] != code {
log.Debug("Invalid code verifier. Unable to split session data")
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_code_verifier",
"error_description": "The code verifier is invalid",
@@ -114,6 +124,7 @@ func TokenHandler() gin.HandlerFunc {
// validate session
claims, err := token.ValidateBrowserSession(gc, sessionDataSplit[1])
if err != nil {
log.Debug("Error validating session: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"error_description": "Invalid session data",
@@ -128,6 +139,7 @@ func TokenHandler() gin.HandlerFunc {
} else {
// validate refresh token
if refreshToken == "" {
log.Debug("Refresh token is empty")
gc.JSON(http.StatusBadRequest, gin.H{
"error": "invalid_refresh_token",
"error_description": "The refresh token is invalid",
@@ -136,6 +148,7 @@ func TokenHandler() gin.HandlerFunc {
claims, err := token.ValidateRefreshToken(gc, refreshToken)
if err != nil {
log.Debug("Error validating refresh token: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"error_description": err.Error(),
@@ -156,6 +169,7 @@ func TokenHandler() gin.HandlerFunc {
user, err := db.Provider.GetUserByID(userID)
if err != nil {
log.Debug("Error getting user: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"error_description": "User not found",
@@ -165,6 +179,7 @@ func TokenHandler() gin.HandlerFunc {
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
if err != nil {
log.Debug("Error creating auth token: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"error_description": "User not found",

View File

@@ -3,15 +3,18 @@ package handlers
import (
"net/http"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/token"
"github.com/gin-gonic/gin"
)
func UserInfoHandler() gin.HandlerFunc {
return func(gc *gin.Context) {
accessToken, err := token.GetAccessToken(gc)
if err != nil {
log.Debug("Error getting access token: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": err.Error(),
})
@@ -20,6 +23,7 @@ func UserInfoHandler() gin.HandlerFunc {
claims, err := token.ValidateAccessToken(gc, accessToken)
if err != nil {
log.Debug("Error validating access token: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": err.Error(),
})
@@ -29,6 +33,7 @@ func UserInfoHandler() gin.HandlerFunc {
userID := claims["sub"].(string)
user, err := db.Provider.GetUserByID(userID)
if err != nil {
log.Debug("Error getting user: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": err.Error(),
})

View File

@@ -6,12 +6,15 @@ import (
"strings"
"time"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
)
// VerifyEmailHandler handles the verify email route.
@@ -23,12 +26,14 @@ func VerifyEmailHandler() gin.HandlerFunc {
}
tokenInQuery := c.Query("token")
if tokenInQuery == "" {
log.Debug("Token is empty")
c.JSON(400, errorRes)
return
}
verificationRequest, err := db.Provider.GetVerificationRequestByToken(tokenInQuery)
if err != nil {
log.Debug("Error getting verification request: ", err)
errorRes["error_description"] = err.Error()
c.JSON(400, errorRes)
return
@@ -38,6 +43,7 @@ func VerifyEmailHandler() gin.HandlerFunc {
hostname := utils.GetHost(c)
claim, err := token.ParseJWTToken(tokenInQuery, hostname, verificationRequest.Nonce, verificationRequest.Email)
if err != nil {
log.Debug("Error parsing token: ", err)
errorRes["error_description"] = err.Error()
c.JSON(400, errorRes)
return
@@ -45,6 +51,7 @@ func VerifyEmailHandler() gin.HandlerFunc {
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
if err != nil {
log.Debug("Error getting user: ", err)
errorRes["error_description"] = err.Error()
c.JSON(400, errorRes)
return
@@ -78,6 +85,7 @@ func VerifyEmailHandler() gin.HandlerFunc {
}
authToken, err := token.CreateAuthToken(c, user, roles, scope)
if err != nil {
log.Debug("Error creating auth token: ", err)
errorRes["error_description"] = err.Error()
c.JSON(500, errorRes)
return
@@ -109,7 +117,11 @@ func VerifyEmailHandler() gin.HandlerFunc {
redirectURL = redirectURL + "?" + params
}
go utils.SaveSessionInDB(c, user.ID)
go db.Provider.AddSession(models.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(c.Request),
IP: utils.GetIP(c.Request),
})
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
}

View File

@@ -2,7 +2,9 @@ package main
import (
"flag"
"log"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
@@ -15,13 +17,51 @@ import (
var VERSION string
type LogUTCFormatter struct {
log.Formatter
}
func (u LogUTCFormatter) Format(e *log.Entry) ([]byte, error) {
e.Time = e.Time.UTC()
return u.Formatter.Format(e)
}
func main() {
envstore.ARG_DB_URL = flag.String("database_url", "", "Database connection string")
envstore.ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite")
envstore.ARG_ENV_FILE = flag.String("env_file", "", "Env file path")
envstore.ARG_LOG_LEVEL = flag.String("log_level", "info", "Log level, possible values are debug,info,warn,error,fatal,panic")
flag.Parse()
log.Println("=> version:", VERSION)
// global log level
logrus.SetFormatter(LogUTCFormatter{&logrus.JSONFormatter{}})
logrus.SetReportCaller(true)
// log instance for gin server
log := logrus.New()
log.SetFormatter(LogUTCFormatter{&logrus.JSONFormatter{}})
log.SetReportCaller(true)
var logLevel logrus.Level
switch *envstore.ARG_LOG_LEVEL {
case "debug":
logLevel = logrus.DebugLevel
case "info":
logLevel = logrus.InfoLevel
case "warn":
logLevel = logrus.WarnLevel
case "error":
logLevel = logrus.ErrorLevel
case "fatal":
logLevel = logrus.FatalLevel
case "panic":
logLevel = logrus.PanicLevel
default:
logLevel = logrus.InfoLevel
}
logrus.SetLevel(logLevel)
log.SetLevel(logLevel)
constants.VERSION = VERSION
// initialize required envs (mainly db & env file path)
@@ -61,6 +101,7 @@ func main() {
log.Fatalln("Error while initializing oauth: ", err)
}
router := routes.InitRouter()
router := routes.InitRouter(log)
log.Info("Starting Authorizer: ", VERSION)
router.Run(":" + envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyPort))
}

View File

@@ -15,7 +15,7 @@ func CORSMiddleware() gin.HandlerFunc {
}
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, X-authorizer-url")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
if c.Request.Method == "OPTIONS" {

78
server/middlewares/log.go Normal file
View File

@@ -0,0 +1,78 @@
package middlewares
import (
"fmt"
"math"
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
var timeFormat = "02/Jan/2006:15:04:05 -0700"
// Logger is the logrus logger handler
func Logger(logger logrus.FieldLogger, notLogged ...string) gin.HandlerFunc {
hostname, err := os.Hostname()
if err != nil {
hostname = "unknown"
}
var skip map[string]struct{}
if length := len(notLogged); length > 0 {
skip = make(map[string]struct{}, length)
for _, p := range notLogged {
skip[p] = struct{}{}
}
}
return func(c *gin.Context) {
// other handler can change c.Path so:
path := c.Request.URL.Path
start := time.Now()
c.Next()
stop := time.Since(start)
latency := int(math.Ceil(float64(stop.Nanoseconds()) / 1000000.0))
statusCode := c.Writer.Status()
clientIP := c.ClientIP()
clientUserAgent := c.Request.UserAgent()
referer := c.Request.Referer()
dataLength := c.Writer.Size()
if dataLength < 0 {
dataLength = 0
}
if _, ok := skip[path]; ok {
return
}
entry := logger.WithFields(logrus.Fields{
"hostname": hostname,
"statusCode": statusCode,
"latency": latency, // time to process
"clientIP": clientIP,
"method": c.Request.Method,
"path": path,
"referer": referer,
"dataLength": dataLength,
"userAgent": clientUserAgent,
})
if len(c.Errors) > 0 {
entry.Error(c.Errors.ByType(gin.ErrorTypePrivate).String())
} else {
msg := fmt.Sprintf("%s - %s [%s] \"%s %s\" %d %d \"%s\" \"%s\" (%dms)", clientIP, hostname, time.Now().Format(timeFormat), c.Request.Method, path, statusCode, dataLength, referer, clientUserAgent, latency)
if statusCode >= http.StatusInternalServerError {
entry.Error(msg)
} else if statusCode >= http.StatusBadRequest {
entry.Warn(msg)
} else {
entry.Info(msg)
}
}
}
}

View File

@@ -3,12 +3,13 @@ package oauth
import (
"context"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/oauth2"
facebookOAuth2 "golang.org/x/oauth2/facebook"
githubOAuth2 "golang.org/x/oauth2/github"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
)
// OAuthProviders is a struct that contains reference all the OAuth providers

View File

@@ -4,6 +4,8 @@ import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/crypto"
@@ -14,15 +16,17 @@ import (
// AdminLoginResolver is a resolver for admin login mutation
func AdminLoginResolver(ctx context.Context, params model.AdminLoginInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
adminSecret := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret)
if params.AdminSecret != adminSecret {
log.Debug("Admin secret is not correct")
return res, fmt.Errorf(`invalid admin secret`)
}

View File

@@ -4,6 +4,8 @@ import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/token"
@@ -12,14 +14,16 @@ import (
// AdminLogoutResolver is a resolver for admin logout mutation
func AdminLogoutResolver(ctx context.Context) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
if !token.IsSuperAdmin(gc) {
log.Debug("Admin is not logged in")
return res, fmt.Errorf("unauthorized")
}

View File

@@ -4,6 +4,8 @@ import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/crypto"
@@ -15,19 +17,22 @@ import (
// AdminSessionResolver is a resolver for admin session query
func AdminSessionResolver(ctx context.Context) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
if !token.IsSuperAdmin(gc) {
log.Debug("Not logged in as super admin")
return res, fmt.Errorf("unauthorized")
}
hashedKey, err := crypto.EncryptPassword(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret))
if err != nil {
log.Debug("Failed to encrypt key: ", err)
return res, err
}
cookie.SetAdminCookie(gc, hashedKey)

View File

@@ -6,6 +6,8 @@ import (
"fmt"
"strings"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/crypto"
@@ -17,19 +19,22 @@ import (
// AdminSignupResolver is a resolver for admin signup mutation
func AdminSignupResolver(ctx context.Context, params model.AdminSignupInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
if strings.TrimSpace(params.AdminSecret) == "" {
log.Debug("Admin secret is empty")
err = fmt.Errorf("please select secure admin secret")
return res, err
}
if len(params.AdminSecret) < 6 {
log.Debug("Admin secret is too short")
err = fmt.Errorf("admin secret must be at least 6 characters")
return res, err
}
@@ -37,6 +42,7 @@ func AdminSignupResolver(ctx context.Context, params model.AdminSignupInput) (*m
adminSecret := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret)
if adminSecret != "" {
log.Debug("Admin secret is already set")
err = fmt.Errorf("admin sign up already completed")
return res, err
}
@@ -47,30 +53,36 @@ func AdminSignupResolver(ctx context.Context, params model.AdminSignupInput) (*m
jsonBytes, err := json.Marshal(envstore.EnvStoreObj.GetEnvStoreClone())
if err != nil {
log.Debug("Failed to marshal envstore: ", err)
return res, err
}
if err := json.Unmarshal(jsonBytes, &storeData); err != nil {
log.Debug("Failed to unmarshal envstore: ", err)
return res, err
}
env, err := db.Provider.GetEnv()
if err != nil {
log.Debug("Failed to get env: ", err)
return res, err
}
envData, err := crypto.EncryptEnvData(storeData)
if err != nil {
log.Debug("Failed to encrypt envstore: ", err)
return res, err
}
env.EnvData = envData
if _, err := db.Provider.UpdateEnv(env); err != nil {
log.Debug("Failed to update env: ", err)
return res, err
}
hashedKey, err := crypto.EncryptPassword(params.AdminSecret)
if err != nil {
log.Debug("Failed to encrypt admin session key: ", err)
return res, err
}
cookie.SetAdminCookie(gc, hashedKey)

View File

@@ -3,7 +3,8 @@ package resolvers
import (
"context"
"fmt"
"log"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
@@ -14,18 +15,26 @@ import (
// DeleteUserResolver is a resolver for delete user mutation
func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
if !token.IsSuperAdmin(gc) {
log.Debug("Not logged in as super admin")
return res, fmt.Errorf("unauthorized")
}
log := log.WithFields(log.Fields{
"email": params.Email,
})
user, err := db.Provider.GetUserByEmail(params.Email)
if err != nil {
log.Debug("Failed to get user from DB: ", err)
return res, err
}
@@ -33,7 +42,7 @@ func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*mod
err = db.Provider.DeleteUser(user)
if err != nil {
log.Println("error deleting user:", err)
log.Debug("Failed to delete user: ", err)
return res, err
}

View File

@@ -3,7 +3,8 @@ package resolvers
import (
"context"
"fmt"
"log"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
@@ -13,18 +14,26 @@ import (
// EnableAccessResolver is a resolver for enabling user access
func EnableAccessResolver(ctx context.Context, params model.UpdateAccessInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
if !token.IsSuperAdmin(gc) {
log.Debug("Not logged in as super admin.")
return res, fmt.Errorf("unauthorized")
}
log := log.WithFields(log.Fields{
"user_id": params.UserID,
})
user, err := db.Provider.GetUserByID(params.UserID)
if err != nil {
log.Debug("Failed to get user from DB: ", err)
return res, err
}
@@ -32,7 +41,7 @@ func EnableAccessResolver(ctx context.Context, params model.UpdateAccessInput) (
user, err = db.Provider.UpdateUser(user)
if err != nil {
log.Println("error updating user:", err)
log.Debug("Failed to update user: ", err)
return res, err
}

View File

@@ -4,6 +4,8 @@ import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
@@ -14,14 +16,16 @@ import (
// EnvResolver is a resolver for config query
// This is admin only query
func EnvResolver(ctx context.Context) (*model.Env, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Env
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
if !token.IsSuperAdmin(gc) {
log.Debug("Not logged in as super admin.")
return res, fmt.Errorf("unauthorized")
}
@@ -34,6 +38,10 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
databaseURL := store.StringEnv[constants.EnvKeyDatabaseURL]
databaseName := store.StringEnv[constants.EnvKeyDatabaseName]
databaseType := store.StringEnv[constants.EnvKeyDatabaseType]
databaseUsername := store.StringEnv[constants.EnvKeyDatabaseUsername]
databasePassword := store.StringEnv[constants.EnvKeyDatabasePassword]
databaseHost := store.StringEnv[constants.EnvKeyDatabaseHost]
databasePort := store.StringEnv[constants.EnvKeyDatabasePort]
customAccessTokenScript := store.StringEnv[constants.EnvKeyCustomAccessTokenScript]
smtpHost := store.StringEnv[constants.EnvKeySmtpHost]
smtpPort := store.StringEnv[constants.EnvKeySmtpPort]
@@ -77,6 +85,10 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
DatabaseName: databaseName,
DatabaseURL: databaseURL,
DatabaseType: databaseType,
DatabaseUsername: databaseUsername,
DatabasePassword: databasePassword,
DatabaseHost: databaseHost,
DatabasePort: databasePort,
ClientID: clientID,
ClientSecret: clientSecret,
CustomAccessTokenScript: &customAccessTokenScript,

View File

@@ -3,10 +3,11 @@ package resolvers
import (
"context"
"fmt"
"log"
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -19,28 +20,38 @@ import (
// ForgotPasswordResolver is a resolver for forgot password mutation
func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
if envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication) {
log.Debug("Basic authentication is disabled")
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
}
params.Email = strings.ToLower(params.Email)
if !utils.IsValidEmail(params.Email) {
log.Debug("Invalid email address: ", params.Email)
return res, fmt.Errorf("invalid email")
}
log := log.WithFields(log.Fields{
"email": params.Email,
})
_, err = db.Provider.GetUserByEmail(params.Email)
if err != nil {
log.Debug("User not found: ", err)
return res, fmt.Errorf(`user with this email not found`)
}
hostname := utils.GetHost(gc)
_, nonceHash, err := utils.GenerateNonce()
if err != nil {
log.Debug("Failed to generate nonce: ", err)
return res, err
}
redirectURL := utils.GetAppURL(gc) + "/reset-password"
@@ -50,9 +61,10 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL)
if err != nil {
log.Println(`error generating token`, err)
log.Debug("Failed to create verification token", err)
return res, err
}
db.Provider.AddVerificationRequest(models.VerificationRequest{
_, err = db.Provider.AddVerificationRequest(models.VerificationRequest{
Token: verificationToken,
Identifier: constants.VerificationTypeForgotPassword,
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
@@ -60,6 +72,10 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
Nonce: nonceHash,
RedirectURI: redirectURL,
})
if err != nil {
log.Debug("Failed to add verification request", err)
return res, err
}
// exec it as go routin so that we can reduce the api latency
go email.SendForgotPasswordMail(params.Email, verificationToken, hostname)

View File

@@ -10,16 +10,19 @@ import (
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
log "github.com/sirupsen/logrus"
)
// GenerateJWTKeysResolver mutation to generate new jwt keys
func GenerateJWTKeysResolver(ctx context.Context, params model.GenerateJWTKeysInput) (*model.GenerateJWTKeysResponse, error) {
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return nil, err
}
if !token.IsSuperAdmin(gc) {
log.Debug("Not logged in as super admin")
return nil, fmt.Errorf("unauthorized")
}
@@ -27,6 +30,7 @@ func GenerateJWTKeysResolver(ctx context.Context, params model.GenerateJWTKeysIn
if crypto.IsHMACA(params.Type) {
secret, _, err := crypto.NewHMACKey(params.Type, clientID)
if err != nil {
log.Debug("Failed to generate new HMAC key: ", err)
return nil, err
}
return &model.GenerateJWTKeysResponse{
@@ -37,6 +41,7 @@ func GenerateJWTKeysResolver(ctx context.Context, params model.GenerateJWTKeysIn
if crypto.IsRSA(params.Type) {
_, privateKey, publicKey, _, err := crypto.NewRSAKey(params.Type, clientID)
if err != nil {
log.Debug("Failed to generate new RSA key: ", err)
return nil, err
}
return &model.GenerateJWTKeysResponse{
@@ -48,6 +53,7 @@ func GenerateJWTKeysResolver(ctx context.Context, params model.GenerateJWTKeysIn
if crypto.IsECDSA(params.Type) {
_, privateKey, publicKey, _, err := crypto.NewECDSAKey(params.Type, clientID)
if err != nil {
log.Debug("Failed to generate new ECDSA key: ", err)
return nil, err
}
return &model.GenerateJWTKeysResponse{
@@ -56,5 +62,6 @@ func GenerateJWTKeysResolver(ctx context.Context, params model.GenerateJWTKeysIn
}, nil
}
log.Debug("Invalid algorithm: ", params.Type)
return nil, fmt.Errorf("invalid algorithm")
}

View File

@@ -4,10 +4,11 @@ import (
"context"
"errors"
"fmt"
"log"
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
@@ -22,19 +23,23 @@ import (
func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return nil, err
}
if !token.IsSuperAdmin(gc) {
log.Debug("Not logged in as super admin.")
return nil, errors.New("unauthorized")
}
// this feature is only allowed if email server is configured
if envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
log.Debug("Email server is not configured")
return nil, errors.New("email sending is disabled")
}
if envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication) && envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableMagicLinkLogin) {
log.Debug("Basic authentication and Magic link login is disabled.")
return nil, errors.New("either basic authentication or magic link login is required")
}
@@ -47,6 +52,7 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput)
}
if len(emails) == 0 {
log.Debug("No valid email addresses")
return nil, errors.New("no valid emails found")
}
@@ -56,14 +62,15 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput)
for _, email := range emails {
_, err := db.Provider.GetUserByEmail(email)
if err != nil {
log.Printf("%s user not found. inviting user.", email)
log.Debugf("User with %s email not found, so inviting user", email)
newEmails = append(newEmails, email)
} else {
log.Println("%s user already exists. skipping.", email)
log.Debugf("User with %s email already exists, so not inviting user", email)
}
}
if len(newEmails) == 0 {
log.Debug("No new emails found.")
return nil, errors.New("all emails already exist")
}
@@ -90,7 +97,7 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput)
verificationToken, err := token.CreateVerificationToken(email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL)
if err != nil {
log.Println(`error generating token`, err)
log.Debug("Failed to create verification token: ", err)
}
verificationRequest := models.VerificationRequest{
@@ -116,13 +123,13 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput)
user, err = db.Provider.AddUser(user)
if err != nil {
log.Printf("error inviting user: %s, err: %v", email, err)
log.Debugf("Error adding user: %s, err: %v", email, err)
return nil, err
}
_, err = db.Provider.AddVerificationRequest(verificationRequest)
if err != nil {
log.Printf("error inviting user: %s, err: %v", email, err)
log.Debugf("Error adding verification request: %s, err: %v", email, err)
return nil, err
}

Some files were not shown because too many files have changed in this diff Show More