Skip to content

Commit

Permalink
add basic auth via a proxy (#5)
Browse files Browse the repository at this point in the history
* add basic auth via a proxy

* update linters and run
  • Loading branch information
jacobfilik authored May 16, 2024
1 parent 464cc7d commit 92f7eba
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 37 deletions.
2 changes: 1 addition & 1 deletion xas-standards-api/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ repos:
name: Run ruff
stages: [commit]
language: system
entry: ruff
entry: ruff check
types: [python]
2 changes: 1 addition & 1 deletion xas-standards-api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ commands =
[tool.ruff]
src = ["src", "tests"]
line-length = 88
select = [
lint.select = [
"C4", # flake8-comprehensions - https://beta.ruff.rs/docs/rules/#flake8-comprehensions-c4
"E", # pycodestyle errors - https://beta.ruff.rs/docs/rules/#error-e
"F", # pyflakes rules - https://beta.ruff.rs/docs/rules/#pyflakes-f
Expand Down
74 changes: 69 additions & 5 deletions xas-standards-api/src/xas_standards_api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,25 @@
import os
from typing import Annotated, List, Optional, Union

from fastapi import Depends, FastAPI, File, Form, Query, UploadFile
import requests
from fastapi import (
Depends,
FastAPI,
File,
Form,
HTTPException,
Query,
UploadFile,
status,
)
from fastapi.responses import HTMLResponse
from fastapi.security.http import HTTPAuthorizationCredentials, HTTPBearer
from fastapi.staticfiles import StaticFiles
from fastapi_pagination import add_pagination
from fastapi_pagination.cursor import CursorPage
from fastapi_pagination.ext.sqlalchemy import paginate
from sqlmodel import Session, create_engine, select
from starlette.responses import RedirectResponse

from .crud import (
add_new_standard,
Expand All @@ -35,11 +47,19 @@
)

dev = False
lifespan = None

env_value = os.environ.get("FASTAPI_APP_ENV")

if env_value and env_value == "development":
print("RUNNING IN DEV MODE")
dev = True

get_bearer_token = HTTPBearer(auto_error=True)

url = os.environ.get("POSTGRESURL")
build_dir = os.environ.get("FRONTEND_BUILD_DIR")
oidc_user_info_endpoint = os.environ.get("OIDC_USER_INFO_ENDPOINT")


if url:
engine = create_engine(url)
Expand All @@ -52,16 +72,59 @@ def get_session():
yield session


app = FastAPI(lifespan=lifespan)
app = FastAPI()

CursorPage = CursorPage.with_custom_options(
size=Query(10, ge=1, le=100),
)


add_pagination(app)


@app.get("/login", response_class=RedirectResponse)
async def redirect_home():
# proxy handles log in so if you reach here go home
return "/"


async def get_current_user(
auth: HTTPAuthorizationCredentials = Depends(get_bearer_token),
):

if auth is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid user token",
)

if dev:
return auth.credentials

if oidc_user_info_endpoint is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="User info endpoint error",
)

response = requests.get(
url=oidc_user_info_endpoint,
headers={"Authorization": f"Bearer {auth.credentials}"},
)

if response.status_code == 401:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid user token",
)

return response.json()["id"]


@app.get("/api/user")
async def check(user_id: str = Depends(get_current_user)):
return {"user": user_id}


@app.get("/api/metadata")
def read_metadata(session: Session = Depends(get_session)) -> MetadataResponse:
return get_metadata(session)
Expand Down Expand Up @@ -142,13 +205,14 @@ def add_standard_file(
licence: Annotated[str, Form()],
additional_files: Optional[list[UploadFile]] = Form(None),
sample_comp: Optional[str] = Form(None),
user_id: str = Depends(get_current_user),
session: Session = Depends(get_session),
) -> XASStandard:

if additional_files:
print(f"Additional files {len(additional_files)}")

person = select_or_create_person(session, "test1234")
person = select_or_create_person(session, user_id)

