0.1 #1
7 changed files with 105 additions and 67 deletions
|
@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.media.Content;
|
|||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import net.friedl.fling.model.dto.ArtifactDto;
|
||||
import net.friedl.fling.service.ArtifactService;
|
||||
|
@ -28,6 +29,7 @@ import net.friedl.fling.service.archive.ArchiveService;
|
|||
@RestController
|
||||
@RequestMapping("/api/artifacts")
|
||||
@Tag(name = "artifact", description = "Operations on /api/artifacts")
|
||||
@SecurityRequirement(name = "bearer")
|
||||
@Validated
|
||||
public class ArtifactController {
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import net.friedl.fling.model.dto.ArtifactDto;
|
||||
import net.friedl.fling.model.dto.FlingDto;
|
||||
|
@ -32,6 +33,7 @@ import net.friedl.fling.service.archive.ArchiveService;
|
|||
@RestController
|
||||
@RequestMapping("/api/fling")
|
||||
@Tag(name = "fling", description = "Operations on /api/fling")
|
||||
@SecurityRequirement(name = "bearer")
|
||||
public class FlingController {
|
||||
|
||||
private FlingService flingService;
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
|
||||
import {Switch, Route, Redirect} from "react-router-dom";
|
||||
|
||||
import request, {isOwner, isUser} from './util/request';
|
||||
import jwt from './util/jwt.js';
|
||||
|
||||
import Login from './components/admin/Login';
|
||||
import FlingAdmin from './components/admin/FlingAdmin';
|
||||
|
@ -29,36 +29,47 @@ export default () => {
|
|||
);
|
||||
}
|
||||
|
||||
// A wrapper for <Route> that redirects to the login
|
||||
// screen if you're not yet authenticated.
|
||||
/*
|
||||
* A wrapper for <Route> that redirects to the login screen if no admin
|
||||
* authentication token was found.
|
||||
*
|
||||
* Note that the token check is purely client-side. It provides no actual
|
||||
* protection! It is hence possible to reach the admin site with some small
|
||||
* amount of trickery. Without a valid token no meaningful actions are possible
|
||||
* on the admin page though.
|
||||
*/
|
||||
function OwnerRoute({ children, ...rest }) {
|
||||
log.info(`Routing request for ${rest['path']}`);
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={({ location }) => {
|
||||
log.info(request.defaults);
|
||||
if(isOwner()) { return children; }
|
||||
if (jwt.hasSubject("admin")) { return children; }
|
||||
else { return <Redirect to={{pathname: "/admin/login", state: {from: location}}} />; }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// A wrapper for <Route> that redirects to the unlock
|
||||
// screen if the fling is protected
|
||||
/* A wrapper for <Route> that redirects to the unlock screen if no authorized token
|
||||
* was found.
|
||||
*
|
||||
* Note that the token check is purely client-side. It provides no actual
|
||||
* protection! It is hence possible to reach the target site with some small
|
||||
* amount of trickery. Without a valid token, no meaningful actions are possible
|
||||
* on the target page though - this must be checked server side.
|
||||
*/
|
||||
function UserRoute({ children, ...rest }) {
|
||||
log.debug(`Routing request for ${rest['path']}`);
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={({ match, location }) => {
|
||||
log.info(request.defaults);
|
||||
log.info(match);
|
||||
log.info(location);
|
||||
let x = {from: location, shareId: match.params.shareId};
|
||||
let state = {from: location, shareId: match.params.shareId};
|
||||
let authorized = jwt.hasSubject("admin") || (jwt.hasSubject("user") && jwt.hasClaim("id", state['shareId']));
|
||||
|
||||
if(isOwner()) { return children; }
|
||||
else if(isUser(match.params.shareId)) { return children; }
|
||||
else { return <Redirect to={ {pathname: "/unlock", state: x} } />; }
|
||||
if (authorized) { return children; }
|
||||
else { return <Redirect to={ {pathname: "/unlock", state: state} } />; }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
import log from 'loglevel';
|
||||
import React, {useState, useEffect} from 'react';
|
||||
|
||||
import {flingClient} from '../../util/flingclient';
|
||||
import {FlingClient} from '../../util/fc';
|
||||
|
||||
import FlingTile from './FlingTile';
|
||||
|
||||
export default function FlingList(props) {
|
||||
const [flings, setFlings] = useState([]);
|
||||
useEffect(() => { (async () => {
|
||||
let flings = await flingClient.getFlings();
|
||||
useEffect(() => {
|
||||
let flingClient = new FlingClient();
|
||||
flingClient.getFlings()
|
||||
.then(flings => {
|
||||
let newFlings = [];
|
||||
|
||||
for (let fling of flings) {
|
||||
let flingTile = <FlingTile fling={fling}
|
||||
key={fling.id}
|
||||
refreshFlingListFn={refreshFlingList} />;
|
||||
let flingTile = <FlingTile fling={fling} key={fling.id} />;
|
||||
newFlings.push(flingTile);
|
||||
}
|
||||
setFlings(newFlings);
|
||||
})(); } , []);
|
||||
}).catch(log.error);
|
||||
}, []);
|
||||
|
||||
return(
|
||||
<div className="panel">
|
||||
|
@ -31,7 +31,4 @@ export default function FlingList(props) {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
async function refreshFlingList() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ export default function Login() {
|
|||
authClient.authenticateOwner(opt)
|
||||
.then(response => {
|
||||
log.info("Login successful");
|
||||
sessionStorage.setItem['token'] = response;
|
||||
log.info("Returning back to", from);
|
||||
sessionStorage.setItem('token', response);
|
||||
log.debug("Returning back to", from);
|
||||
history.replace(from);
|
||||
}).catch(log.error);
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ let clientConfig = (token) => {
|
|||
let config = new fc.ApiClient();
|
||||
config.basePath = process.env.REACT_APP_API.replace(/\/+$/, '');
|
||||
|
||||
token = token || sessionStorage.getItem['token'];
|
||||
token = token || sessionStorage.getItem('token');
|
||||
if(token) { config.authentications['bearer'].accessToken = token; }
|
||||
|
||||
return config;
|
||||
|
|
26
web/fling/src/util/jwt.js
Normal file
26
web/fling/src/util/jwt.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Utilities for working with JWT tokens
|
||||
*/
|
||||
import jwtDecode from 'jwt-decode';
|
||||
|
||||
let jwt = {
|
||||
/**
|
||||
* Check the session store token for an arbitrary claim
|
||||
*/
|
||||
hasClaim: function (name, value) {
|
||||
if(!sessionStorage.getItem('token')) return false;
|
||||
let tokenPayload = jwtDecode(sessionStorage.getItem('token'));
|
||||
return tokenPayload[name] === value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the session store token for a subject
|
||||
*/
|
||||
hasSubject: function (value) {
|
||||
if(!sessionStorage.getItem('token')) return false;
|
||||
let tokenPayload = jwtDecode(sessionStorage.getItem('token'));
|
||||
return tokenPayload['sub'] === value;
|
||||
}
|
||||
};
|
||||
|
||||
export default jwt;
|
Loading…
Reference in a new issue