Skip to content

Commit

Permalink
KNOX-2625 - initial commit (apache#476)
Browse files Browse the repository at this point in the history
* KNOX-2625 - Enhance KnoxSSO to Support Session Timeout and Logout
  • Loading branch information
lmccay authored Aug 27, 2021
1 parent 8080b28 commit 762ba37
Show file tree
Hide file tree
Showing 15 changed files with 364 additions and 65 deletions.
4 changes: 4 additions & 0 deletions gateway-applications/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
<groupId>org.apache.knox</groupId>
<artifactId>gateway-openapi-ui</artifactId>
</dependency>
<dependency>
<groupId>org.apache.knox</groupId>
<artifactId>gateway-spi</artifactId>
</dependency>
<dependency>
<groupId>org.apache.knox</groupId>
<artifactId>knox-token-management-ui</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,70 +19,74 @@ var loginPageSuffix = "/knoxauth/login.html";
var webssoURL = "/api/v1/websso?originalUrl=";
var userAgent = navigator.userAgent.toLowerCase();

function get(name){
//KNOX-820 changing the regex so that multiple query params get included with the 'originalUrl'
if(name=(new RegExp('[?&]'+encodeURIComponent(name)+'=([^]*)')).exec(location.search))
return decodeURIComponent(name[1]);
function get(name) {
//KNOX-820 changing the regex so that multiple query params get included with the 'originalUrl'
if ((name = (new RegExp('[?&]' + encodeURIComponent(name) + '=([^]*)')).exec(location.search))) {
return decodeURIComponent(name[1]);
}
}

function testSameOrigin(url) {
var loc = window.location,
a = document.createElement('a');
a.href = url;
return a.hostname == loc.hostname &&
a.port == loc.port &&
a.protocol == loc.protocol;
var loc = window.location,
a = document.createElement('a');
a.href = url;
return a.hostname == loc.hostname &&
a.port == loc.port &&
a.protocol == loc.protocol;
}

function redirect(redirectUrl) {
try { window.location.replace(redirectUrl); }
catch(e) { window.location = redirectUrl; }
try {
window.location.replace(redirectUrl);
} catch (e) {
window.location = redirectUrl;
}
}

var keypressed = function(event) {
if (event.keyCode == 13) {
login();
}
}
if (event.keyCode == 13) {
login();
}
};

var login = function() {
var pathname = window.location.pathname;
var topologyContext = pathname.replace(loginPageSuffix, "");;
var loginURL = topologyContext + webssoURL;
var form = document.forms[0];
var username = form.username.value;
var password = form.password.value;
var _login = function() {
var originalUrl = get("originalUrl");
var idpUrl = loginURL + originalUrl;
var redirectUrl = originalUrl;
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("POST", idpUrl, true);
request.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password))
request.send(null);
var pathname = window.location.pathname;
var topologyContext = pathname.replace(loginPageSuffix, "");
var loginURL = topologyContext + webssoURL;
var form = document.forms[0];
var username = form.username.value;
var password = form.password.value;
var _login = function() {
var originalUrl = get("originalUrl");
var idpUrl = loginURL + originalUrl;
var redirectUrl = originalUrl;
//Instantiate HTTP Request
var request = ((window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
request.open("POST", idpUrl, true);
request.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password));
request.send(null);

//Process Response
request.onreadystatechange = function(){
if (request.readyState == 4) {
if (request.status==0 || request.status==200 || request.status==204 || request.status==307 || request.status==303) {
if (testSameOrigin(originalUrl) == false) {
redirectUrl = "redirecting.html?originalUrl=" + originalUrl;
}
redirect(redirectUrl);
} else {
$('#errorBox').show();
$('#signInLoading').hide();
$('#signIn').removeAttr('disabled');
if (request.status==401) {
$('#errorBox .errorMsg').text("The username or password you entered is incorrect.");
} else {
$('#errorBox .errorMsg').text("Response from " + request.responseURL + " - " + request.status + ": " + request.statusText);
}
}
}
}
}
//Process Response
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 0 || request.status == 200 || request.status == 204 || request.status == 307 || request.status == 303) {
if (testSameOrigin(originalUrl) == false) {
redirectUrl = "redirecting.html?originalUrl=" + originalUrl;
}
redirect(redirectUrl);
} else {
$('#errorBox').show();
$('#signInLoading').hide();
$('#signIn').removeAttr('disabled');
if (request.status == 401) {
$('#errorBox .errorMsg').text("The username or password you entered is incorrect.");
} else {
$('#errorBox .errorMsg').text("Response from " + request.responseURL + " - " + request.status + ": " + request.statusText);
}
}
}
};
};

