mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 01:15:44 +01:00
Introduce extension point for logout redirection
This commit is contained in:
@@ -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();
|
||||
}
|
||||
@@ -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))
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class RedirectAfterLogoutDto {
|
||||
private String logoutRedirect;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user