Skip to content

Commit be9a395

Browse files
author
Ariel Guelfi
committed
Separate Auth config for when using OAuth2 or not.
Profile oauth2 is necessary for using Keycloak or others. Move material-table dep to a github one instead of file to avoid conflicts during build time.
1 parent 9d291f3 commit be9a395

13 files changed

Lines changed: 3460 additions & 11450 deletions

File tree

docker/docker-compose.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ version: '3.3'
22
services:
33
postgres:
44
image: postgres:12.2-alpine
5+
container_name: databucket_postgres
56
expose:
67
- "5432"
78
ports:
@@ -73,4 +74,4 @@ networks:
7374
auth:
7475
internal: true
7576
# server:
76-
# internal: true
77+
# internal: true

frontend/package-lock.json

Lines changed: 3179 additions & 11352 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"jsoneditor": "9.10.3",
2121
"jsoneditor-react": "3.1.2",
2222
"jsonpath": "1.1.1",
23-
"material-table": "file:material-table",
23+
"material-table": "github:databucket/material-table#6000f027884f41c7d85710003bea52e60ab6ff28",
2424
"material-ui-confirm": "3.0.9",
2525
"moment": "2.29.4",
2626
"mui-color-input": "1.0.6",
@@ -35,14 +35,15 @@
3535
},
3636
"scripts": {
3737
"start": "react-scripts --openssl-legacy-provider start",
38-
"build": "npm run build-submodule && react-scripts build",
39-
"build-submodule": "cd material-table && npm run build",
38+
"build": "react-scripts build",
4039
"test": "react-scripts test",
41-
"eject": "react-scripts eject",
42-
"postinstall": "cd material-table && npm i"
40+
"eject": "react-scripts eject"
4341
},
4442
"eslintConfig": {
45-
"extends": "react-app"
43+
"extends": [
44+
"react-app",
45+
"plugin:react-hooks/recommended"
46+
]
4647
},
4748
"browserslist": {
4849
"production": [
@@ -54,5 +55,9 @@
5455
"last 1 firefox version",
5556
"last 1 safari version"
5657
]
58+
},
59+
"devDependencies": {
60+
"eslint": "8.50.0",
61+
"eslint-plugin-react-hooks": "4.6.0"
5762
}
5863
}