_login();
}
_login();
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<%@ page import="java.util.Collection" %>
<%@ page import="java.util.Map" %>
<%@ page import="org.apache.knox.gateway.topology.Topology" %>
<%@ page import="org.apache.knox.gateway.topology.Service" %>
<%@ page import="org.apache.knox.gateway.util.RegExUtils" %>
<%@ page import="org.apache.knox.gateway.util.WhitelistUtils" %>
<%@ page import="org.apache.knox.gateway.config.GatewayConfig" %>
<%@ page import="java.net.MalformedURLException" %>
<%@ page import="org.apache.knox.gateway.util.Urls" %>

<!DOCTYPE html>
<!--[if lt IE 7]><html class="no-js lt-ie9 lt-ie8 lt-ie7"><![endif]-->
<!--[if IE 7]><html class="no-js lt-ie9 lt-ie8"><![endif]-->
<!--[if IE 8]><html class="no-js lt-ie9"><![endif]-->
<!--[if gt IE 8]><!-->
<html class="no-js">
<!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

<link rel="shortcut icon" href="images/favicon.ico">
<link href="styles/bootstrap.min.css" media="all" rel="stylesheet" type="text/css" id="bootstrap-css">
<link href="styles/knox.css" media="all" rel="stylesheet" type="text/css" >

<script src="libs/bower/jquery/js/jquery-3.5.1.min.js" ></script>

<script type="text/javascript" src="js/knoxauth.js"></script>
<%
String originalUrl = request.getParameter("originalUrl");
Topology topology = (Topology)request.getSession().getServletContext().getAttribute("org.apache.knox.gateway.topology");
String whitelist = null;
String cookieName = null;
GatewayConfig gatewayConfig =
(GatewayConfig) request.getServletContext().
getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
String globalLogoutPageURL = gatewayConfig.getGlobalLogoutPageUrl();
Collection<Service> services = topology.getServices();
for (Object service : services) {
Service svc = (Service)service;
if (svc.getRole().equals("KNOXSSO")) {
Map<String, String> params = svc.getParams();
whitelist = params.get("knoxsso.redirect.whitelist.regex");
// LJM TODO: get cookie name and possibly domain prefix info for use in logout
cookieName = params.get("knoxsso.cookie.name");
if (cookieName == null) {
cookieName = "hadoop-jwt";
}
}
break;
}
if (whitelist == null) {
whitelist = WhitelistUtils.getDispatchWhitelist(request);
if (whitelist == null) {
whitelist = "";
}
}
boolean validRedirect = false;
String origUrl = request.getParameter("originalUrl");
String del = "?";
if (origUrl != null && origUrl.contains("?")) {
del = "&";
}
if (origUrl != null) {
validRedirect = RegExUtils.checkWhitelist(whitelist, origUrl);
}
if (("1".equals(request.getParameter("returnToApp")))) {
if (validRedirect) {
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location",originalUrl + del + "refresh=1");
return;
}
}
else if (("1".equals(request.getParameter("globalLogout")))) {
Cookie c = new Cookie(cookieName, null);
c.setMaxAge(0);
c.setPath("/");
try {
String domainName = Urls.getDomainName(request.getRequestURL().toString(), null);
if(domainName != null) {
c.setDomain(domainName);
}
} catch (MalformedURLException e) {
// we are probably not going to be able to
// remove the cookie due to this error but it
// isn't necessarily not going to work.
}
response.addCookie(c);
response.setStatus(HttpServletResponse.SC_SEE_OTHER);
response.setHeader("Location", globalLogoutPageURL);
return;
}
%>
</head>

