0.1 #1

Merged
armin merged 30 commits from 0.1 into master 2020-07-26 00:51:55 +00:00
7 changed files with 105 additions and 67 deletions
Showing only changes of commit bcfbf349cd - Show all commits

View file

@ -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 {

View file

@ -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;

View file

@ -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';
@ -13,53 +13,64 @@ import FlingUser from './components/user/FlingUser';
import LandingPage from './components/LandingPage';
export default () => {
return (
<Switch>
<Route exact path="/" component={LandingPage} />
return (
<Switch>
<Route exact path="/" component={LandingPage} />
<Route exact path="/admin/login" component={Login} />
<OwnerRoute exact path="/admin"><FlingAdmin /></OwnerRoute>
<OwnerRoute path="/admin/:fling"><FlingAdmin /></OwnerRoute>
<Route exact path="/admin/login" component={Login} />
<OwnerRoute exact path="/admin"><FlingAdmin /></OwnerRoute>
<OwnerRoute path="/admin/:fling"><FlingAdmin /></OwnerRoute>
<Route exact path="/unlock" component={Unlock} />
<UserRoute path="/f/:shareId"><FlingUser /></UserRoute>
<Route exact path="/unlock" component={Unlock} />
<UserRoute path="/f/:shareId"><FlingUser /></UserRoute>
<Route match="*">Not implemented</Route>
</Switch>
);
<Route match="*">Not implemented</Route>
</Switch>
);
}
// 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 }) {
return (
<Route
{...rest}
render={({ location }) => {
log.info(request.defaults);
if(isOwner()) { return children; }
else { return <Redirect to={{pathname: "/admin/login", state: {from: location}}} />; }
}}
/>
);
log.info(`Routing request for ${rest['path']}`);
return (
<Route
{...rest}
render={({ location }) => {
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 }) {
return (
<Route
{...rest}
render={({ match, location }) => {
log.info(request.defaults);
log.info(match);
log.info(location);
let x = {from: location, shareId: match.params.shareId};
log.debug(`Routing request for ${rest['path']}`);
return (
<Route
{...rest}
render={({ match, location }) => {
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} } />; }
}}
/>
);
}

View file

@ -1,37 +1,34 @@
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();
const [flings, setFlings] = useState([]);
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} />;
newFlings.push(flingTile);
let flingTile = <FlingTile fling={fling} key={fling.id} />;
newFlings.push(flingTile);
}
setFlings(newFlings);
})(); } , []);
}).catch(log.error);
}, []);
return(
<div className="panel">
{log.info(`Got active fling: ${props.activeFling}`)}
<div className="panel-header p-2">
<h5>My Flings</h5>
</div>
<div className="panel-body p-0">
{flings}
</div>
</div>
);
async function refreshFlingList() {
}
return(
<div className="panel">
{log.info(`Got active fling: ${props.activeFling}`)}
<div className="panel-header p-2">
<h5>My Flings</h5>
</div>
<div className="panel-body p-0">
{flings}
</div>
</div>
);
}

View file

@ -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);
};

View file

@ -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
View 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;