Skip to content

Commit

Permalink
add openId connection
Browse files Browse the repository at this point in the history
  • Loading branch information
hassanelnajjar committed Sep 11, 2023
1 parent 25bde10 commit a0d0890
Show file tree
Hide file tree
Showing 20 changed files with 10,182 additions and 2,421 deletions.
2,727 changes: 1,502 additions & 1,225 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
},
"dependencies": {
"@datafoodconsortium/connector": "^1.0.0-alpha",
"@shopify/app": "3.39.0",
"@shopify/cli": "3.39.0",
"@shopify/app": "3.47.5",
"@shopify/cli": "3.47.5",
"@shopify/shopify-app-session-storage-postgresql": "^1.1.9",
"axios": "^1.4.0",
"babel-jest": "^29.5.0",
Expand All @@ -28,6 +28,7 @@
"nock": "^13.3.1",
"node-localstorage": "^2.2.1",
"node-persist": "^3.1.3",
"openid-client": "^5.5.0",
"passport": "^0.6.0",
"passport-openidconnect": "^0.1.1",
"pg": "^8.11.3",
Expand Down
27 changes: 27 additions & 0 deletions web/api-modules/users/controllers/edit-user-status-by-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { query } from '../../../database/connect.js';

const editUserStatusById = async (req, res, next) => {
const { id: userId } = req.params;

if (!userId) {
return res.status(400).json({
message: 'User id not provided',
error: 'No user id provided'
});
}

try {
await query('UPDATE users SET status = NOT status WHERE user_id = $1', [
userId
]);

return res.status(200).json({
message: 'User status updated successfully',
success: true
});
} catch (err) {
return next(err);
}
};

export default editUserStatusById;
14 changes: 14 additions & 0 deletions web/api-modules/users/controllers/get-users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { query } from '../../../database/connect.js';

const getUsers = async (req, res, next) => {
try {
const users = await query('SELECT * FROM users');
return res.json({
users: users.rows
});
} catch (err) {
return next(err);
}
};

export default getUsers;
10 changes: 10 additions & 0 deletions web/api-modules/users/controllers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Router } from 'express';
import getUsers from './get-users.js';
import editUserStatusById from './edit-user-status-by-id.js';

const users = Router();

users.get('/', getUsers);
users.patch('/:id', editUserStatusById);

export default users;
3 changes: 3 additions & 0 deletions web/api-modules/users/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Controllers from './controllers/index.js';

export default { Controllers };
3 changes: 3 additions & 0 deletions web/api-routers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { Router } from 'express';

import checkOnlineSession from './middleware/checkOnlineSession.js';
import ProductsModules from './api-modules/products/index.js';
import UsersModules from './api-modules/users/index.js';

const apiRouter = Router();

apiRouter.use('/products', checkOnlineSession, ProductsModules.Controllers);

apiRouter.use('/hub-users', checkOnlineSession, UsersModules.Controllers);

export default apiRouter;
7 changes: 1 addition & 6 deletions web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,7 @@ app.use('/api/*', shopify.validateAuthenticatedSession());

app.use('/*', addSessionShopToReqParams);

app.use(
'/api',
express.json(),
express.urlencoded({ extended: true }),
apiRouters
);
app.use('/api', express.json(), apiRouters);

app.use(serveStatic(STATIC_PATH, { index: false }));

Expand Down
10 changes: 10 additions & 0 deletions web/database/users/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
BEGIN;
DROP TABLE IF EXISTS users;
CREATE TABLE IF NOT EXISTS users (
"id" SERIAL PRIMARY KEY,
user_id TEXT NOT NULL UNIQUE,
shop TEXT,
name TEXT,
status BOOLEAN NOT NULL DEFAULT FALSE
);
COMMIT;
9 changes: 8 additions & 1 deletion web/fdc-modules/products/controllers/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { Router } from 'express';
import getProducts from './get-products.js';

import checkUserAccessPermissions from '../../../middleware/checkUserAccessPermissions.js';

import checkOfflineSession from '../../../middleware/checkOfflineSession.js';

const products = Router();

products.get('/', checkOfflineSession, getProducts);
products.post(
'/',
checkOfflineSession,
checkUserAccessPermissions,
getProducts
);

