Introduce extension point for logout redirection

This commit is contained in:
René Pfeuffer
2019-04-12 11:42:19 +02:00
parent 84c8f7159f
commit 1b60857959
6 changed files with 65 additions and 14 deletions

View File

@@ -0,0 +1,11 @@
package sonia.scm.api.v2.resources;
import sonia.scm.plugin.ExtensionPoint;
import java.net.URI;
import java.util.Optional;
@ExtensionPoint(multi = false)
public interface LogoutRedirection {
Optional<URI> afterLogoutRedirectTo();
}

View File

@@ -3,6 +3,7 @@ import React from "react";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import { Redirect } from "react-router-dom";
import type { History } from "history";
import {
logout,
@@ -20,15 +21,16 @@ type Props = {
logoutLink: string,
// dispatcher functions
logout: (link: string) => void,
logout: (link: string, history: History) => void,
// context props
history: History,
t: string => string
};
class Logout extends React.Component<Props> {
componentDidMount() {
this.props.logout(this.props.logoutLink);
this.props.logout(this.props.logoutLink, this.props.history);
}
render() {
@@ -64,7 +66,7 @@ const mapStateToProps = state => {
const mapDispatchToProps = dispatch => {
return {
logout: (link: string) => dispatch(logout(link))
logout: (link: string, history: History) => dispatch(logout(link, history))
};
};

View File

@@ -11,6 +11,7 @@ import {
fetchIndexResourcesPending,
fetchIndexResourcesSuccess
} from "./indexResource";
import type { History } from "history";
// Action
@@ -30,6 +31,10 @@ export const LOGOUT_PENDING = `${LOGOUT}_${types.PENDING_SUFFIX}`;
export const LOGOUT_SUCCESS = `${LOGOUT}_${types.SUCCESS_SUFFIX}`;
export const LOGOUT_FAILURE = `${LOGOUT}_${types.FAILURE_SUFFIX}`;
type LogoutRedirection = {
logoutRedirect: string
};
// Reducer
const initialState = {};
@@ -130,9 +135,7 @@ export const fetchMeFailure = (error: Error) => {
// side effects
const callFetchMe = (link: string): Promise<Me> => {
return apiClient
.get(link)
.then(response => {
return apiClient.get(link).then(response => {
return response.json();
});
};
@@ -187,13 +190,24 @@ export const fetchMe = (link: string) => {
};
};
export const logout = (link: string) => {
export const logout = (link: string, history: History) => {
return function(dispatch: any) {
dispatch(logoutPending());
return apiClient
.delete(link)
.then(() => {
.then(response => {
return response.status === 200
? response.json()
: new Promise(function(resolve) {
resolve(undefined);
});
})
.then(json => {
if (json && json.logoutRedirect) {
location.href = json.logoutRedirect;
} else {
dispatch(logoutSuccess());
}
})
.then(() => {
dispatch(fetchIndexResources());

View File

@@ -16,6 +16,8 @@ import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.util.Optional;
@Path(AuthenticationResource.PATH)
@AllowAnonymousAccess
@@ -27,12 +29,14 @@ public class AuthenticationResource {
private final AccessTokenBuilderFactory tokenBuilderFactory;
private final AccessTokenCookieIssuer cookieIssuer;
private final LogoutRedirection logoutRedirection;
@Inject
public AuthenticationResource(AccessTokenBuilderFactory tokenBuilderFactory, AccessTokenCookieIssuer cookieIssuer)
public AuthenticationResource(AccessTokenBuilderFactory tokenBuilderFactory, AccessTokenCookieIssuer cookieIssuer, LogoutRedirection logoutRedirection)
{
this.tokenBuilderFactory = tokenBuilderFactory;
this.cookieIssuer = cookieIssuer;
this.logoutRedirection = logoutRedirection;
}
@@ -121,6 +125,7 @@ public class AuthenticationResource {
@DELETE
@Path("access_token")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 204, condition = "success"),
@ResponseCode(code = 500, condition = "internal server error")
@@ -135,7 +140,16 @@ public class AuthenticationResource {
cookieIssuer.invalidate(request, response);
// TODO anonymous access ??
if (logoutRedirection == null) {
return Response.noContent().build();
} else {
Optional<URI> uri = logoutRedirection.afterLogoutRedirectTo();
if (uri.isPresent()) {
return Response.ok(new RedirectAfterLogoutDto(uri.get().toASCIIString())).build();
} else {
return Response.noContent().build();
}
}
}
}

View File

@@ -0,0 +1,10 @@
package sonia.scm.api.v2.resources;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class RedirectAfterLogoutDto {
private String logoutRedirect;
}

View File

@@ -103,7 +103,7 @@ public class AuthenticationResourceTest {
@Before
public void prepareEnvironment() {
AuthenticationResource authenticationResource = new AuthenticationResource(accessTokenBuilderFactory, cookieIssuer);
AuthenticationResource authenticationResource = new AuthenticationResource(accessTokenBuilderFactory, cookieIssuer, null);
dispatcher.getRegistry().addSingletonResource(authenticationResource);
AccessToken accessToken = mock(AccessToken.class);