<body class="login" style="">
<section id="signout-container" style="margin-top: 4.5px;">
<div class="l-logo">
<img src="images/knox-logo.gif" alt="Knox logo">
</div>
<%
if (validRedirect) {
%>
<h1 style="color: gray;">Session Termination</h1>
<div style="background: dark-gray;" class="l2-logo">
<p style="color: white;display: block">
Your session has timed out or you have attempted to logout of an application
that is participating in SSO. You may establish a new session by returning to
the application. If your previously established SSO session is still valid then
you will likely be automatically logged into your application. Otherwise, you
will be required to login again.
<a href="?returnToApp=1&originalUrl=<%= originalUrl %>" >Return to Application</a>
</p>
<%
if (globalLogoutPageURL != null && !globalLogoutPageURL.isEmpty()) {
%>
<p style="color: white;display: block">
If you would like to logout of the Knox SSO session, you need to do so from
the configured SSO provider. Subsequently, authentication will be required to access
any SSO protected resources. Note that this may or may not invalidate any previously
established application sessions. Application sessions are subject to their application
specific session cookies and timeouts.
<a href="<%= request.getRequestURI() %>?globalLogout=1" >Global Logout</a>
</p>
</div>
<%
}
}
else {
%>
<div style="background: gray;text-color: white;text-align:center;">
<h1 style="color: red;">ERROR</h1>
<div style="background: white;" class="l-logo">
</div>
<p style="color: white;display: block">Invalid Redirect: Possible Phishing Attempt</p>
<%
}
%>
</div>
</section>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

<script type="text/javascript" src="js/knoxauth.js"></script>
<%
String originalUrl = request.getParameter("originalUrl");
Topology topology = (Topology)request.getSession().getServletContext().getAttribute("org.apache.knox.gateway.topology");
String whitelist = null;
Collection services = topology.getServices();
Expand All @@ -57,14 +58,14 @@
whitelist = "";
}
}
boolean validRedirect = RegExUtils.checkWhitelist(whitelist, request.getParameter("originalUrl"));
boolean validRedirect = RegExUtils.checkWhitelist(whitelist, originalUrl);
if (validRedirect) {
%>
<script>
document.addEventListener("load", redirectOnLoad());
function redirectOnLoad() {
var originalUrl = get("originalUrl");
var originalUrl = <%= originalUrl %>;
if (originalUrl != null) {
redirect(originalUrl);
}
Expand All @@ -74,7 +75,7 @@
}
%>
</head>

<body>
<section id="signin-container" style="margin-top: 80px;">
<%
Expand All @@ -85,7 +86,7 @@
<div style="background: white;" class="l-logo">
<img src="images/loading.gif" alt="Knox logo" style="text-align:center;width: 2%; height: 2%">
</div>
<p style="color: white;display: block">Loading should complete in few a seconds. If not, click <a href="#" onclick='redirect(get("originalUrl"));' >here</a></p>
<p style="color: white;display: block">Loading should complete in few a seconds. If not, click <a href="<%= originalUrl %>">here</a></p>
<%
} else {
%>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1588,11 +1588,20 @@ body.login {
margin: 0 auto;
width: 280px;
}
.login #signout-container {
margin: 0 auto;
width: 400px;
}
.login .l-logo {
text-align: center;
width: 100%;
margin-bottom: 20px;
}
.login .l2-logo {
text-align: left;
width: 100%;
margin-bottom: 20px;
}

.icon-sign-in {
margin-left: 15px;
Expand Down
Loading

0 comments on commit 762ba37

Please sign in to comment.