Skip to content

Commit

Permalink
Merge pull request #167 from codefordenver/152-add-auth-state-handlin…
Browse files Browse the repository at this point in the history
…g-to-frontend

152 add auth state handling to frontend
  • Loading branch information
ethoreson authored Sep 24, 2019
2 parents 7987067 + b2f6449 commit aae2c6f
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 120 deletions.
32 changes: 28 additions & 4 deletions Backend/src/main/java/idealab/api/security/WebSecurity.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
import idealab.api.model.EmployeeRole;
import idealab.api.repositories.EmployeeRepo;
import idealab.api.service.UserDetailsServiceImpl;
import idealab.api.security.SecurityConstants;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
Expand Down Expand Up @@ -32,9 +37,10 @@ public WebSecurity(UserDetailsServiceImpl userDetailsService, BCryptPasswordEnco
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
//.antMatchers(SIGN_UP_URL, LOGIN_URL).permitAll()
.antMatchers(HttpMethod.DELETE).hasRole(String.valueOf(EmployeeRole.ADMIN))
//.anyRequest().authenticated()
.antMatchers(SecurityConstants.SIGN_UP_URL).permitAll()
.antMatchers(SecurityConstants.LOGIN_URL).permitAll()
.antMatchers(HttpMethod.DELETE).hasRole("Admin")
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager(), employeeRepo))
Expand All @@ -50,7 +56,25 @@ public void configure(AuthenticationManagerBuilder auth) throws Exception {
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
final CorsConfiguration configuration = new CorsConfiguration();

List<String> origins = new ArrayList<String>();
origins.add("*");
configuration.setAllowedOrigins(origins);

List<String> methods = new ArrayList<String>();
methods.add("HEAD");
methods.add("GET");
methods.add("POST");
methods.add("OPTIONS");
methods.add("PUT");
methods.add("DELETE");
methods.add("PATCH");
configuration.setAllowedMethods(methods);
configuration.applyPermitDefaultValues();

configuration.setExposedHeaders(Arrays.asList("X-Requested-With","Origin","Content-Type","Accept","Authorization"));
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
5 changes: 5 additions & 0 deletions Backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.properties.hibernate.hbm2ddl.auto=create
# TODO(e-carlin): (^^^) This will wipe away the database data. Good for dev not for prod

# These two lines make it so you can have multiple line SQL statements
spring.jpa.properties.hibernate.connection.charSet=UTF-8
spring.jpa.properties.hibernate.hbm2ddl.import_files_sql_extractor=org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.datasource.data=
8 changes: 6 additions & 2 deletions Backend/src/main/resources/import.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ INSERT INTO color_type (color, available) VALUES ('blue', true),('green', false)

INSERT INTO customer_info (fk_email_hash_id, created_date, first_name, last_name, email) VALUES (1, '2019-09-12', 'hannah', 'amundson', 'fake@gmail.com'), (2, '2019-07-12', 'john', 'frank', 'hi@gmail.com'),(3, '2019-09-12', 'josiah', 'taylor', 'hello@gmail.com'),(1, '2019-09-12', 'ben', 'arch', 'fake@gmail.com'),(2, '2019-09-12', 'haley', 'black', 'hi@gmail.com');

INSERT INTO employee (first_name, last_name, "password", "role", username) VALUES ('hank','joseph','sadfsdf','STAFF','hankjoseph'),('hannah','amundson','sasdfadfdfsdf','STAFF','hannahamundson'),('megan','forgey','sasdfsdfsdadfsdf','STAFF','mforgey'),('carol','hadley','2sfkdjlsa','ADMIN','carolhadley')
INSERT INTO employee (first_name, last_name, "password", "role", username) VALUES
('hank','joseph','$2a$10$riZO8iqbcCR1X/4fEgh0Au/VBNqgTOjVhZu9ZdlaMxztQEPYF2o8i','STAFF','hankjoseph'),
('hannah','amundson','$2a$10$riZO8iqbcCR1X/4fEgh0Au/VBNqgTOjVhZu9ZdlaMxztQEPYF2o8i','STAFF','hannahamundson'),
('megan','forgey','$2a$10$riZO8iqbcCR1X/4fEgh0Au/VBNqgTOjVhZu9ZdlaMxztQEPYF2o8i','STAFF','mforgey'),
('carol','hadley','$2a$10$riZO8iqbcCR1X/4fEgh0Au/VBNqgTOjVhZu9ZdlaMxztQEPYF2o8i','ADMIN','carolhadley');

INSERT INTO print_job (status, fk_email_hash_id, comments, dropbox_sharable_link, dropbox_path, updated_at, created_at, fk_color_type_id, fk_employee_id) VALUES ('PENDING_REVIEW', 1, 'user comment', 'http://fakelink.com', '/fakepath', '2019-05-01','2019-03-01',1,1),('PRINTING', 2, 'user comment 2', 'http://what.com', '/whatpath','2018-05-01','2018-03-01',2,2),('PENDING_REVIEW',2, 'user comment 3', 'http://fake.com', '/fakepath','2018-04-01','2018-03-01',3,3),('PENDING_REVIEW',2, 'user comment 4', 'http://hi.com', '/hipath','2019-05-14','2019-03-12',4,4),('PENDING_REVIEW',3, 'user comment 6', 'http://dotcom.com', '/dotpath','2019-05-15','2019-03-10',2,2),('PENDING_REVIEW', 1, 'user comment 7', 'http://website.com', '/websitepath', '2019-05-23','2019-03-06',3,2),('PENDING_REVIEW', 3, 'user comment 8', 'http://site.com', '/sitepath','2019-05-27','2019-05-07',3,3),('PENDING_REVIEW', 4, 'user comment 9', 'http://web.com', '/webpath','2019-05-03','2019-04-08',4,1);

INSERT INTO print_status_audit (status_after, status_before, created_at, fk_employee_id, fk_print_model_id) VALUES ('Waiting', 'PENDING_REVIEW', '01/01/2019', 1, 1), ('PENDING_REVIEW', 'PRINTING', '01/02/2019', 2, 1), ('Waiting', 'PENDING_REVIEW', '01/01/2019', 1, 2), ('Complete', 'PENDING_REVIEW', '01/01/2019', 1, 3);

INSERT INTO queue ("rank", fk_print_job_id) VALUES (1,1),(2,3),(3,2);
INSERT INTO queue ("rank", fk_print_job_id) VALUES (1,1),(2,3),(3,2);
54 changes: 40 additions & 14 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,53 @@
import React from 'react';
import React, { useState } from 'react';
import './App.css';
import AuthContext from './AuthContext';
import QueueContainer from './components/Queue/QueueContainer';
import UploadContainer from './components/Upload/UploadContainer';
import LoginManager from './components/Login/LoginManager';
import CreateAccountManager from './components/CreateAccount/CreateAccountManager';
import SidebarNavigation from './SidebarNavigation';
import { HashRouter, Switch, Route } from 'react-router-dom';
import PrivateRoute from './components/Routing/PrivateRoute';

import { HashRouter, Switch, Route, Redirect } from 'react-router-dom';


function App() {
const activeUser = 'admin';
const sidebar = activeUser ? <SidebarNavigation /> : null;
const [authenticated, setAuthenticated] = useState(false);
const [token, setToken] = useState(null);
return (
<div className="App grid-container">
<HashRouter>
{sidebar}
<Switch>
<Route exact path="/queue" component={QueueContainer} />
<Route exact path="/manageaccounts" component={CreateAccountManager} />
<Route exact path="/upload" component={UploadContainer} />
<Route path="/login" component={LoginManager} />
<Route path='/account' component={CreateAccountManager} />
</Switch>
</HashRouter>
<AuthContext.Provider value={{ authenticated: authenticated, token: token, setAuthenticated: setAuthenticated, setToken: setToken }}>
<HashRouter>
<SidebarNavigation logout={() => {
setToken('');
setAuthenticated(false);
}} />
<Switch>
<PrivateRoute exact path="/queue" component={QueueContainer} />
<PrivateRoute exact path="/manageaccounts" component={CreateAccountManager} />
<PrivateRoute exact path="/upload" component={UploadContainer} />
<Route
path="/login"
render={props =>
authenticated ? (
<Redirect
to={{
pathname: "/queue",
state: { from: props.location }
}}
/>
) : (
<LoginManager{...props} />
)
}
/>

<PrivateRoute path='/account' component={CreateAccountManager} />
<PrivateRoute path="/*" component={UploadContainer} />
</Switch>
</HashRouter>
</AuthContext.Provider>

</div>
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/AuthContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

const AuthContext = React.createContext({ authenticated: false, token: null });

export default AuthContext;
65 changes: 36 additions & 29 deletions src/SidebarNavigation.jsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,46 @@
import React from 'react';
import AuthContext from './AuthContext';
import './App.css';
import { NavLink } from 'react-router-dom';

class SidebarNavigation extends React.Component {
render() {
return (
<nav className={'grid-item-nav'}>
<ul className={'navList'}>
<li>
<NavLink to="/upload" activeClassName={'selected'}>
upload
</NavLink>
</li>
<li>
<NavLink to="/queue" activeClassName={'selected'}>
queue
</NavLink>
</li>
<li>
<NavLink to="/manageaccounts" activeClassName={'selected'}>
manage accounts
</NavLink>
</li>
<li>
<NavLink to="/analytics" activeClassName={'selected'}>
analytics
</NavLink>
</li>
<li onClick={this.props.logout}>
<NavLink to="/login" activeClassName={'selected'}>
log out
</NavLink>
</li>
</ul>
</nav>
<AuthContext.Consumer>
{context => {
return (
<nav className={'grid-item-nav'}>
<ul className={'navList'}>
<li>
<NavLink to="/upload" activeClassName={'selected'}>
upload
</NavLink>
</li>
<li>
<NavLink to="/queue" activeClassName={'selected'}>
queue
</NavLink>
</li>
<li>
<NavLink to="/manageaccounts" activeClassName={'selected'}>
manage accounts
</NavLink>
</li>
<li>
<NavLink to="/analytics" activeClassName={'selected'}>
analytics
</NavLink>
</li>
<li onClick={context.authenticated ? this.props.logout : null}>
<NavLink to="/login" activeClassName={'selected'}>
{context.authenticated ? 'log out' : 'log in'}
</NavLink>
</li>
</ul>
</nav>
);
}}
</AuthContext.Consumer>
);
}
}
Expand Down
135 changes: 92 additions & 43 deletions src/components/Login/LoginManager.jsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,101 @@
import React, { useState } from 'react';
import Dropdown from '../globalStyles/Dropdown';
import BasicInput from '../BasicInput';
import RequestService from '../../util/RequestService';
import AuthContext from '../../AuthContext';
import './LoginManager.css';

import ideaLABlogo from './../../ideaLABlogo.png';

const LoginManager = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const locations = [
'Denver Central Library',
'Blair-Caldwell',
'Ford-Warren',
'Ross-Cherry',
'Park Hill'
]
const [location, setLocation] = useState(locations[0]);

const onSubmit = e => {
e.preventDefault();
};

return (
<div className='container'>
<img src={ideaLABlogo} alt='ideaLABLogo' />
<h4>3D Printing and Upload Queue</h4>
<h2>Sign In</h2>
<form onSubmit={e => onSubmit(e)}>
<input
name='email'
placeholder='email'
autoComplete='off'
autoFocus
value={email}
onChange={e => setEmail(e.target.value)}
/>
<Dropdown options={locations} optionsName={'locations'} currentValue={location} />
<input
name='password'
placeholder='password'
type='password'
autoComplete='off'
value={password}
onChange={e => setPassword(e.target.value)}
/>
<button type='submit'>SIGN IN</button>
</form>
</div>
);
const LoginManager = props => {
const [errors, setErrors] = useState({});
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const locations = [
'Denver Central Library',
'Blair-Caldwell',
'Ford-Warren',
'Ross-Cherry',
'Park Hill',
];
const [location, setLocation] = useState(locations[0]);

function thenCallback(callbacks) {
return function actualCallback(response) {
const token = response.headers ? response.headers.authorization : '';
if (token) {
callbacks.setToken(token);
callbacks.setAuthenticated(true);
RequestService.requestState.token = token;
} else {
// Something should happen
callbacks.setAuthenticated(false);
setErrors({
form: 'Unable to log in with the information provided',
});
}
};
}

const onSubmit = (e, callbacks) => {
e.preventDefault();

const payload = {
username: username,
password: password,
};

RequestService.login(
payload,
thenCallback(callbacks),
thenCallback(callbacks),
);
};

return (
<AuthContext.Consumer>
{context => {
return (
<div className="container">
{context.authenticated}
<img src={ideaLABlogo} alt="ideaLABLogo" />
<h4>3D Printing and Upload Queue</h4>
<h2>Sign In</h2>
<form
onSubmit={e => {
const callbacks = {
setToken: context.setToken,
setAuthenticated: context.setAuthenticated,
};
onSubmit(e, callbacks);
}}
>
<span>{errors.form ? errors.form : null}</span>
<BasicInput
name="username"
placeHolder="username"
changeHandler={setUsername}
error={errors.username}
/>
<Dropdown
options={locations}
optionsName={'locations'}
currentValue={location}
/>
<BasicInput
name="password"
placeHolder="password"
type="password"
changeHandler={setPassword}
error={errors.password}
/>
<button type="submit">SIGN IN</button>
</form>
</div>
);
}}
</AuthContext.Consumer>
);
};

export default LoginManager;
Loading

0 comments on commit aae2c6f

Please sign in to comment.