Add Playwright tests for live site authentication and race page loading
- Introduced `auth.setup.js` to handle authentication against the live site and store the session state. - Created `live-race.spec.js` to test loading a live race page with an authenticated session, including cookie validation. - Added utility functions in `live-site-test-utils.js` for managing authentication, dismissing cookie banners, and checking UI states. - Included a temporary JSON file for live state inspection. - Updated deployment manifest to reflect new and modified files. - Implemented `_inc_faceai_identity.jsp` for managing FaceAI identity cookies and included it in relevant JSP files. - Added language management JavaScript in `lang.js`. - Adjusted `fotoCR-en.jsp` and `fotoCR.jsp` to include the FaceAI identity logic. - Created a tarball for staging deployment.
This commit is contained in:
parent
9f56dfba1d
commit
1d1bccdae6
23 changed files with 4358 additions and 11 deletions
260
www/_inc_faceai_identity.jsp
Normal file
260
www/_inc_faceai_identity.jsp
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
<%@ page language="java" import="java.nio.charset.StandardCharsets" %>
|
||||
<%@ page language="java" import="java.util.Base64" %>
|
||||
<%@ page language="java" import="java.lang.reflect.Method" %>
|
||||
<%@ page language="java" import="javax.crypto.Mac" %>
|
||||
<%@ page language="java" import="javax.crypto.spec.SecretKeySpec" %>
|
||||
<%!
|
||||
private String faceAiCookieEnv(String key, String defaultValue) {
|
||||
String value = System.getenv(key);
|
||||
if (value == null || value.trim().length() == 0) {
|
||||
value = System.getProperty(key, defaultValue);
|
||||
}
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
value = value.trim();
|
||||
return value.length() == 0 ? defaultValue : value;
|
||||
}
|
||||
|
||||
private String faceAiBase64Url(byte[] value) {
|
||||
return Base64.getUrlEncoder().withoutPadding().encodeToString(value);
|
||||
}
|
||||
|
||||
private String faceAiJsonEscape(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder(value.length() + 16);
|
||||
for (int index = 0; index < value.length(); index++) {
|
||||
char current = value.charAt(index);
|
||||
switch (current) {
|
||||
case '\\':
|
||||
builder.append("\\\\");
|
||||
break;
|
||||
case '"':
|
||||
builder.append("\\\"");
|
||||
break;
|
||||
case '\b':
|
||||
builder.append("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
builder.append("\\f");
|
||||
break;
|
||||
case '\n':
|
||||
builder.append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
builder.append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
builder.append("\\t");
|
||||
break;
|
||||
default:
|
||||
if (current < 0x20) {
|
||||
String hex = Integer.toHexString(current);
|
||||
builder.append("\\u");
|
||||
for (int padding = hex.length(); padding < 4; padding++) {
|
||||
builder.append('0');
|
||||
}
|
||||
builder.append(hex);
|
||||
} else {
|
||||
builder.append(current);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private Object faceAiInvoke(Object bean, String methodName) {
|
||||
if (bean == null || methodName == null || methodName.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Method method = bean.getClass().getMethod(methodName, new Class[0]);
|
||||
return method.invoke(bean, new Object[0]);
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private long faceAiUserId(Object user) {
|
||||
Object value = faceAiInvoke(user, "getId_users");
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
if (value != null) {
|
||||
try {
|
||||
return Long.parseLong(String.valueOf(value));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
private String faceAiUserString(Object user, String methodName) {
|
||||
Object value = faceAiInvoke(user, methodName);
|
||||
return value == null ? "" : String.valueOf(value).trim();
|
||||
}
|
||||
|
||||
private boolean faceAiUserDaRinnovare(Object user) {
|
||||
Object value = faceAiInvoke(user, "isDaRinnovare");
|
||||
return value instanceof Boolean ? ((Boolean) value).booleanValue() : false;
|
||||
}
|
||||
|
||||
private String faceAiCookieDisplayName(Object user) {
|
||||
String nome = faceAiUserString(user, "getNome");
|
||||
String cognome = faceAiUserString(user, "getCognome");
|
||||
String displayName = (nome + " " + cognome).trim();
|
||||
if (displayName.length() > 0) {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
String email = faceAiUserString(user, "getEMail");
|
||||
if (email.length() > 0) {
|
||||
return email;
|
||||
}
|
||||
|
||||
return String.valueOf(faceAiUserId(user));
|
||||
}
|
||||
|
||||
private String faceAiIdentityToken(Object user, String secret, long expiresAt) throws Exception {
|
||||
String email = faceAiUserString(user, "getEMail");
|
||||
String membershipStatus = faceAiUserDaRinnovare(user) ? "inactive" : "active";
|
||||
String payload = "{"
|
||||
+ "\"type\":\"legacy-identity\","
|
||||
+ "\"userId\":\"" + faceAiJsonEscape(String.valueOf(faceAiUserId(user))) + "\","
|
||||
+ "\"displayName\":\"" + faceAiJsonEscape(faceAiCookieDisplayName(user)) + "\","
|
||||
+ "\"email\":\"" + faceAiJsonEscape(email) + "\","
|
||||
+ "\"membershipStatus\":\"" + membershipStatus + "\","
|
||||
+ "\"expiresAt\":" + expiresAt
|
||||
+ "}";
|
||||
|
||||
String body = faceAiBase64Url(payload.getBytes(StandardCharsets.UTF_8));
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
|
||||
String signature = faceAiBase64Url(mac.doFinal(body.getBytes(StandardCharsets.UTF_8)));
|
||||
return body + "." + signature;
|
||||
}
|
||||
|
||||
private boolean faceAiRequestIsSecure(javax.servlet.http.HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return false;
|
||||
}
|
||||
if (request.isSecure()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String forwardedProto = request.getHeader("X-Forwarded-Proto");
|
||||
if (forwardedProto != null && "https".equalsIgnoreCase(forwardedProto.trim())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String frontEndHttps = request.getHeader("Front-End-Https");
|
||||
return frontEndHttps != null && "on".equalsIgnoreCase(frontEndHttps.trim());
|
||||
}
|
||||
|
||||
private void faceAiWriteCookieHeader(javax.servlet.http.HttpServletResponse response, String cookieName, String cookieValue, int maxAgeSeconds, boolean secureCookie) {
|
||||
StringBuilder headerValue = new StringBuilder();
|
||||
headerValue.append(cookieName).append('=').append(cookieValue == null ? "" : cookieValue);
|
||||
headerValue.append("; Max-Age=").append(maxAgeSeconds);
|
||||
headerValue.append("; Path=/; HttpOnly; SameSite=Lax");
|
||||
if (secureCookie) {
|
||||
headerValue.append("; Secure");
|
||||
}
|
||||
response.addHeader("Set-Cookie", headerValue.toString());
|
||||
}
|
||||
|
||||
private Object faceAiResolveUser(javax.servlet.jsp.PageContext pageContext) {
|
||||
if (pageContext == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object[] candidates = new Object[] {
|
||||
pageContext.findAttribute("user"),
|
||||
pageContext.findAttribute("utenteLogon")
|
||||
};
|
||||
|
||||
for (int index = 0; index < candidates.length; index++) {
|
||||
Object candidate = candidates[index];
|
||||
if (candidate != null && faceAiUserId(candidate) > 0L) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object faceAiResolveUserFromSession(javax.servlet.jsp.PageContext pageContext) {
|
||||
if (pageContext == null || pageContext.getSession() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object loginUserId = pageContext.getSession().getAttribute("loginUser_id");
|
||||
long resolvedLoginUserId = 0L;
|
||||
if (loginUserId instanceof Number) {
|
||||
resolvedLoginUserId = ((Number) loginUserId).longValue();
|
||||
} else if (loginUserId != null) {
|
||||
try {
|
||||
resolvedLoginUserId = Long.parseLong(String.valueOf(loginUserId));
|
||||
} catch (NumberFormatException ignored) {
|
||||
resolvedLoginUserId = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
if (resolvedLoginUserId <= 0L) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object sessionUser = pageContext.findAttribute("utenteLogon");
|
||||
if (sessionUser == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (faceAiUserId(sessionUser) == resolvedLoginUserId) {
|
||||
return sessionUser;
|
||||
}
|
||||
|
||||
try {
|
||||
Method findByPrimaryKey = sessionUser.getClass().getMethod("findByPrimaryKey", long.class);
|
||||
findByPrimaryKey.invoke(sessionUser, Long.valueOf(resolvedLoginUserId));
|
||||
} catch (NoSuchMethodException missingPrimitiveOverload) {
|
||||
try {
|
||||
Method findByPrimaryKey = sessionUser.getClass().getMethod("findByPrimaryKey", Long.class);
|
||||
findByPrimaryKey.invoke(sessionUser, new Long(resolvedLoginUserId));
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return faceAiUserId(sessionUser) > 0L ? sessionUser : null;
|
||||
}
|
||||
%>
|
||||
<%
|
||||
String faceAiCookieName = faceAiCookieEnv("FACEAI_IDENTITY_COOKIE", "rus_faceai_identity");
|
||||
String faceAiCookieSecret = faceAiCookieEnv("FACEAI_SHARED_SECRET", "disagio-spaghetti-science-lol-boh");
|
||||
Object faceAiRequestUser = pageContext.findAttribute("user");
|
||||
Object faceAiSessionUser = pageContext.findAttribute("utenteLogon");
|
||||
long faceAiRequestUserId = faceAiUserId(faceAiRequestUser);
|
||||
long faceAiSessionUserId = faceAiUserId(faceAiSessionUser);
|
||||
Object faceAiCookieUser = faceAiRequestUserId > 0L ? faceAiRequestUser : (faceAiSessionUserId > 0L ? faceAiSessionUser : faceAiResolveUserFromSession(pageContext));
|
||||
boolean faceAiSecureCookie = faceAiRequestIsSecure(request);
|
||||
|
||||
if (faceAiFeatureEnabled && faceAiCookieUser != null && faceAiUserId(faceAiCookieUser) > 0L) {
|
||||
long faceAiExpiresAt = System.currentTimeMillis() + (30L * 60L * 1000L);
|
||||
try {
|
||||
String faceAiToken = faceAiIdentityToken(faceAiCookieUser, faceAiCookieSecret, faceAiExpiresAt);
|
||||
faceAiWriteCookieHeader(response, faceAiCookieName, faceAiToken, 30 * 60, faceAiSecureCookie);
|
||||
} catch (Exception faceAiIdentityError) {
|
||||
faceAiWriteCookieHeader(response, faceAiCookieName, "", 0, faceAiSecureCookie);
|
||||
log("Unable to mint FaceAI identity cookie", faceAiIdentityError);
|
||||
}
|
||||
} else {
|
||||
faceAiWriteCookieHeader(response, faceAiCookieName, "", 0, faceAiSecureCookie);
|
||||
}
|
||||
%>
|
||||
55
www/_js/lang.js
Normal file
55
www/_js/lang.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// JavaScript Document
|
||||
////////////////////////////////////////////
|
||||
// gestione cambio lingua
|
||||
////////////////////////////////////////////
|
||||
var userLang = navigator.language || navigator.userLanguage;
|
||||
console.log(userLang);
|
||||
|
||||
function changeLang(lang) {
|
||||
var page = window.location.href;
|
||||
var idx = page.lastIndexOf(".");
|
||||
|
||||
if (idx > 0) {
|
||||
var ext = page.substring(idx, page.length);
|
||||
|
||||
if (ext.indexOf("eu") >= 0 || ext.indexOf("com") >= 0 || ext.indexOf("net") >= 0 || ext.indexOf("it") >= 0) {
|
||||
ext = "";
|
||||
var page1 = page;
|
||||
}
|
||||
else
|
||||
|
||||
var page1 = page.substring(0, idx);
|
||||
}
|
||||
else {
|
||||
// caso di pagina root www.xxxx.com/
|
||||
var ext = "";
|
||||
var page1 = page;
|
||||
}
|
||||
// 1 se ho pagine del tipo xxx.html --> aggiungo -lang.html
|
||||
// 2 se ho pagine del tipo xxx_xxx-xxx-xxx-.html --> sostituisco l'ultima parte con xxx-lang.html
|
||||
// 3 se ho pagine del tipo xxx_xxx-xx-xx-lang.html --> sostituisco l'ultima parte
|
||||
// 4 se non ho estensione sono nella root e finisce con /(www.xxxx.com/) --> vado su index-lang
|
||||
// 5 altrimenti vado su index-lang.jsp
|
||||
// 6 caso Ordine.abl --> ritorno alla home
|
||||
|
||||
if (ext == ".abl") {
|
||||
theSvlt = "index-" + lang + ".html";
|
||||
}
|
||||
else if (ext == "") {
|
||||
theSvlt = page1 + "index-" + lang + ".html";
|
||||
}
|
||||
else if (page1.lastIndexOf("-") == (page1.length - 1)) {
|
||||
theSvlt = page1 + lang + ext;
|
||||
}
|
||||
else if (page1.lastIndexOf("-") == (page1.length - 3)) {
|
||||
theSvlt = page1.substring(0, page1.length - 2) + lang + ext;
|
||||
}
|
||||
else if (page1.substring(page1.length - 1, page1.length) != "-") {
|
||||
theSvlt = page1 + "-" + lang + ext;
|
||||
}
|
||||
else {
|
||||
theSvlt = "index-" + lang + ".jsp";
|
||||
}
|
||||
|
||||
location.href = theSvlt;
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@
|
|||
<%@ taglib uri="/WEB-INF/cc.tld" prefix="cc" %>
|
||||
<html lang="<%=lang%>"><!-- InstanceBegin template="/Templates/rus.dwt" codeOutsideHTMLIsLocked="false" -->
|
||||
<head>
|
||||
<jsp:include page="_inc_lang.jsp" flush="true" />
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
|
@ -57,6 +56,10 @@ if (faceAiFeatureEnabledValue == null || faceAiFeatureEnabledValue.trim().length
|
|||
}
|
||||
String faceAiFeatureEnabledNormalized = faceAiFeatureEnabledValue != null ? faceAiFeatureEnabledValue.trim() : "";
|
||||
boolean faceAiFeatureEnabled = !("0".equals(faceAiFeatureEnabledNormalized) || "false".equalsIgnoreCase(faceAiFeatureEnabledNormalized) || "no".equalsIgnoreCase(faceAiFeatureEnabledNormalized) || "off".equalsIgnoreCase(faceAiFeatureEnabledNormalized));
|
||||
%>
|
||||
<%@ include file="_inc_faceai_identity.jsp" %>
|
||||
<jsp:include page="_inc_lang.jsp" flush="true" />
|
||||
<%
|
||||
java.util.Date faceAiRaceDate = CR.getGara().getDataGaraInizio();
|
||||
String faceAiRacePathBase = CR.getGara().getPathBase() != null ? CR.getGara().getPathBase().trim() : "";
|
||||
String faceAiRaceYear = "";
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
<%@ taglib uri="/WEB-INF/cc.tld" prefix="cc" %>
|
||||
<html lang="<%=lang%>"><!-- InstanceBegin template="/Templates/rus.dwt" codeOutsideHTMLIsLocked="false" -->
|
||||
<head>
|
||||
<jsp:include page="_inc_lang.jsp" flush="true" />
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
|
@ -57,6 +56,10 @@ if (faceAiFeatureEnabledValue == null || faceAiFeatureEnabledValue.trim().length
|
|||
}
|
||||
String faceAiFeatureEnabledNormalized = faceAiFeatureEnabledValue != null ? faceAiFeatureEnabledValue.trim() : "";
|
||||
boolean faceAiFeatureEnabled = !("0".equals(faceAiFeatureEnabledNormalized) || "false".equalsIgnoreCase(faceAiFeatureEnabledNormalized) || "no".equalsIgnoreCase(faceAiFeatureEnabledNormalized) || "off".equalsIgnoreCase(faceAiFeatureEnabledNormalized));
|
||||
%>
|
||||
<%@ include file="_inc_faceai_identity.jsp" %>
|
||||
<jsp:include page="_inc_lang.jsp" flush="true" />
|
||||
<%
|
||||
java.util.Date faceAiRaceDate = CR.getGara().getDataGaraInizio();
|
||||
String faceAiRacePathBase = CR.getGara().getPathBase() != null ? CR.getGara().getPathBase().trim() : "";
|
||||
String faceAiRaceYear = "";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue