@Override public void filter(final ClientRequestContext clientRequestContext) { final ZonkyApiToken supplied = token == null ? null : token.get(); logger.trace("Using token #{}.", supplied); final char[] t = supplied == null ? AuthenticatedFilter.EMPTY_TOKEN : supplied.getAccessToken(); this.setRequestHeader("Authorization", "Bearer " + String.valueOf(t)); super.filter(clientRequestContext); } }
private ZonkyApiToken actuallyRefreshOrLogin(final ZonkyApiToken token) { if (token.isExpired()) { LOGGER.debug("Found expired token #{}.", token.getId()); return login(); } LOGGER.debug("Current token #{} expiring on {}.", token.getId(), token.getExpiresOn()); try { return refresh(token); } catch (final Exception ex) { LOGGER.debug("Failed refreshing access token, falling back to password.", ex); return login(); } }
public boolean isExpired() { return willExpireIn(Duration.ZERO); }
public ZonkyApiToken refresh(final ZonkyApiToken token) { return api.call(a -> a.refresh(String.valueOf(token.getRefreshToken()), ZonkyApiToken.REFRESH_TOKEN_STRING, token.getScope())); } }
private static ZonkyApiToken getTokenExpiringIn(final Duration duration) { return new ZonkyApiToken(UUID.randomUUID().toString(), UUID.randomUUID().toString(), OffsetDateTime.ofInstant(DateUtil.now().minus(Duration.ofMinutes(5)).plus(duration), Defaults.ZONE_ID)); }
@Test void fresh() { final ZonkyApiToken token = new ZonkyApiToken(); assertThat(token.getObtainedOn()).isBeforeOrEqualTo(OffsetDateTime.now()); }
public boolean willExpireIn(final TemporalAmount temporalAmount) { final OffsetDateTime maxExpirationDate = OffsetDateTime.now().plus(temporalAmount); return getExpiresOn().isBefore(maxExpirationDate); }
static Duration reloadAfter(final ZonkyApiToken token) { final int expirationInSeconds = token.getExpiresIn(); final int minimumSecondsBeforeExpiration = 5; final int secondsToReloadAfter = Math.max(minimumSecondsBeforeExpiration, expirationInSeconds - minimumSecondsBeforeExpiration); return Duration.ofSeconds(secondsToReloadAfter); }
@Test void refresh() { final String originalTokenId = UUID.randomUUID().toString(); final ZonkyApiToken originToken = new ZonkyApiToken(UUID.randomUUID().toString(), originalTokenId, OffsetDateTime.now()); final ZonkyApiToken resultToken = mock(ZonkyApiToken.class); final ZonkyOAuthApi api = mock(ZonkyOAuthApi.class); when(api.refresh(eq(originalTokenId), anyString(), any())).thenReturn(resultToken); final Api<ZonkyOAuthApi> wrapper = new Api<>(api); final OAuth oauth = new OAuth(wrapper); final ZonkyApiToken returnedToken = oauth.refresh(originToken); assertThat(returnedToken).isEqualTo(resultToken); } }
public ZonkyApiToken refresh(final ZonkyApiToken token) { final OAuthScope scope = token.getScope().getPrimaryScope().orElse(OAuthScope.SCOPE_APP_WEB); return api.call(a -> a.refresh(String.valueOf(token.getRefreshToken()), ZonkyApiToken.REFRESH_TOKEN_STRING, scope)); } }
public boolean willExpireIn(final TemporalAmount temporalAmount) { final OffsetDateTime maxExpirationDate = DateUtil.offsetNow().plus(temporalAmount); return getExpiresOn().isBefore(maxExpirationDate); }
@Override public void filter(final ClientRequestContext clientRequestContext) { final ZonkyApiToken supplied = token == null ? null : token.get(); final char[] t = supplied == null ? AuthenticatedFilter.EMPTY_TOKEN : supplied.getAccessToken(); this.setRequestHeader("Authorization", "Bearer " + String.valueOf(t)); super.filter(clientRequestContext); } }
@Test void changes400to401() throws IOException { final int expectedCode = 400; final ClientRequestContext ctx = mock(ClientRequestContext.class); final ClientResponseContext ctx2 = mock(ClientResponseContext.class); final ZonkyApiToken token = new ZonkyApiToken("", "", 299); when(ctx2.hasEntity()).thenReturn(true); when(ctx2.getHeaders()).thenReturn(new MultivaluedMapImpl<>()); when(ctx2.getEntityStream()).thenReturn(c(token)); when(ctx2.getStatusInfo()).thenReturn(Response.Status.fromStatusCode(expectedCode)); when(ctx2.getStatus()).thenReturn(expectedCode); final RoboZonkyFilter filter = new AuthenticatedFilter(() -> token); filter.filter(ctx, ctx2); verify(ctx2, times(1)).setStatus(401); } }
@Override public synchronized void close() { final ZonkyApiToken toClose = token.getAndSet(null); if (toClose == null || toClose.willExpireIn(Duration.ZERO)) { return; } LOGGER.debug("Logging '{}' out of Zonky ({}).", secrets.getUsername(), scope); apis.run(Zonky::logout, () -> toClose); } }
private static InputStream c(final ZonkyApiToken token) { final String error = "{\"error\":\"invalid_token\",\"error_description\":\"Invalid access token: " + Arrays.toString(token.getAccessToken()) + "\"}"; return new ByteArrayInputStream(error.getBytes(Defaults.CHARSET)); }
private ZonkyApiToken refreshTokenIfNecessary(final ZonkyApiToken token) { if (!token.willExpireIn(refresh)) { return token; } LOGGER.debug("Token refresh commencing."); isUpdating.set(true); try { return refreshToken(token); } catch (final Exception ex) { LOGGER.debug("Failed refreshing access token, falling back to password.", ex); return login(); } finally { isUpdating.set(false); LOGGER.debug("Token refresh over."); } }
private static ZonkyApiToken mockToken() { final ZonkyApiToken t = mock(ZonkyApiToken.class); when(t.getAccessToken()).thenReturn(UUID.randomUUID().toString().toCharArray()); return t; }
@SuppressWarnings({"unchecked", "rawtypes"}) private ApiProvider mockApi(final String username, final char... password) { final ApiProvider api = spy(new ApiProvider()); final ZonkyApiToken token = mock(ZonkyApiToken.class); when(token.getAccessToken()).thenReturn(new char[0]); final OAuth oauth = mock(OAuth.class); when(oauth.login(eq(username), eq(password))).thenReturn(token); doAnswer(i -> { final Function f = i.getArgument(0); return f.apply(oauth); }).when(api).oauth(any()); final Zonky z = mock(Zonky.class); doAnswer(i -> { final Consumer f = i.getArgument(0); f.accept(z); return null; }).when(api).run(any(Consumer.class), any()); return api; }
@SuppressWarnings({"unchecked", "rawtypes"}) private ApiProvider mockFailingApi() { final ApiProvider api = spy(new ApiProvider()); final ZonkyApiToken token = mock(ZonkyApiToken.class); when(token.getAccessToken()).thenReturn(new char[0]); final OAuth oauth = mock(OAuth.class); when(oauth.login(any(), any())).thenReturn(token); doAnswer(i -> { final Function f = i.getArgument(0); return f.apply(oauth); }).when(api).oauth(any()); final Zonky z = mock(Zonky.class); doAnswer(i -> { final Consumer f = i.getArgument(0); f.accept(z); return null; }).when(api).run(any(Consumer.class), any()); doThrow(IllegalStateException.class).when(z).logout(); // last call will fail return api; }
@Test void hasToken() throws URISyntaxException { final ClientRequestContext crc = mock(ClientRequestContext.class); when(crc.getUri()).thenReturn(new URI("http://somewhere")); when(crc.getHeaders()).thenReturn(new MultivaluedHashMap<>()); getTestedFilter().filter(crc); assertThat(crc.getHeaders().getFirst("Authorization")) .isEqualTo("Bearer " + String.valueOf(AuthenticatedFilterTest.TOKEN.getAccessToken())); }