Skip to content

Commit

Permalink
support basic authentication on frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
larinam committed Dec 9, 2023
1 parent 765cf75 commit f5e1457
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 29 deletions.
2 changes: 1 addition & 1 deletion backend/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
else:
log.info("MongoDB user already exists")
mongo_connection_string = f"mongodb://{mongo_username}:{mongo_password}@{mongo_host}:{mongo_port}/"
log.info(f"MongoDB connection string: {mongo_connection_string}")
log.debug(f"MongoDB connection string: {mongo_connection_string}")
connect(mongo_db_name, host=mongo_connection_string)
else: # just local MongoDB
log.info("Connecting to local MongoDB")
Expand Down
3 changes: 2 additions & 1 deletion frontend/.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
REACT_APP_API_URL=http://localhost:8000
REACT_APP_API_URL=http://localhost:8000
REACT_APP_REQUIRE_BASIC_AUTH=
24 changes: 21 additions & 3 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
import React from 'react';
import React, { useState } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import MainComponent from './components/MainComponent';
import Login from './components/Login'; // Assuming Login is in the components folder

function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [authHeader, setAuthHeader] = useState('');

const handleLogin = (username, password) => {
const encodedCredentials = btoa(`${username}:${password}`);
setAuthHeader(`Basic ${encodedCredentials}`);
setIsAuthenticated(true);
// Additional logic for handling login can be added here
};

// Check the updated environment variable
const requiresBasicAuth = process.env.REACT_APP_REQUIRE_BASIC_AUTH === 'true';

return (
<Router>
<Routes>
<Route path="/" element={<MainComponent />} />
{/* Add more routes as needed */}
{requiresBasicAuth && !isAuthenticated ? (
<Route path="/" element={<Login onLogin={handleLogin} />} />
) : (
<Route path="/" element={<MainComponent authHeader={authHeader} />} />
)}
{/* Additional routes can be added here */}
</Routes>
</Router>
);
Expand Down
36 changes: 21 additions & 15 deletions frontend/src/components/CalendarComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import './styles.css';

const API_URL = process.env.REACT_APP_API_URL;

