mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 09:25:43 +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 { connect } from "react-redux";
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import { Redirect } from "react-router-dom";
|
import { Redirect } from "react-router-dom";
|
||||||
|
import type { History } from "history";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
logout,
|
logout,
|
||||||
@@ -20,15 +21,16 @@ type Props = {
|
|||||||
logoutLink: string,
|
logoutLink: string,
|
||||||
|
|
||||||
// dispatcher functions
|
// dispatcher functions
|
||||||
logout: (link: string) => void,
|
logout: (link: string, history: History) => void,
|
||||||
|
|
||||||
// context props
|
// context props
|
||||||
|
history: History,
|
||||||
t: string => string
|
t: string => string
|
||||||
};
|
};
|
||||||
|
|
||||||
class Logout extends React.Component<Props> {
|
class Logout extends React.Component<Props> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.logout(this.props.logoutLink);
|
this.props.logout(this.props.logoutLink, this.props.history);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -64,7 +66,7 @@ const mapStateToProps = state => {
|
|||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
logout: (link: string) => dispatch(logout(link))
|
logout: (link: string, history: History) => dispatch(logout(link, history))
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
fetchIndexResourcesPending,
|
fetchIndexResourcesPending,
|
||||||
fetchIndexResourcesSuccess
|
fetchIndexResourcesSuccess
|
||||||
} from "./indexResource";
|
} from "./indexResource";
|
||||||
|
import type { History } from "history";
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
|
|
||||||
@@ -30,6 +31,10 @@ export const LOGOUT_PENDING = `${LOGOUT}_${types.PENDING_SUFFIX}`;
|
|||||||
export const LOGOUT_SUCCESS = `${LOGOUT}_${types.SUCCESS_SUFFIX}`;
|
export const LOGOUT_SUCCESS = `${LOGOUT}_${types.SUCCESS_SUFFIX}`;
|
||||||
export const LOGOUT_FAILURE = `${LOGOUT}_${types.FAILURE_SUFFIX}`;
|
export const LOGOUT_FAILURE = `${LOGOUT}_${types.FAILURE_SUFFIX}`;
|
||||||
|
|
||||||
|
type LogoutRedirection = {
|
||||||
|
logoutRedirect: string
|
||||||
|
};
|
||||||
|
|
||||||
// Reducer
|
// Reducer
|
||||||
|
|
||||||
const initialState = {};
|
const initialState = {};
|
||||||
@@ -130,9 +135,7 @@ export const fetchMeFailure = (error: Error) => {
|
|||||||
// side effects
|
// side effects
|
||||||
|
|
||||||
const callFetchMe = (link: string): Promise<Me> => {
|
const callFetchMe = (link: string): Promise<Me> => {
|
||||||
return apiClient
|
return apiClient.get(link).then(response => {
|
||||||
.get(link)
|
|
||||||
.then(response => {
|
|
||||||
return response.json();
|
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) {
|
return function(dispatch: any) {
|
||||||
dispatch(logoutPending());
|
dispatch(logoutPending());
|
||||||
return apiClient
|
return apiClient
|
||||||
.delete(link)
|
.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());
|
dispatch(logoutSuccess());
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
dispatch(fetchIndexResources());
|
dispatch(fetchIndexResources());
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import javax.ws.rs.*;
|
|||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Path(AuthenticationResource.PATH)
|
@Path(AuthenticationResource.PATH)
|
||||||
@AllowAnonymousAccess
|
@AllowAnonymousAccess
|
||||||
@@ -27,12 +29,14 @@ public class AuthenticationResource {
|
|||||||
|
|
||||||
private final AccessTokenBuilderFactory tokenBuilderFactory;
|
private final AccessTokenBuilderFactory tokenBuilderFactory;
|
||||||
private final AccessTokenCookieIssuer cookieIssuer;
|
private final AccessTokenCookieIssuer cookieIssuer;
|
||||||
|
private final LogoutRedirection logoutRedirection;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AuthenticationResource(AccessTokenBuilderFactory tokenBuilderFactory, AccessTokenCookieIssuer cookieIssuer)
|
public AuthenticationResource(AccessTokenBuilderFactory tokenBuilderFactory, AccessTokenCookieIssuer cookieIssuer, LogoutRedirection logoutRedirection)
|
||||||
{
|
{
|
||||||
this.tokenBuilderFactory = tokenBuilderFactory;
|
this.tokenBuilderFactory = tokenBuilderFactory;
|
||||||
this.cookieIssuer = cookieIssuer;
|
this.cookieIssuer = cookieIssuer;
|
||||||
|
this.logoutRedirection = logoutRedirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -121,6 +125,7 @@ public class AuthenticationResource {
|
|||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("access_token")
|
@Path("access_token")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@StatusCodes({
|
@StatusCodes({
|
||||||
@ResponseCode(code = 204, condition = "success"),
|
@ResponseCode(code = 204, condition = "success"),
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
@@ -135,7 +140,16 @@ public class AuthenticationResource {
|
|||||||
cookieIssuer.invalidate(request, response);
|
cookieIssuer.invalidate(request, response);
|
||||||
|
|
||||||
// TODO anonymous access ??
|
// TODO anonymous access ??
|
||||||
|
if (logoutRedirection == null) {
|
||||||
return Response.noContent().build();
|
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
|
@Before
|
||||||
public void prepareEnvironment() {
|
public void prepareEnvironment() {
|
||||||
AuthenticationResource authenticationResource = new AuthenticationResource(accessTokenBuilderFactory, cookieIssuer);
|
AuthenticationResource authenticationResource = new AuthenticationResource(accessTokenBuilderFactory, cookieIssuer, null);
|
||||||
dispatcher.getRegistry().addSingletonResource(authenticationResource);
|
dispatcher.getRegistry().addSingletonResource(authenticationResource);
|
||||||
|
|
||||||
AccessToken accessToken = mock(AccessToken.class);
|
AccessToken accessToken = mock(AccessToken.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user