Use generated client in web interface
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Armin Friedl 2020-07-12 18:48:12 +02:00
parent 8e0685819d
commit 39fd416b4a
Signed by: armin
GPG key ID: 48C726EEE7FBCBC8
17 changed files with 385 additions and 346 deletions

View file

@ -26,6 +26,36 @@ steps:
- java -jar fling-0.1.0-SNAPSHOT.jar
detach: true
- name: generate-clients
image: alpine
environment:
NEXUS_USER:
from_secret: nexus_user
NEXUS_PASSWORD:
from_secret: nexus_password
commands:
- apk add --update --no-cache openjdk11 npm
- sleep 20
- npm install @openapitools/openapi-generator-cli -g
# Python client
- openapi-generator generate
-i http://runservice:8080/v3/api-docs
-g python
-o flingclient.py
--enable-post-process-file
# JavaScript client
- openapi-generator generate
-i http://runservice:8080/v3/api-docs
-g javascript
--additional-properties projectName=@fling/flingclient,usePromises=true,npmRepository=https://nexus.friedl.net/repository/npm-private/
-o flingclient.js
--enable-post-process-file
- cd flingclient.js && npm install && npm run build
- echo "https://nexus.friedl.net/repository/npm-private/" >> .npmrc
- echo -n "_auth=" >> .npmrc && echo -n "$NEXUS_USER:$NEXUS_PASSWORD" | base64 >> .npmrc
- echo "email=dev@friedl.net" >> .npmrc
- npm publish
- name: build-web
image: node:latest
volumes:
@ -60,36 +90,6 @@ steps:
build_args:
- VERSION=0.1.0-snapshot
- name: generate-clients
image: alpine
environment:
NEXUS_USER:
from_secret: nexus_user
NEXUS_PASSWORD:
from_secret: nexus_password
commands:
- apk add --update --no-cache openjdk11 npm
- sleep 20
- npm install @openapitools/openapi-generator-cli -g
# Python client
- openapi-generator generate
-i http://runservice:8080/v3/api-docs
-g python
-o flingclient.py
--enable-post-process-file
# JavaScript client
- openapi-generator generate
-i http://runservice:8080/v3/api-docs
-g javascript
--additional-properties projectName=@fling/flingclient,usePromises=true,npmRepository=https://nexus.friedl.net/repository/npm-private/
-o flingclient.js
--enable-post-process-file
- cd flingclient.js && npm install && npm run build
- echo "https://nexus.friedl.net/repository/npm-private/" >> .npmrc
- echo -n "_auth=" >> .npmrc && echo -n "$NEXUS_USER:$NEXUS_PASSWORD" | base64 >> .npmrc
- echo "email=dev@friedl.net" >> .npmrc
- npm publish
volumes:
- name: m2-cache
host:

View file

@ -3,7 +3,7 @@ package net.friedl.fling;
import java.nio.file.Path;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.DeserializationFeature;
@ -19,7 +19,7 @@ public class FlingConfiguration {
@Bean
public PasswordEncoder passwordEncoder() {
return new Argon2PasswordEncoder();
return new BCryptPasswordEncoder();
}
@Bean

View file

@ -4,9 +4,8 @@ import static net.friedl.fling.security.FlingAuthorities.FLING_ADMIN;
import static org.springframework.security.config.Customizer.withDefaults;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@ -24,12 +23,11 @@ import net.friedl.fling.security.authentication.JwtAuthenticationFilter;
import net.friedl.fling.service.AuthorizationService;
@Slf4j
@Configuration
@EnableWebSecurity
@ConfigurationProperties(prefix = "fling.security")
@Getter
@Setter
public class FlingWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Value("fling.security.allowedOrigins")
private List<String> allowedOrigins;
private JwtAuthenticationFilter jwtAuthenticationFilter;

View file

@ -36,8 +36,8 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
String header = request.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
log.info("Anonymous request for {} {}?{}", request.getMethod(), request.getRequestURL(),
request.getQueryString());
log.info("Anonymous request for {} {}{}", request.getMethod(), request.getRequestURL(),
request.getQueryString() != null ? "?"+request.getQueryString(): "");
filterChain.doFilter(request, response);
return;
}
@ -47,8 +47,8 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
SecurityContext securityContext = SecurityContextHolder.getContext();
if (securityContext.getAuthentication() == null) {
log.info("Authenticating request for {} {}?{}", request.getMethod(), request.getRequestURL(),
request.getQueryString());
log.info("Authenticating request for {} {}{}", request.getMethod(), request.getRequestURL(),
request.getQueryString() != null ? "?"+request.getQueryString(): "");
FlingToken token = authenticationService.parseAuthentication(authToken);
log.info("Authenticated as {}", token.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.joining(",")));

