diff --git a/service/fling/src/main/java/net/friedl/fling/service/AuthenticationService.java b/service/fling/src/main/java/net/friedl/fling/service/AuthenticationService.java index e15fb73..2617716 100644 --- a/service/fling/src/main/java/net/friedl/fling/service/AuthenticationService.java +++ b/service/fling/src/main/java/net/friedl/fling/service/AuthenticationService.java @@ -15,12 +15,14 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.JwtParser; import io.jsonwebtoken.Jwts; import lombok.extern.slf4j.Slf4j; import net.friedl.fling.model.dto.AdminAuthDto; +import net.friedl.fling.model.dto.FlingDto; import net.friedl.fling.model.dto.UserAuthDto; import net.friedl.fling.persistence.entities.FlingEntity; import net.friedl.fling.persistence.entities.TokenEntity; @@ -84,10 +86,14 @@ public class AuthenticationService { throw new EntityNotFoundException("No entity for shareId=" + userAuth.getShareId()); } - String providedAuthCodeHash = passwordEncoder.encode(userAuth.getAuthCode()); + String providedAuthCode = userAuth.getAuthCode(); String actualAuthCodeHash = flingEntity.getAuthCode(); + + Boolean isProtected = StringUtils.hasText(actualAuthCodeHash); - if (!actualAuthCodeHash.equals(providedAuthCodeHash)) { + if(!isProtected) log.debug("No protection set for fling [.shareId={}]"); + + if (isProtected && !passwordEncoder.matches(providedAuthCode, actualAuthCodeHash)) { log.debug("Authentication failed for fling [.shareId={}]", userAuth.getShareId()); return Optional.empty(); } @@ -96,6 +102,7 @@ public class AuthenticationService { return Optional.of( getJwtBuilder() .setSubject("user") + .claim("shareId", flingEntity.getShareId()) .claim("id", flingEntity.getId()) .compact()); diff --git a/service/fling/src/test/java/net/friedl/fling/service/AuthenticationServiceTest.java b/service/fling/src/test/java/net/friedl/fling/service/AuthenticationServiceTest.java index e829625..4413353 100644 --- a/service/fling/src/test/java/net/friedl/fling/service/AuthenticationServiceTest.java +++ b/service/fling/src/test/java/net/friedl/fling/service/AuthenticationServiceTest.java @@ -118,6 +118,16 @@ public class AuthenticationServiceTest { assertThat(authenticationService.authenticate(userAuthDto), equalTo(Optional.empty())); } + @Test + public void authenticate_authCodeEmpty_ok() { + FlingEntity flingEntity = new FlingEntity(); + UserAuthDto userAuthDto = new UserAuthDto("shareId", ""); + + when(flingRepository.findByShareId(any(String.class))).thenReturn(flingEntity); + + assertThat(authenticationService.authenticate(userAuthDto), not(equalTo(Optional.empty()))); + } + @Test public void authenticate_authCodeEquals_ok() { FlingEntity flingEntity = new FlingEntity(); @@ -130,6 +140,7 @@ public class AuthenticationServiceTest { when(flingRepository.findByShareId(any(String.class))).thenReturn(flingEntity); when(passwordEncoder.encode(any(String.class))).thenReturn("authCodeHash"); + when(passwordEncoder.matches("authCode", "authCodeHash")).thenReturn(true); assertThat(authenticationService.authenticate(userAuthDto), not(equalTo(Optional.empty()))); } diff --git a/web/fling/src/App.jsx b/web/fling/src/App.jsx index 4c9f970..942dcab 100644 --- a/web/fling/src/App.jsx +++ b/web/fling/src/App.jsx @@ -87,7 +87,7 @@ function UserRoute({ children, ...rest }) { let authorized = jwt.hasSubject("admin") - || ( jwt.hasSubject("user") && jwt.hasClaim("id", state['shareId']) ); + || ( jwt.hasSubject("user") && jwt.hasClaim("shareId", state['shareId']) ); if (authorized) { return children; } else { return ; } diff --git a/web/fling/src/components/user/Error.jsx b/web/fling/src/components/user/Error.jsx deleted file mode 100644 index be4d4c4..0000000 --- a/web/fling/src/components/user/Error.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, {useState} from 'react'; -import classNames from 'classnames'; - -import log from 'loglevel'; - -export default (props) => { - function renderError() { - return ( -
- -
Ooops!
-
  • - { props.errors.map( (err, idx) =>
      {err}
    ) } -
  • -
    - ); - } - - return ( - <> - { props.errors.length > 0 && !props.below ? renderError() : "" } - { props.children } - { props.errors.length > 0 && props.below ? renderError() : "" } - - ); -} diff --git a/web/fling/src/components/user/FlingUser.jsx b/web/fling/src/components/user/FlingUser.jsx index ec13db7..b76a37f 100644 --- a/web/fling/src/components/user/FlingUser.jsx +++ b/web/fling/src/components/user/FlingUser.jsx @@ -1,26 +1,27 @@ -import React, {useState, useEffect} from 'react'; +import React, { useState, useEffect } from 'react'; -import {useParams} from 'react-router-dom'; +import { useParams } from 'react-router-dom'; -import {flingClient} from '../../util/flingclient'; +import { FlingClient } from '../../util/fc'; import DirectDownload from './DirectDownload'; import FlingUserList from './FlingUserList'; export default function FlingUser() { - let { shareId } = useParams(); - let [fling, setFling] = useState({}); + let { shareId } = useParams(); + let [fling, setFling] = useState({}); - useEffect(() => { - flingClient.getFlingByShareId(shareId) - .then(f => setFling(f)); - }, [shareId]); + useEffect(() => { + let flingClient = new FlingClient(); + flingClient.getFlingByShareId(shareId) + .then(f => setFling(f)); + }, [shareId]); - return( -
    - {fling.sharing && fling.sharing.directDownload - ? - : } -
    - ); + return ( +
    + {fling.sharing && fling.sharing.directDownload + ? + : } +
    + ); } diff --git a/web/fling/src/components/user/Unlock.jsx b/web/fling/src/components/user/Unlock.jsx index 3dec25c..799be63 100644 --- a/web/fling/src/components/user/Unlock.jsx +++ b/web/fling/src/components/user/Unlock.jsx @@ -1,61 +1,60 @@ import log from 'loglevel'; -import React, {useState, useEffect} from 'react'; -import {useHistory, useLocation} from 'react-router-dom'; +import React, { useState, useEffect } from 'react'; +import { useHistory, useLocation } from 'react-router-dom'; -import request, {setAuth} from '../../util/request'; +import { AuthClient, fc } from '../../util/fc'; export default function Unlock() { - const [authCode, setAuthCode] = useState(""); - const history = useHistory(); - const location = useLocation(); - const { from, shareId } = location.state || { from: { pathname: "/admin" }, shareId: "" }; + const [authCode, setAuthCode] = useState(""); + const history = useHistory(); + const location = useLocation(); + const { from, shareId } = location.state || { from: { pathname: "/admin" }, shareId: "" }; - useEffect(() => setAuth(null), []); + useEffect(() => { + let authClient = new AuthClient(); + let userAuth = new fc.UserAuth(location.state.shareId, "") - useEffect(() => { - request.post("/auth/user", {"shareId": location.state.shareId}) - .then(response => { - log.info("Fling is not protected. Logged in successfully."); - setAuth(response.data); - history.replace(location.state.from); - }) - .catch(err => {/* ignored */}); - }, [location, history]); + authClient.authenticateUser({ 'userAuth': userAuth }) + .then(response => { + log.info("Fling is not protected. Logged in successfully."); + sessionStorage.setItem('token', response); + history.replace(location.state.from); + }).catch(error => { + log.info("Fling protected. Could not unlock without code.") + }); + }, [location, history]); - return ( -
    -
    -
    -

    This Fling is locked.

    -
    -
    -
    -
    - - -
    -
    -
    -
    + return ( +
    +
    +
    +

    This Fling is locked.

    - ); +
    +
    +
    + setAuthCode(ev.target.value)} /> + +
    +
    +
    +
    +
    + ); - function handleSubmit(ev) { - ev.preventDefault(); + function handleSubmit(ev) { + ev.preventDefault(); + let authClient = new AuthClient(); + let userAuth = new fc.UserAuth(shareId, authCode) - request.post("/auth/user", {"shareId": shareId, "code": authCode}) - .then(response => { - log.info("Logged in successfully"); - setAuth(response.data); - history.replace(from); - }) - .catch(error => { - log.error(error); - }); - }; - - function handleChange(ev) { - let val = ev.target.value; - setAuthCode(val); - }; + authClient.authenticateUser({ 'userAuth': userAuth }) + .then(response => { + log.info("Logged in successfully"); + sessionStorage.setItem('token', response); + history.replace(from); + }).catch(error => { + log.error(error); + }); + }; }