const CalendarComponent = ({ teamData, holidays, updateTeamData }) => {
const CalendarComponent = ({ teamData, holidays, updateTeamData, authHeader }) => {
const [currentMonth, setCurrentMonth] = useState(new Date());
const [newTeamName, setNewTeamName] = useState('');
const [showAddMemberForm, setShowAddMemberForm] = useState(false);
Expand Down Expand Up @@ -55,6 +55,14 @@ const CalendarComponent = ({ teamData, holidays, updateTeamData }) => {
return '';
};

const getHeaders = () => {
const headers = { 'Content-Type': 'application/json' };
if (authHeader) {
headers['Authorization'] = authHeader;
}
return headers;
};

const handleDayClick = async (teamId, memberId, day) => {
const date = new Date(currentMonth.getFullYear(), currentMonth.getMonth(), day);
const formattedDate = formatDate(date);
Expand All @@ -66,9 +74,7 @@ const CalendarComponent = ({ teamData, holidays, updateTeamData }) => {
try {
const response = await fetch(API_URL+`/teams/${teamId}/members/${memberId}/vac_days/`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
headers: getHeaders(),
body: JSON.stringify([formattedDate]),
});

Expand All @@ -86,9 +92,7 @@ const CalendarComponent = ({ teamData, holidays, updateTeamData }) => {
try {
const response = await fetch(API_URL+`/teams/${teamId}/members/${memberId}/vac_days/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
headers: getHeaders(),
body: JSON.stringify([formattedDate]),
});

Expand All @@ -107,7 +111,10 @@ const CalendarComponent = ({ teamData, holidays, updateTeamData }) => {
const deleteTeam = async (teamId) => {
if (window.confirm('Are you sure you want to delete this team?')) {
try {
const response = await fetch(API_URL + `/teams/${teamId}`, { method: 'DELETE' });
const response = await fetch(API_URL + `/teams/${teamId}`, {
method: 'DELETE',
headers: getHeaders(),
});
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
updateTeamData(); // Refresh data
} catch (error) {
Expand All @@ -119,7 +126,10 @@ const CalendarComponent = ({ teamData, holidays, updateTeamData }) => {
const deleteTeamMember = async (teamId, memberId) => {
if (window.confirm('Are you sure you want to delete this team member?')) {
try {
const response = await fetch(API_URL + `/teams/${teamId}/members/${memberId}`, { method: 'DELETE' });
const response = await fetch(API_URL + `/teams/${teamId}/members/${memberId}`, {
method: 'DELETE',
headers: getHeaders(),
});
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
updateTeamData(); // Refresh data
} catch (error) {
Expand All @@ -134,9 +144,7 @@ const CalendarComponent = ({ teamData, holidays, updateTeamData }) => {
try {
const response = await fetch(API_URL + '/teams/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
headers: getHeaders(),
body: JSON.stringify({ name: newTeamName }),
});

Expand All @@ -159,9 +167,7 @@ const CalendarComponent = ({ teamData, holidays, updateTeamData }) => {
try {
const response = await fetch(API_URL + `/teams/${selectedTeamId}/members/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
headers: getHeaders(),
body: JSON.stringify(newMemberData),
});

Expand Down
36 changes: 36 additions & 0 deletions frontend/src/components/Login.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { useState } from 'react';
import './LoginStyles.css';

const Login = ({ onLogin }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');

const handleSubmit = (e) => {
e.preventDefault();
onLogin(username, password);
};

return (
<div className="loginContainer">
<form onSubmit={handleSubmit} className="formStyle">
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Username"
className="inputStyle"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
className="inputStyle"
/>
<button type="submit" className="buttonStyle">Login</button>
</form>
</div>
);
};

export default Login;
41 changes: 41 additions & 0 deletions frontend/src/components/LoginStyles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* LoginStyles.css - Ensuring form elements alignment */

.loginContainer {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}

.formStyle {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
border-radius: 5px;
box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.2);
background-color: white;
}

.inputStyle, .buttonStyle {
margin: 10px 0; /* Consistent vertical margin */
padding: 8px;
width: 300px; /* Adjust width as needed */
display: block; /* Block level for consistent alignment */
box-sizing: border-box; /* Include padding and border in width/height */
}

.inputStyle {
border: 1px solid rgba(0, 0, 0, 0.1);
}

.buttonStyle {
padding: 8px 15px;
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
width: 320px; /* Slightly larger than input for better visual */
margin-top: 20px; /* Increased top margin for separation from inputs */
}
21 changes: 13 additions & 8 deletions frontend/src/components/MainComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,23 @@ import CalendarComponent from './CalendarComponent';

const API_URL = process.env.REACT_APP_API_URL;

const MainComponent = () => {
const MainComponent = ({ authHeader }) => {
const [data, setData] = useState(null);

useEffect(() => {
fetch(API_URL+'/')
.then(response => response.json())
.then(data => setData(data))
.catch(error => console.error('Error fetching data:', error));
}, []);
fetch(API_URL+'/', {
headers: authHeader ? { 'Authorization': authHeader } : {}
})
.then(response => response.json())
.then(data => setData(data))
.catch(error => console.error('Error fetching data:', error));
}, [authHeader]);

const fetchTeamData = async () => {
try {
const response = await fetch(API_URL+'/');
const response = await fetch(API_URL+'/', {
headers: authHeader ? { 'Authorization': authHeader } : {}
});
const data = await response.json();
setData(data);
} catch (error) {
Expand All @@ -25,7 +30,7 @@ const MainComponent = () => {
if (!data) return <div>Loading...</div>;

return (
<CalendarComponent teamData={data.teams} holidays={data.holidays} currentMonth={new Date()} updateTeamData={fetchTeamData}/>
<CalendarComponent teamData={data.teams} holidays={data.holidays} currentMonth={new Date()} updateTeamData={fetchTeamData} authHeader={authHeader}/>
);
};

Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ form {

form input[type="text"] {
padding: 8px;
margin-right: 10px;
}

form button {
Expand Down

0 comments on commit f5e1457

Please sign in to comment.