form_input = XASStandardInput(
submitter_id=person.id,
Expand Down
26 changes: 13 additions & 13 deletions xas-standards-api/src/xas_standards_api/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,23 +81,23 @@ def add_new_standard(session, file1, xs_input: XASStandardInput, additional_file
tmp_filename = pvc_location + str(uuid.uuid4())

with open(tmp_filename, "wb") as ntf:
filename = ntf.name
ntf.write(file1.file.read())
xdi_data = xdi.read_xdi(filename)

set_labels = set(xdi_data.array_labels)
xdi_data = xdi.read_xdi(tmp_filename)

fluorescence = "mufluor" in set_labels
transmission = "mutrans" in set_labels
emission = "mutey" in set_labels
set_labels = set(xdi_data.array_labels)

xsd = XASStandardDataInput(
fluorescence=fluorescence,
location=tmp_filename,
original_filename=file1.filename,
emission=emission,
transmission=transmission,
)
fluorescence = "mufluor" in set_labels
transmission = "mutrans" in set_labels
emission = "mutey" in set_labels

xsd = XASStandardDataInput(
fluorescence=fluorescence,
location=tmp_filename,
original_filename=file1.filename,
emission=emission,
transmission=transmission,
)

new_standard = XASStandard.model_validate(xs_input)
new_standard.xas_standard_data = XASStandardData.model_validate(xsd)
Expand Down
32 changes: 23 additions & 9 deletions xas-standards-client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,33 @@ import StandardSubmission from "./components/StandardSubmission.tsx";
import WelcomePage from "./components/WelcomePage.tsx";

import { MetadataProvider } from "./contexts/MetadataContext.tsx";
import { UserProvider } from "./contexts/UserContext.tsx";

import LogInPage from "./components/LogInPage.tsx";
import RequireAuth from "./components/RequireAuth.tsx";

function App() {
return (
<div className="mainwindow">
<Header />
<MetadataProvider>
<Routes>
<Route path="/" element={<WelcomePage />} />
<Route path="/view" element={<StandardViewer />} />
<Route path="/submit" element={<StandardSubmission />} />
{/* <Route path="/review" element={<ReviewPage />} /> */}
</Routes>
</MetadataProvider>
<UserProvider>
<Header />
<MetadataProvider>
<Routes>
<Route path="/" element={<WelcomePage />} />
<Route path="/view" element={<StandardViewer />} />
<Route
path="/submit"
element={
<RequireAuth>
<StandardSubmission />
</RequireAuth>
}
/>
<Route path="/login" element={<LogInPage />} />
{/* <Route path="/review" element={<ReviewPage />} /> */}
</Routes>
</MetadataProvider>
</UserProvider>
</div>
);
}
Expand Down
33 changes: 27 additions & 6 deletions xas-standards-client/src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import "./Header.css";
import { NavLink } from "react-router-dom";
import { useContext } from "react";
import { UserContext } from "../contexts/UserContext";

export default function Header() {
const user = useContext(UserContext);
console.log(user);
const loggedIn = user != null;

return (
<div className="header">
<h2 className="headerstart">XAS Standards</h2>
Expand All @@ -15,6 +21,16 @@ export default function Header() {
>
<div className="headernavitem"> Home </div>
</NavLink>
{loggedIn ? null : (
<NavLink
to="/login"
style={({ isActive }) => ({
color: isActive ? "#CCCCCC" : "gray",
})}
>
<div className="headernavitem"> Log In </div>
</NavLink>
)}
<NavLink
to="/view"
style={({ isActive }) => ({
Expand All @@ -23,12 +39,17 @@ export default function Header() {
>
<div className="headernavitem"> View </div>
</NavLink>
<NavLink
to="/submit"
style={({ isActive }) => ({ color: isActive ? "#CCCCCC" : "gray" })}
>
<div className="headernavitem"> Submit </div>
</NavLink>
{loggedIn ? (
<NavLink
to="/submit"
style={({ isActive }) => ({
color: isActive ? "#CCCCCC" : "gray",
})}
>
<div className="headernavitem"> Submit </div>
</NavLink>
) : null}
;
{/* <NavLink
to="/review"
style={({ isActive }) => ({ color: isActive ? "#CCCCCC" : "gray" })}
Expand Down
11 changes: 11 additions & 0 deletions xas-standards-client/src/components/LogInPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Link } from "react-router-dom";

function LogInPage() {
return (
<Link to="/login" reloadDocument={true}>
Log In
</Link>
);
}

export default LogInPage;
15 changes: 15 additions & 0 deletions xas-standards-client/src/components/RequireAuth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useContext, FC } from "react";
import { UserContext } from "../contexts/UserContext";

import { Navigate } from "react-router-dom";

const RequireAuth: FC<{ children: React.ReactElement }> = ({ children }) => {
const user = useContext(UserContext);

if (user === null) {
return <Navigate to={"/login"} replace />;
}
return children;
};

export default RequireAuth;
6 changes: 5 additions & 1 deletion xas-standards-client/src/components/StandardSubmission.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import InstrumentForm from "./InstrumentForm";
import CitationForm from "./CitationForm";
import AdditionalInformationForm from "./AdditionalInfoForm";

import { UserContext } from "../contexts/UserContext";

import { useNavigate } from "react-router-dom";

const standards_url = "/api/standards";
Expand All @@ -33,10 +35,12 @@ function StandardSubmission() {
const [beamlineHeader, setBeamlineHeader] = useState("");
const [doi, setDOI] = useState("");
const [date, setDate] = useState("");
const [licence, setLicence] = useState("");
const [licence, setLicence] = useState(licences[0]);
const [citation, setCitation] = useState("");
const [comments, setComments] = useState("");

const { user } = useContext(UserContext);

const navigate = useNavigate();

const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
Expand Down
14 changes: 14 additions & 0 deletions xas-standards-client/src/contexts/UserContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createContext } from "react";
import { User } from "../models";
import useUser from "../hooks/useUser";

const UserContext = createContext<User | null>(null);

function UserProvider(props: { children: React.ReactNode }) {
const { children } = props;
const user = useUser();

return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
}

export { UserContext, UserProvider };
1 change: 0 additions & 1 deletion xas-standards-client/src/hooks/useMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ function useMetadata(): AppMetadata {

useEffect(() => {
axios.get(metadata_url).then((res) => {
// console.log("BEAMLINE! " + res.data.name);
setAppMetdata(res.data);
});
}, []);
Expand Down
26 changes: 26 additions & 0 deletions xas-standards-client/src/hooks/useUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect, useState } from "react";
import axios, { AxiosResponse } from "axios";
import { User } from "../models";

const user_url = "/api/user";

function useUser(): User | null {
const [currentUser, setCurrentUser] = useState<User | null>(null);

useEffect(() => {
axios
.get(user_url)
.then((res: AxiosResponse) => {
// console.log(res.status);
setCurrentUser({ identifier: res.data.user });
})
.catch((error) => {
console.log(error.response);
setCurrentUser(null);
});
}, []);

return currentUser;
}

export default useUser;
4 changes: 4 additions & 0 deletions xas-standards-client/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ export interface AppMetadata {
edges: Edge[];
licences: string[];
}

export interface User {
identifier: string;
}
3 changes: 3 additions & 0 deletions xas-standards-client/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export default defineConfig({
"/api": {
target: "http://127.0.0.1:5000/",
},
"/login": {
target: "http://127.0.0.1:5000/",
},
},
},
});

0 comments on commit 92f7eba

Please sign in to comment.