frontend/src/components/management/ProjectsTab.jsx

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ import {
1010
getTableHeaderBackgroundColor,
1111
getTableRowBackgroundColor
1212
} from "../../utils/MaterialTableHelper";
13-
import {
14-
getLastPageSize,
15-
setLastPageSize
16-
} from "../../utils/ConfigurationStorage";
13+
import {getLastPageSize, setLastPageSize} from "../../utils/ConfigurationStorage";
1714
import {
1815
arraysEquals,
1916
convertNullValuesInObject,
@@ -54,38 +51,39 @@ export default function ProjectsTab() {
5451
const [pageSize, setPageSize] = useState(getLastPageSize);
5552
const [filtering, setFiltering] = useState(false);
5653
const tableRef = createRef();
57-
const projectsContext = useContext(ProjectsContext);
58-
const {projects, fetchProjects, addProject, editProject, removeProject} = projectsContext;
59-
const usersContext = useContext(ManageUsersContext);
60-
const {users, fetchUsers, notifyUsers} = usersContext;
61-
const templatesContext = useContext(TemplatesContext);
62-
const {templates, fetchTemplates, notifyTemplates} = templatesContext;
63-
const rolesContext = useContext(RolesContext);
64-
const {roles, fetchRoles} = rolesContext;
65-
const changeableFields = ['id', 'publicVisible', 'enabled', 'name', 'description', 'usersIds', 'templatesIds', 'expirationDate'];
54+
const {projects, fetchProjects, addProject, editProject, removeProject} = useContext(ProjectsContext);
55+
const {users, fetchUsers, notifyUsers} = useContext(ManageUsersContext);
56+
const {templates, fetchTemplates, notifyTemplates} = useContext(TemplatesContext);
57+
const {roles, fetchRoles} = useContext(RolesContext);
58+
const changeableFields = ['id', 'publicVisible', 'enabled', 'name', 'description', 'usersIds', 'templatesIds',
59+
'expirationDate'];
6660
const projectSpecification = {
6761
name: {title: 'Name', check: ['notEmpty', 'min1', 'max30']},
6862
description: {title: 'Description', check: ['max250']}
6963
};
7064

7165
useEffect(() => {
72-
if (users == null)
66+
if (users == null) {
7367
fetchUsers();
68+
}
7469
}, [users, fetchUsers]);
7570

7671
useEffect(() => {
77-
if (templates == null)
72+
if (templates == null) {
7873
fetchTemplates();
74+
}
7975
}, [templates, fetchTemplates]);
8076

8177
useEffect(() => {
82-
if (projects == null)
78+
if (projects == null) {
8379
fetchProjects();
80+
}
8481
}, [projects, fetchProjects]);
8582

8683
useEffect(() => {
87-
if (roles == null)
84+
if (roles == null) {
8885
fetchRoles();
86+
}
8987
}, [roles, fetchRoles]);
9088

9189
const onChangeRowsPerPage = (pageSize) => {
@@ -101,15 +99,16 @@ export default function ProjectsTab() {
10199
setTimeout(() => {
102100
let e = false;
103101
fetch(getBaseUrl(`manage/projects/${confirmRemove.id}`), getDeleteOptions())
104-
.then(handleErrors)
105-
.catch(error => {
106-
e = true;
107-
setMessageBox({open: true, severity: 'error', title: 'Error', message: error});
108-
})
109-
.then(() => {
110-
if (!e)
111-
removeProject(confirmRemove.id);
112-
});
102+
.then(handleErrors)
103+
.catch(error => {
104+
e = true;
105+
setMessageBox({open: true, severity: 'error', title: 'Error', message: error});
106+
})
107+
.then(() => {
108+
if (!e) {
109+
removeProject(confirmRemove.id);
110+
}
111+
});
113112
}, 100);
114113
}
115114

@@ -119,7 +118,6 @@ export default function ProjectsTab() {
119118
return (
120119
<div>
121120
<MaterialTable
122-
123121
title='Projects'
124122
tableRef={tableRef}
125123
columns={[
@@ -214,19 +212,19 @@ export default function ProjectsTab() {
214212
}
215213

216214
fetch(getBaseUrl('manage/projects'), getPostOptions(newData))
217-
.then(handleErrors)
218-
.catch(error => {
219-
reject();
220-
setMessageBox({open: true, severity: 'error', title: 'Error', message: error});
221-
})
222-
.then((project) => {
223-
if (project != null) {
224-
addProject(convertNullValuesInObject(project, getManageProjectMapper()));
225-
notifyUsers('PROJECT', project.id, project['usersIds']);
226-
notifyTemplates('PROJECT', project.id, project['templatesIds']);
227-
resolve();
228-
}
229-
});
215+
.then(handleErrors)
216+
.catch(error => {
217+
reject();
218+
setMessageBox({open: true, severity: 'error', title: 'Error', message: error});
219+
})
220+
.then((project) => {
221+
if (project != null) {
222+
addProject(convertNullValuesInObject(project, getManageProjectMapper()));
223+
notifyUsers('PROJECT', project.id, project['usersIds']);
224+
notifyTemplates('PROJECT', project.id, project['templatesIds']);
225+
resolve();
226+
}
227+
});
230228
}),
231229

232230
onRowUpdate: (newData, oldData) =>
@@ -258,20 +256,21 @@ export default function ProjectsTab() {
258256
const payload = getSelectedValues(newData, changeableFields);
259257

260258
fetch(getBaseUrl('manage/projects'), getPutOptions(payload))
261-
.then(handleErrors)
262-
.catch(error => {
263-
setMessageBox({open: true, severity: 'error', title: 'Error', message: error});
264-
reject();
265-
})
266-
.then((project) => {
267-
if (project != null) {
268-
editProject(convertNullValuesInObject(project, getManageProjectMapper()));
269-
if (!arraysEquals(newData, oldData, 'usersIds'))
270-
notifyUsers('PROJECT', project.id, project['usersIds']);
271-
notifyTemplates('PROJECT', project.id, project['templatesIds']);
272-
resolve();
259+
.then(handleErrors)
260+
.catch(error => {
261+
setMessageBox({open: true, severity: 'error', title: 'Error', message: error});
262+
reject();
263+
})
264+
.then((project) => {
265+
if (project != null) {
266+
editProject(convertNullValuesInObject(project, getManageProjectMapper()));
267+
if (!arraysEquals(newData, oldData, 'usersIds')) {
268+
notifyUsers('PROJECT', project.id, project['usersIds']);
273269
}
274-
});
270+
notifyTemplates('PROJECT', project.id, project['templatesIds']);
271+
resolve();
272+
}
273+
});
275274
}),
276275

277276
// onRowDelete: oldData =>

frontend/src/components/management/_ManagementTabs.jsx

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import React, {useState} from 'react';
2-
import {IconButton, Tabs, Toolbar} from "@mui/material";
1+
import React, {useState, Suspense, lazy } from 'react';
2+
import { IconButton, Stack, Tabs, Toolbar } from "@mui/material";
33
import {Close as CloseIcon} from "@mui/icons-material";
44
import {Link, Redirect, Route, Switch} from "react-router-dom";
55
import {
@@ -16,7 +16,6 @@ import {
1616
setLastManagementPageName,
1717
setPathname
1818
} from "../../utils/ConfigurationStorage";
19-
import ProjectsTab from "./ProjectsTab";
2019
import UsersTab from "./UsersTab";
2120
import NotFoundPage from "../NotFoundPage";
2221
import ManagementRoute from "../../route/ManagementRoute";
@@ -59,13 +58,17 @@ export default function _ManagementTabs() {
5958
}
6059

6160
if (logged) {
61+
const ProjectsTab = lazy(() => import("./ProjectsTab"));
62+
const UsersTab = lazy(() => import("./UsersTab"));
63+
const TemplatesTab = lazy(() => import("./TemplatesTab"));
64+
6265
setPathname(null); // clear path
6366
return (
6467
<Route
6568
path="/"
6669
render={({location}) => (
67-
<>
68-
<CustomAppBar position="fixed" sx={{flex: 1}}>
70+
<Stack>
71+
<CustomAppBar position={"sticky"} sx={{flex: 1}}>
6972
<Toolbar variant={'dense'}>
7073
{hasProject() ? (
7174
<IconButton
@@ -95,18 +98,19 @@ export default function _ManagementTabs() {
9598
<UserProfile onLogout={handleLogout}/>
9699
</Toolbar>
97100
</CustomAppBar>
98-
<ProjectsProvider> <ManageUsersProvider> <RolesProvider> <TemplatesProvider> <DataProvider>
99-
<DataItemsProvider>
100-
<Switch>
101-
<ManagementRoute exact path={getManagementProjectsPath()} component={ProjectsTab}/>
102-
<ManagementRoute exact path={getManagementUsersPath()} component={UsersTab}/>
103-
<ManagementRoute exact path={getManagementTemplatesPath()}
104-
component={TemplatesTab}/>
105-
<PublicRoute path="*" component={NotFoundPage}/>
106-
</Switch>
107-
</DataItemsProvider> </DataProvider> </TemplatesProvider> </RolesProvider>
108-
</ManageUsersProvider> </ProjectsProvider>
109-
</>
101+
<Suspense fallback={<div>Loading...</div>}>
102+
<ProjectsProvider> <ManageUsersProvider> <RolesProvider> <TemplatesProvider> <DataProvider>
103+
<DataItemsProvider>
104+
<Switch>
105+
<ManagementRoute exact path={getManagementProjectsPath()} component={ProjectsTab}/>
106+
<ManagementRoute exact path={getManagementUsersPath()} component={UsersTab}/>
107+
<ManagementRoute exact path={getManagementTemplatesPath()} component={TemplatesTab}/>
108+
<PublicRoute path="*" component={NotFoundPage}/>
109+
</Switch>
110+
</DataItemsProvider> </DataProvider> </TemplatesProvider> </RolesProvider>
111+
</ManageUsersProvider> </ProjectsProvider>
112+
</Suspense>
113+
</Stack>
110114
)}
111115
/>
112116
);

frontend/src/components/utils/AuthHelper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {handleLoginErrors} from "../../utils/FetchHelper";
21
import {
32
hasAdminRole,
43
hasMemberRole,
@@ -9,6 +8,7 @@ import {
98
setToken,
109
setUsername
1110
} from "../../utils/ConfigurationStorage";
11+
1212
export const handleSuccessfulLogin = (data, state) => {
1313
logOut();
1414
setUsername(data.username);

src/main/java/pl/databucket/server/configuration/WebMvcConfiguration.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
import java.io.IOException;
44
import lombok.RequiredArgsConstructor;
5-
import org.springframework.beans.factory.annotation.Autowired;
6-
import org.springframework.beans.factory.annotation.Value;
75
import org.springframework.context.annotation.Configuration;
86
import org.springframework.core.io.ClassPathResource;
97
import org.springframework.core.io.Resource;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package pl.databucket.server.security;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import lombok.extern.log4j.Log4j2;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.context.annotation.Profile;
8+
import org.springframework.http.HttpMethod;
9+
import org.springframework.http.HttpStatus;
10+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
11+
import org.springframework.security.config.http.SessionCreationPolicy;
12+
import org.springframework.security.web.SecurityFilterChain;
13+
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
14+
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
15+
import pl.databucket.server.dto.AuthRespDTO;
16+
17+
@Log4j2
18+
@Configuration
19+
@Profile("!oauth2")
20+
public class BasicAuthSecurityConfig {
21+
22+
@Bean
23+
public SecurityFilterChain basicSecurityFilterChain(HttpSecurity http,
24+
AuthResponseBuilder authResponseBuilder,
25+
ObjectMapper mapper) throws Exception {
26+
AuthenticationSuccessHandler successHandler = getFormSuccessHandler(authResponseBuilder);
27+
AuthenticationFailureHandler failureHandler = getAuthenticationFailureHandler(mapper);
28+
http.cors().and().csrf().disable()
29+
.authorizeRequests()
30+
.antMatchers(HttpMethod.GET,
31+
"/", "/login**", "/sign-up", "/forgot-password", "/change-password",
32+
"/index*", "/**/static/**", "/*.js", "/*.json", "/**/*.ico",
33+
"/api/auth/**")
34+
.permitAll()
35+
.antMatchers(HttpMethod.POST,
36+
"/api/auth/sign-up", "/api/auth/forgot-password"
37+
)
38+
.permitAll()
39+
.anyRequest()
40+
.authenticated()
41+
.and()
42+
.formLogin()
43+
.loginPage("/login-form")
44+
.successHandler(successHandler)
45+
.failureHandler(failureHandler)
46+
.permitAll();
47+
// .and()
48+
// .exceptionHandling().authenticationEntryPoint(unauthorizedHandler);
49+
http.sessionManagement()
50+
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
51+
http.oauth2ResourceServer().jwt();
52+
return http.build();
53+
}
54+
55+
private static AuthenticationFailureHandler getAuthenticationFailureHandler(ObjectMapper mapper) {
56+
return (request, response, exception) -> {
57+
log.error("Auth error", exception);
58+
AuthRespDTO authResponse = AuthRespDTO.builder().message(exception.getMessage()).build();
59+
response.setContentType("application/json");
60+
response.setCharacterEncoding("UTF-8");
61+
response.setStatus(HttpStatus.FORBIDDEN.value());
62+
response.getWriter().write(mapper.writeValueAsString(authResponse));
63+
};
64+
}
65+
66+
private AuthenticationSuccessHandler getFormSuccessHandler(AuthResponseBuilder authResponseBuilder) {
67+
return new FormAuthSuccessHandler(authResponseBuilder);
68+
}
69+
}

0 commit comments

Comments
 (0)