Skip to content

Commit

Permalink
CCS-4249 sso enhancement (#542)
Browse files Browse the repository at this point in the history
* provid login and logout servlets
  • Loading branch information
xdavidson committed Mar 12, 2021
1 parent c749788 commit 93cff4d
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ describe("User tests", () => {
expect(view).toMatchSnapshot()
})

it("should render a Link component", () => {
const wrapper = mount(<Router><User {...mockStateUser} /></Router>)
const navLinks = wrapper.find(Link)
expect(navLinks.exists()).toBe(true)
})

it("test render function", () => {
const wrapper = renderer.create(<Router><User {...mockStateUser} /></Router>)
const inst = wrapper.getInstance()
Expand Down
59 changes: 18 additions & 41 deletions pantheon-bundle/frontend/src/app/components/Chrome/Header/User.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,19 @@ import { IAppState } from "@app/app"
interface IState {
helpDropdownOpen: boolean
loginUrl: string
logoutUrl: string
}

class User extends Component<IAppState, IState> {
constructor(props) {
super(props)
this.state = {
helpDropdownOpen: false,
loginUrl: "/login",
loginUrl: "/auth/login",
logoutUrl: "/system/sling/logout"
}
}

public componentDidMount() {
this.getLoginUrl()
}

public render() {
const dropdownItems = [
<DropdownItem key="help" href="/pantheon/docs/assemblies/assembly-pantheon-help.html" target="_blank">Help</DropdownItem>,
Expand All @@ -35,21 +33,21 @@ class User extends Component<IAppState, IState> {
return (
<React.Fragment>
<Dropdown onSelect={this.onHelpSelect}
toggle={
<DropdownToggle toggleIndicator={null} onToggle={this.onHelpToggle}>
<HelpIcon />
</DropdownToggle>
}
isPlain={true}
isOpen={this.state.helpDropdownOpen}
dropdownItems={dropdownItems}
position={DropdownPosition.right}
toggle={
<DropdownToggle toggleIndicator={null} onToggle={this.onHelpToggle}>
<HelpIcon />
</DropdownToggle>
}
isPlain={true}
isOpen={this.state.helpDropdownOpen}
dropdownItems={dropdownItems}
position={DropdownPosition.right}
/>
<Link className="p2-header__login"
to={this.props.userAuthenticated ? "" : this.state.loginUrl}
onClick={this.conditionalRedirect}>
{this.props.userAuthenticated ? "[" + this.props.username + "]" : "Log In"}
</Link>
<a className="p2-header__login"
href={this.props.userAuthenticated ? this.state.logoutUrl : this.state.loginUrl}
onClick={this.conditionalRedirect}>
{this.props.userAuthenticated ? "Log Out [" + this.props.username + "]" : "Log In"}
</a>
</React.Fragment>
)
}
Expand All @@ -66,31 +64,10 @@ class User extends Component<IAppState, IState> {

private conditionalRedirect = () => {
if (this.props.userAuthenticated) {
fetch("/system/sling/logout")
fetch(this.state.logoutUrl)
.then(response => window.location.href = "/pantheon")
}
}

private getLoginUrl = () => {
fetch("/conf/pantheon/pant:ssoLoginUrl")
.then((response => {
if (response.ok) {
return response.text()
} else {
throw new Error(response.statusText)
}
}))
.then(
responseText => {
if (responseText.length > 0) {
this.setState({ loginUrl: responseText })
}
// console.log("The response text from pant:ssoLoginUrl is: " + responseText)
})
.catch((error) => {
console.log(error)
})
}
}

export { User }
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ exports[`User tests should render User component 1`] = `
</DropdownToggle>
}
/>
<Link
<a
className="p2-header__login"
href="/system/sling/logout"
onClick={[Function]}
to=""
>
[demo]
</Link>
Log Out [demo]
</a>
</Fragment>
`;

Expand Down
1 change: 1 addition & 0 deletions pantheon-bundle/frontend/src/app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class Routes extends Component<IAppState> {

return (
// https://github.com/ReactTraining/react-router/issues/5521#issuecomment-329491083
// FIXME: here we are routing to the form based authentication regardless if SSO is enabled or not
<Switch>
{routes.map(({ path, exact, component, requiresLogin }, idx) => (
<Route path={path} exact={exact} render={(routeProps) => (this.props.userAuthenticated || !requiresLogin) ? component(routeProps) : <Login />} key={idx} />
Expand Down
10 changes: 5 additions & 5 deletions pantheon-bundle/frontend/src/app/searchResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,12 +247,12 @@ class SearchResults extends Component<IProps, ISearchState> {
const publishedIcon = publishedDate !== "-" ? <><CheckCircleIcon className="p2-search__check-circle-icon"/></> : ""
const cellItem = new Array()
cellItem.push(publishedIcon)
// if (this.props.userAuthenticated) {
if (this.props.userAuthenticated) {
cellItem.push({ title: <a href={"/pantheon/#" + item["sling:resourceType"].substring(SlingTypesPrefixes.PANTHEON.length) + "/" + item['pant:transientPath'] + "?variant=" + item.variant}> {item["jcr:title"] !== "-" ? item["jcr:title"] : item["pant:transientPath"]} </a> })
// } else {
// let docTitle = item["jcr:title"] !== "-" ? item["jcr:title"] : item["pant:transientPath"]
// cellItem.push(docTitle)
// }
} else {
let docTitle = item["jcr:title"] !== "-" ? item["jcr:title"] : item["pant:transientPath"]
cellItem.push(docTitle)
}
cellItem.push(item["pant:transientSourceName"])
cellItem.push(item["pant:dateUploaded"])
cellItem.push(publishedDate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ public AuthenticationInfo extractCredentials(
KeycloakSecurityContext ctx =
(KeycloakSecurityContext)
request.getSession().getAttribute("org.keycloak.KeycloakSecurityContext");
// log.info("[" + KeycloakAuthenticationHandler.class.getSimpleName() + "] KeycloakSecurityContext:" + ctx);
if (ctx != null) {
// log.info("[" + KeycloakAuthenticationHandler.class.getSimpleName() + "] username: " + ctx.getToken().getPreferredUsername());

ResourceResolver resolver = null;
try {
Expand Down Expand Up @@ -118,7 +116,7 @@ public boolean requestCredentials(HttpServletRequest request, HttpServletRespons
try {
response.getWriter().print("Request");
} catch (IOException e) {
log.info("[" +KeycloakAuthenticationHandler.class.getSimpleName() + "] Error occurred when requesting credentials.");
log.error("[" +KeycloakAuthenticationHandler.class.getSimpleName() + "] Error occurred when requesting credentials.");
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,15 @@
service = Filter.class,
property = {
KeycloakOIDCFilter.CONFIG_FILE_PARAM + "=" + "keycloak.json",
"keycloak.config.skipPattern=(/pantheon/internal/modules.json|/pantheon/builddate.json|/pantheon/fonts/*|/content/repositories.harray.1.json|/starter.html|/bin/browser.html|/content/starter/css/bundle.css|/content/starter/img/sling-logo.svg|/content/starter/img/asf-logo.svg|/content/starter/img/sling-logo.svg|/content/starter/img/gradient.jpg|/content/starter/fonts/OpenSans-Light-webfont.woff|/content/starter/fonts/OpenSans-Regular-webfont.woff|/system/sling.js|/system/*|/pantheon/*.js)",
HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN + "=" + "/pantheon/*",
HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN + "=" + "/content/pantheon",
HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN + "=" + "/content/products",
HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT
+ "="
+ "(osgi.http.whiteboard.context.name=pantheon)",
})

@SlingServletFilter(scope = {SlingServletFilterScope.REQUEST},
pattern = "/content/.*",
pattern = "/auth/login",
methods = {"GET", "POST"})

public class KeycloakFilter extends KeycloakOIDCFilter implements Filter {

private static final Logger log = LoggerFactory.getLogger(KeycloakFilter.class.getName());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.redhat.pantheon.auth.sso;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Servlet;
import java.io.IOException;

/**
* A login servlet that handles Keycloak or basic auth request
*
* @author Lisa Davidson
*/
@Component(
service = Servlet.class,
property = {
"sling.servlet.methods={GET, POST}",
"sling.servlet.paths=" + LoginServlet.PATH_PATTERN
},
immediate = true)
public class LoginServlet extends SlingAllMethodsServlet {
private static final Logger log = LoggerFactory.getLogger(LoginServlet.class.getName());
private static final String BASIC_AUTH_LOGIN_URI = "/pantheon/#/login";
static final String PATH_PATTERN = "/auth/login";

@Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
String uri = "";
if (System.getenv("AUTH_SERVER_URL") != null) {
try {
uri = System.getenv("SSO_LOGIN_URL") != null ? System.getenv("SSO_LOGIN_URL") : "";

if (uri.length() > 0) {
response.setStatus(302);
response.setHeader("Location", uri);
} else {
log.error("Error occurred while creating the Authentication request.");
}

} catch (Exception e) {
log.error("Error occurred while creating the Authentication request.");
}
} else {
log.debug("SSO is not enabled.");
response.setStatus(302);
response.setHeader("Location", BASIC_AUTH_LOGIN_URI);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.redhat.pantheon.auth.sso;

import org.apache.sling.servlets.annotations.SlingServletFilter;
import org.apache.sling.servlets.annotations.SlingServletFilterScope;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* A Filter that handles logout request for Keycloak Integration
*
* * @author Lisa Davidson
*/
@Component(
service = Filter.class,
property = {
Constants.SERVICE_DESCRIPTION + "=Filter for keycloak logout request",
Constants.SERVICE_VENDOR + "=Red Hat Content Tooling team"
})
@SlingServletFilter(scope = {SlingServletFilterScope.REQUEST},
pattern = "/system/sling/logout",
methods = {"GET"})
public class LogoutFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(LogoutFilter.class.getName());
private static final String REDIRECT_URI = "/pantheon/";
public void init(FilterConfig filterConfig) throws ServletException {
}

public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException,
ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;

if (System.getenv("AUTH_SERVER_URL") != null) {
// Invalidate keycloak session
request.getSession().invalidate();
}
chain.doFilter(request, response);
}

public void destroy() {
}
}

0 comments on commit 93cff4d

Please sign in to comment.