export default products;
4 changes: 2 additions & 2 deletions web/frontend/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export default function App() {
<NavigationMenu
navigationLinks={[
{
label: 'Page name',
destination: '/pagename'
label: 'Hub Users',
destination: '/hubUsers'
}
]}
/>
Expand Down
8 changes: 7 additions & 1 deletion web/frontend/hooks/useAppMutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ export const useAppMutation = ({ reactQueryOptions }) => {
() =>
async ({ url, fetchInit = {} }) => {
const response = await authenticatedFetch(url, fetchInit);
return response.json();
const responseJson = await response.json();

if (response.status >= 400) {
throw new Error(responseJson.error);
}

return responseJson;
},
[]
);
Expand Down
63 changes: 63 additions & 0 deletions web/frontend/pages/HubUsers.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Checkbox } from '@shopify/polaris';
import { useQueryClient } from 'react-query';
import { useAppQuery, useAppMutation } from '../hooks';

const HubUsers = () => {
const queryClient = useQueryClient();
const { data, isLoading } = useAppQuery({
url: '/api/hub-users'
});

const { mutateAsync, isLoading: updateUserStatusLoading } = useAppMutation({
reactQueryOptions: {
onSuccess: () => {
queryClient.invalidateQueries('/api/hub-users');
}
}
});

if (isLoading) {
return 'loading...';
}

const users = data?.users;

return users.map((user) => (
<div
key={user.id}
style={{
display: 'flex',
alignItems: 'center'
}}
>
<Checkbox
style={{
width: '50px'
}}
disabled={updateUserStatusLoading}
checked={user.status}
onChange={async () => {
await mutateAsync({
url: `/api/hub-users/${user.userId}`,
fetchInit: {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
}
}
});
}}
/>
<p
style={{
margin: '0 10px'
}}
>
{user.name}
</p>
<p>{user.userId}</p>
</div>
));
};

export default HubUsers;
2 changes: 1 addition & 1 deletion web/frontend/pages/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function HomePage() {
url: '/api/products'
});

const { mutateAsync, isLoading: updateProductIsLoading } = useAppMutation({
const { mutateAsync } = useAppMutation({
reactQueryOptions: {
onSettled: (updateProductData) => {
setDisabledProductCheckbox((x) => ({
Expand Down
4 changes: 2 additions & 2 deletions web/middleware/checkOnlineSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ const checkOnlineSession = async (req, res, next) => {

req.shopifySession = session;

next();
return next();
} catch (err) {
res.status(500).json({
return res.status(500).json({
error: 'Error checking offline session'
});
}
Expand Down
71 changes: 71 additions & 0 deletions web/middleware/checkUserAccessPermissions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Issuer } from 'openid-client';
import { query } from '../database/connect.js';

const clientId = process.env.OIDC_CLIENT_ID;
const clientSecret = process.env.OIDC_CLIENT_SECRET;
const issuerURL = process.env.OIDC_ISSUER;

const checkUserAccessPermissions = async (req, res, next) => {
const { userId, accessToken } = req.body;

if (!userId) {
return res.status(403).json({
message: 'User access denied',
error: 'No user id provided'
});
}

const issuer = await Issuer.discover(issuerURL);

const client = new issuer.Client({
client_id: clientId,
client_secret: clientSecret
});

const tokenSet = await client.introspect(accessToken);

if (!tokenSet.active) {
return res.status(403).json({
message: 'User access denied',
error: 'User not authorized'
});
}

const userName = tokenSet.name;

try {
const user = await query('SELECT * FROM users WHERE user_id = $1', [
userId
]);

if (!user || user.rows.length === 0) {
// insert this user into the database with status false
await query(
'INSERT INTO users (user_id, status,name) VALUES ($1, $2,$3)',
[userId, false, userName]
);
return res.status(403).json({
message: 'User access denied',
error: 'User not found in database'
});
}

const { status } = user.rows[0];

if (status === true) {
return next();
}

return res.status(403).json({
message: 'User access denied',
error: 'User not authorized'
});
} catch (err) {
return res.status(500).json({
message: 'User access denied',
error: err.message
});
}
};

export default checkUserAccessPermissions;
Loading

0 comments on commit a0d0890

Please sign in to comment.