View file

@ -5,6 +5,7 @@ import java.time.Instant;
import java.util.Date;
import java.util.Optional;
import java.util.UUID;
import javax.persistence.EntityNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
@ -70,6 +71,8 @@ public class AuthenticationService {
public Optional<String> authenticate(UserAuthDto userAuth) {
log.info("Authenticating for fling [.shareId={}]", userAuth.getShareId());
FlingEntity flingEntity = flingRepository.findByShareId(userAuth.getShareId());
if(flingEntity == null) { throw new EntityNotFoundException("No entity for shareId="+userAuth.getShareId()); }
String providedAuthCodeHash = passwordEncoder.encode(userAuth.getAuthCode());
String actualAuthCodeHash = flingEntity.getAuthCode();

View file

@ -1,6 +1,7 @@
package net.friedl.fling.service;
import java.util.UUID;
import javax.persistence.EntityNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.stereotype.Service;
@ -69,6 +70,7 @@ public class AuthorizationService {
public boolean allowFlingAccessByShareId(String shareId, AbstractAuthenticationToken token) {
FlingEntity flingEntity = flingRepository.findByShareId(shareId);
if(flingEntity == null) { throw new EntityNotFoundException("No entity for shareId="+shareId); }
return allowFlingAccess(flingEntity.getId(), token);
}

View file

@ -5,6 +5,7 @@ import java.util.Base64;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.keygen.KeyGenerators;
@ -89,6 +90,7 @@ public class FlingService {
public FlingDto getByShareId(String shareId) {
FlingEntity flingEntity = flingRepository.findByShareId(shareId);
if(flingEntity == null) { throw new EntityNotFoundException("No entity for shareId="+shareId); }
return flingMapper.map(flingEntity);
}

View file

@ -28,8 +28,8 @@ fling:
- "http://localhost:3000"
- "http://localhost:5000"
- "http://10.0.2.2:5000"
admin-name: "adminName"
admin-password: "adminPassword"
admin-name: "admin"
admin-password: "123"
signing-key: "changeitchangeitchangeitchangeit"
jwt-expiration: "180000"
api:

View file

@ -11,6 +11,7 @@ import java.security.Key;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import javax.persistence.EntityNotFoundException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
@ -115,6 +116,18 @@ public class AuthenticationServiceTest {
assertThat(authenticationService.authenticate(userAuthDto), not(equalTo(Optional.empty())));
}
@Test
public void authenticate_noFlingForShareId_throws() {
UserAuthDto userAuthDto = UserAuthDto.builder()
.authCode("authCode")
.shareId("doesNotExist").build();
when(flingRepository.findByShareId(any(String.class))).thenReturn(null);
when(passwordEncoder.encode(any(String.class))).thenReturn("authCodeHash");
assertThrows(EntityNotFoundException.class, () -> authenticationService.authenticate(userAuthDto));
}
@Test
public void parseAuthentication_owner_AdminAuthority() {
Jws<Claims> jwsClaims = new DefaultJws<>(new DefaultJwsHeader(),

View file

@ -1,10 +1,13 @@
package net.friedl.fling.service;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.UUID;
import javax.persistence.EntityNotFoundException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
@ -113,4 +116,14 @@ public class AuthorizationServiceTest {
FlingToken flingToken = new FlingToken(new FlingUserAuthority(new UUID(0, 0)), "jwtToken");
assertTrue(authorizationService.allowFlingAccess(new UUID(0, 0), flingToken));
}
@Test
public void allowFlingAccessByShareId_noFlingForShareId_throw() {
FlingToken flingToken = new FlingToken(new FlingUserAuthority(new UUID(0, 0)), "jwtToken");
when(flingRepository.findByShareId(any(String.class))).thenReturn(null);
assertThrows(EntityNotFoundException.class,
() -> authorizationService.allowFlingAccessByShareId("doesNotExist", flingToken));
}
}

View file

@ -9,6 +9,7 @@ import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.emptyOrNullString;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -17,6 +18,7 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.persistence.EntityNotFoundException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -161,6 +163,13 @@ public class FlingServiceTest {
assertThat(foundFling.getShareId(), equalTo("shareId2"));
}
@Test
public void getByShareId_noFlingForShareId_throws() {
when(flingRepository.findByShareId(any(String.class))).thenReturn(null);
assertThrows(EntityNotFoundException.class, () -> flingService.getByShareId("doesNotExist"));
}
@Test
public void delete_deletesFromArchiveAndDb() throws IOException {
UUID testId = UUID.randomUUID();

View file

@ -1,2 +1,2 @@
REACT_APP_API=https://fling.friedl.net/api
REACT_APP_API=https://fling.friedl.net
REACT_APP_LOGLEVEL=warn

View file

@ -1,2 +1,2 @@
REACT_APP_API=http://localhost:8080/api
REACT_APP_API=http://localhost:8080/
REACT_APP_LOGLEVEL=trace

View file

@ -4,6 +4,135 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/cli": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.10.4.tgz",
"integrity": "sha512-xX99K4V1BzGJdQANK5cwK+EpF1vP9gvqhn+iWvG+TubCjecplW7RSQimJ2jcCvu6fnK5pY6mZMdu6EWTj32QVA==",
"requires": {
"chokidar": "^2.1.8",
"commander": "^4.0.1",
"convert-source-map": "^1.1.0",
"fs-readdir-recursive": "^1.1.0",
"glob": "^7.0.0",
"lodash": "^4.17.13",
"make-dir": "^2.1.0",
"slash": "^2.0.0",
"source-map": "^0.5.0"
},
"dependencies": {
"binary-extensions": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
"optional": true
},
"chokidar": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
"integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
"optional": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
"normalize-path": "^3.0.0",
"path-is-absolute": "^1.0.0",
"readdirp": "^2.2.1",
"upath": "^1.1.1"
}
},
"commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="
},
"fsevents": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"optional": true,
"requires": {
"nan": "^2.12.1"
}
},
"glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
"optional": true,
"requires": {
"is-glob": "^3.1.0",
"path-dirname": "^1.0.0"
},
"dependencies": {
"is-glob": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
"optional": true,
"requires": {
"is-extglob": "^2.1.0"
}
}
}
},
"is-binary-path": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
"integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
"optional": true,
"requires": {
"binary-extensions": "^1.0.0"
}
},
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"optional": true
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"optional": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"readdirp": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
"integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
"optional": true,
"requires": {
"graceful-fs": "^4.1.11",
"micromatch": "^3.1.10",
"readable-stream": "^2.0.2"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"optional": true,
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"@babel/code-frame": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
@ -1078,6 +1207,15 @@
"resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz",
"integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg=="
},
"@fling/flingclient": {
"version": "0.1.0-snapshot",
"resolved": "https://nexus.friedl.net/repository/npm-private/@fling/flingclient/-/flingclient-0.1.0-snapshot.tgz",
"integrity": "sha512-KXeJE/tTCi+IRBZ8pBeFLFEn7GDBWw/aIDj4xaofjw6S0DFEpw5TwW+Oh45NALk/SEiR4DKBuG/sfgiHrpZwLA==",
"requires": {
"@babel/cli": "^7.0.0",
"superagent": "3.7.0"
}
},
"@hapi/address": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
@ -3808,6 +3946,11 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"cookiejar": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz",
"integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA=="
},
"copy-concurrently": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
@ -5838,6 +5981,11 @@
"mime-types": "^2.1.12"
}
},
"formidable": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz",
"integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q=="
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@ -5907,6 +6055,11 @@
"minipass": "^3.0.0"
}
},
"fs-readdir-recursive": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
"integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA=="
},
"fs-write-stream-atomic": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
@ -6154,12 +6307,12 @@
}
},
"globule": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
"integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz",
"integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==",
"requires": {
"glob": "~7.1.1",
"lodash": "~4.17.12",
"lodash": "~4.17.10",
"minimatch": "~3.0.2"
}
},
@ -6512,9 +6665,9 @@
"integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q="
},
"http-proxy": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz",
"integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==",
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"requires": {
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
@ -8132,9 +8285,9 @@
}
},
"js-base64": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
"integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ=="
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.3.tgz",
"integrity": "sha512-fiUvdfCaAXoQTHdKMgTvg6IkecXDcVz6V5rlftUTclF9IKBjMizvSdQaCl/z/6TApDeby5NL+axYou3i0mu1Pg=="
},
"js-tokens": {
"version": "4.0.0",
@ -8448,9 +8601,9 @@
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
"lodash._reinterpolate": {
"version": "3.0.0",
@ -9270,9 +9423,9 @@
"integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ=="
},
"node-sass": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.0.tgz",
"integrity": "sha512-AxqU+DFpk0lEz95sI6jO0hU0Rwyw7BXVEv6o9OItoXLyeygPeaSpiV4rwQb10JiTghHaa0gZeD21sz+OsQluaw==",
"version": "4.14.1",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz",
"integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==",
"requires": {
"async-foreach": "^0.1.3",
"chalk": "^1.1.1",
@ -9288,7 +9441,7 @@
"node-gyp": "^3.8.0",
"npmlog": "^4.0.0",
"request": "^2.88.0",
"sass-graph": "^2.2.4",
"sass-graph": "2.2.5",
"stdout-stream": "^1.4.0",
"true-case-path": "^1.0.2"
},
@ -12229,219 +12382,14 @@
"integrity": "sha512-vTxrZz4dX5W86M6oVWVdOVe72ZiPs41Oi7Z6Km4W5Turyz28mrXSJhhEBZoRtzJWIv3833WKVwLSDWWkEfupMg=="
},
"sass-graph": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
"integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz",
"integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==",
"requires": {
"glob": "^7.0.0",
"lodash": "^4.0.0",
"scss-tokenizer": "^0.2.3",
"yargs": "^7.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"camelcase": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo="
},
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wrap-ansi": "^2.0.0"
}
},
"find-up": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
"requires": {
"path-exists": "^2.0.0",
"pinkie-promise": "^2.0.0"
}
},
"get-caller-file": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
},
"invert-kv": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"requires": {
"number-is-nan": "^1.0.0"
}
},
"lcid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
"requires": {
"invert-kv": "^1.0.0"
}
},
"load-json-file": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^2.2.0",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0",
"strip-bom": "^2.0.0"
}
},
"os-locale": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
"requires": {
"lcid": "^1.0.0"
}
},
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
"requires": {
"error-ex": "^1.2.0"
}
},
"path-exists": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
"requires": {
"pinkie-promise": "^2.0.0"
}
},
"path-type": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
"requires": {
"graceful-fs": "^4.1.2",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0"
}
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
},
"read-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
"integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
"requires": {
"load-json-file": "^1.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^1.0.0"
}
},
"read-pkg-up": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
"integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
"requires": {
"find-up": "^1.0.0",
"read-pkg": "^1.0.0"
}
},
"require-main-filename": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"strip-bom": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
"requires": {
"is-utf8": "^0.2.0"
}
},
"which-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8="
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1"
}
},
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
},
"yargs": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
"integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
"requires": {
"camelcase": "^3.0.0",
"cliui": "^3.2.0",
"decamelize": "^1.1.1",
"get-caller-file": "^1.0.1",
"os-locale": "^1.4.0",
"read-pkg-up": "^1.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^1.0.2",
"which-module": "^1.0.0",
"y18n": "^3.2.1",
"yargs-parser": "^5.0.0"
}
},
"yargs-parser": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
"integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
"requires": {
"camelcase": "^3.0.0"
}
}
"yargs": "^13.3.2"
}
},
"sass-loader": {
@ -13480,6 +13428,60 @@
}
}
},
"superagent": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-3.7.0.tgz",
"integrity": "sha512-/8trxO6NbLx4YXb7IeeFTSmsQ35pQBiTBsLNvobZx7qBzBeHYvKCyIIhW2gNcWbLzYxPAjdgFbiepd8ypwC0Gw==",
"requires": {
"component-emitter": "^1.2.0",
"cookiejar": "^2.1.0",
"debug": "^3.1.0",
"extend": "^3.0.0",
"form-data": "^2.3.1",
"formidable": "^1.1.1",
"methods": "^1.1.1",
"mime": "^1.4.1",
"qs": "^6.5.1",
"readable-stream": "^2.0.5"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"requires": {
"ms": "^2.1.1"
}
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

View file

@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@fling/flingclient": "0.1.0-snapshot",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
@ -11,7 +12,7 @@
"core-js": "^3.6.5",
"jwt-decode": "^2.2.0",
"loglevel": "^1.6.8",
"node-sass": "^4.14.0",
"node-sass": "^4.14.1",
"normalize.css": "^8.0.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",

View file

@ -1,88 +1,59 @@
import log from 'loglevel';
import React, {useState, useEffect} from 'react';
import React, {useState} from 'react';
import {useHistory, useLocation} from 'react-router-dom';
import request, {setAuth} from '../../util/request';
import Error from './Error';
import {fc, AuthClient} from '../../util/fc';
export default function Login() {
const [errors, setErrors] = useState([]);
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const history = useHistory();
const location = useLocation();
const { from } = location.state || { from: { pathname: "/admin" } };
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const history = useHistory();
const location = useLocation();
const { from } = location.state || { from: { pathname: "/admin" } };
useEffect(() => setAuth(null), []);
return (
<div className="container-center">
<div>
<Error errors={errors} clearErrors={clearErrors} >
<form className="login-form" onSubmit={handleSubmit}>
<div className="form-group">
<label className="form-label" htmlFor="username">Username</label>
<input className="form-input" id="username" name="username" type="text" placeholder="Username"
value={username} onChange={handleChange} />
</div>
<div className="form-group">
<label className="form-label" htmlFor="password">Password</label>
<input className="form-input" id="password" name="password" type="password" placeholder={"*".repeat(18)}
value={password} onChange={handleChange} />
</div>
<div className="login-action-row">
<div className="form-group">
<label className="form-switch input-sm">
<input type="checkbox" />
<i className="form-icon" /> Remember me
</label>
</div>
<button className="btn btn-primary" type="submit">Sign In</button>
</div>
</form>
</Error>
<p className="login-footer">Ready. Set. Fling.</p>
return (
<div className="container-center">
<div>
<form className="login-form" onSubmit={handleSubmit}>
<div className="form-group">
<label className="form-label" htmlFor="username">Username</label>
<input className="form-input" id="username" name="username" type="text" placeholder="Username"
value={username} onChange={ev => setUsername(ev.currentTarget.value)} />
</div>
<div className="form-group">
<label className="form-label" htmlFor="password">Password</label>
<input className="form-input" id="password" name="password" type="password" placeholder={"*".repeat(18)}
value={password} onChange={ev => setPassword(ev.currentTarget.value)} />
</div>
<div className="login-action-row">
<div className="form-group">
<label className="form-switch input-sm">
<input type="checkbox" />
<i className="form-icon" /> Remember me
</label>
</div>
<button className="btn btn-primary" type="submit">Sign In</button>
</div>
</form>
</div>
);
<p className="login-footer">Ready. Set. Fling.</p>
</div>
function handleSubmit(ev) {
ev.preventDefault();
</div>
);
request.post("/auth/owner", {'username': username, 'password': password})
.then(response => {
log.info("Logged in successfully");
setAuth(response.data);
history.replace(from);
})
.catch(error => {
log.error(error);
let response = error.response;
response.data && response.data.message && setErrors( prev => [response.data.message, ...prev] );
});
};
function handleSubmit(ev) {
ev.preventDefault();
function handleChange(ev) {
let name = ev.target.name;
let val = ev.target.value;
let authClient = new AuthClient();
let opt = {adminAuth: new fc.AdminAuth(username, password)};
switch(name) {
case "username":
setUsername(val);
break;
case "password":
setPassword(val);
break;
default:
log.error(`Cannot handle change ${name}`);
break;
}
};
function clearErrors() {
setErrors([]);
}
authClient.authenticateOwner(opt)
.then(response => {
log.info("Login successful");
sessionStorage.setItem['token'] = response;
log.info("Returning back to", from);
history.replace(from);
}).catch(log.error);
};
}

25
web/fling/src/util/fc.js Normal file
View file

@ -0,0 +1,25 @@
import * as fc from '@fling/flingclient';
let clientConfig = (token) => {
let config = new fc.ApiClient();
config.basePath = process.env.REACT_APP_API.replace(/\/+$/, '');
token = token || sessionStorage.getItem['token'];
if(token) { config.authentications['bearer'].accessToken = token; }
return config;
};
function FlingClient(token) {
return new fc.FlingApi(clientConfig(token));
}
function ArtifactClient(token) {
return new fc.ArtifactApi(clientConfig(token));
}
function AuthClient() {
return new fc.AuthApi(clientConfig());
}
export {FlingClient, ArtifactClient, AuthClient, fc};