From: Mathieu Baudier Date: Mon, 16 Dec 2019 10:34:22 +0000 (+0100) Subject: Improve integration of authentication. X-Git-Tag: argeo-commons-2.1.84~12 X-Git-Url: http://git.argeo.org/?p=lgpl%2Fargeo-commons.git;a=commitdiff_plain;h=7e77ed8fc812ea81d5cecec67006fbe363150ef8 Improve integration of authentication. --- diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CmsSession.java b/org.argeo.cms/src/org/argeo/cms/auth/CmsSession.java index f6984bcc6..078543020 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/CmsSession.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/CmsSession.java @@ -9,6 +9,7 @@ import javax.naming.ldap.LdapName; import org.argeo.naming.LdapAttrs; import org.osgi.service.useradmin.Authorization; +/** An authenticated user session. */ public interface CmsSession { final static String USER_DN = LdapAttrs.DN; final static String SESSION_UUID = LdapAttrs.entryUUID.name(); @@ -27,8 +28,9 @@ public interface CmsSession { boolean isAnonymous(); ZonedDateTime getCreationTime(); + ZonedDateTime getEnd(); - + Locale getLocale(); boolean isValid(); diff --git a/org.argeo.cms/src/org/argeo/cms/integration/CmsLoginServlet.java b/org.argeo.cms/src/org/argeo/cms/integration/CmsLoginServlet.java index 47b221688..2adc5371d 100644 --- a/org.argeo.cms/src/org/argeo/cms/integration/CmsLoginServlet.java +++ b/org.argeo.cms/src/org/argeo/cms/integration/CmsLoginServlet.java @@ -1,7 +1,10 @@ package org.argeo.cms.integration; import java.io.IOException; +import java.util.Locale; +import java.util.Set; +import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; @@ -19,14 +22,17 @@ import org.argeo.cms.auth.HttpRequestCallbackHandler; import org.argeo.node.NodeConstants; import org.osgi.service.useradmin.Authorization; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.stream.JsonWriter; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; /** Externally authenticate an http session. */ public class CmsLoginServlet extends HttpServlet { + public final static String PARAM_USERNAME = "username"; + public final static String PARAM_PASSWORD = "password"; + private static final long serialVersionUID = 2478080654328751539L; - private Gson gson = new GsonBuilder().setPrettyPrinting().create(); +// private Gson gson = new GsonBuilder().setPrettyPrinting().create(); + private ObjectMapper objectMapper = new ObjectMapper(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) @@ -38,82 +44,85 @@ public class CmsLoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { LoginContext lc = null; - String username = request.getParameter("username"); - String password = request.getParameter("password"); - if (username != null && password != null) { - try { - lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, - new HttpRequestCallbackHandler(request, response) { - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - for (Callback callback : callbacks) { - if (callback instanceof NameCallback && username != null) - ((NameCallback) callback).setName(username); - else if (callback instanceof PasswordCallback && password != null) - ((PasswordCallback) callback).setPassword(password.toCharArray()); - else if (callback instanceof HttpRequestCallback) { - ((HttpRequestCallback) callback).setRequest(request); - ((HttpRequestCallback) callback).setResponse(response); - } - } - } - }); - lc.login(); - - CmsSessionId cmsSessionId = (CmsSessionId) lc.getSubject().getPrivateCredentials(CmsSessionId.class) - .toArray()[0]; - Authorization authorization = (Authorization) lc.getSubject().getPrivateCredentials(Authorization.class) - .toArray()[0]; - - JsonWriter jsonWriter = gson.newJsonWriter(response.getWriter()); - jsonWriter.beginObject(); - // Authorization - jsonWriter.name("username").value(authorization.getName()); - jsonWriter.name("displayName").value(authorization.toString()); - // Roles - jsonWriter.name("roles").beginArray(); - for (String role : authorization.getRoles()) - if (!role.equals(authorization.getName())) - jsonWriter.value(role); - jsonWriter.endArray(); - // CMS session - jsonWriter.name("cmsSession").beginObject(); - jsonWriter.name("uuid").value(cmsSessionId.getUuid().toString()); - jsonWriter.endObject(); - - // extensions - enrichJson(jsonWriter); - - jsonWriter.endObject(); - - String redirectTo = redirectTo(request); - if (redirectTo != null) - response.sendRedirect(redirectTo); - } catch (LoginException e) { - response.setStatus(403); + String username = request.getParameter(PARAM_USERNAME); + String password = request.getParameter(PARAM_PASSWORD); + try { + lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, new HttpRequestCallbackHandler(request, response) { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback && username != null) + ((NameCallback) callback).setName(username); + else if (callback instanceof PasswordCallback && password != null) + ((PasswordCallback) callback).setPassword(password.toCharArray()); + else if (callback instanceof HttpRequestCallback) { + ((HttpRequestCallback) callback).setRequest(request); + ((HttpRequestCallback) callback).setResponse(response); + } + } + } + }); + lc.login(); + + Subject subject = lc.getSubject(); + CmsSessionId cmsSessionId = extractFrom(subject.getPrivateCredentials(CmsSessionId.class)); + if (cmsSessionId == null) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } - } else { - response.setStatus(403); + Authorization authorization = extractFrom(subject.getPrivateCredentials(Authorization.class)); + Locale locale = extractFrom(subject.getPublicCredentials(Locale.class)); + + CmsSessionDescriptor cmsSessionDescriptor = new CmsSessionDescriptor(authorization.getName(), + cmsSessionId.getUuid().toString(), authorization.getRoles(), authorization.toString(), + locale != null ? locale.toString() : null); + + response.setContentType("application/json"); + JsonGenerator jg = objectMapper.getFactory().createGenerator(response.getWriter()); + jg.writeObject(cmsSessionDescriptor); + +// JsonWriter jsonWriter = gson.newJsonWriter(response.getWriter()); +// jsonWriter.beginObject(); +// // Authorization +// jsonWriter.name("username").value(authorization.getName()); +// jsonWriter.name("displayName").value(authorization.toString()); +// // Roles +// jsonWriter.name("roles").beginArray(); +// for (String role : authorization.getRoles()) +// if (!role.equals(authorization.getName())) +// jsonWriter.value(role); +// jsonWriter.endArray(); +// // CMS session +// jsonWriter.name("cmsSession").beginObject(); +// jsonWriter.name("uuid").value(cmsSessionId.getUuid().toString()); +// jsonWriter.endObject(); +// +// // extensions +// enrichJson(jsonWriter); +// +// jsonWriter.endObject(); + + String redirectTo = redirectTo(request); + if (redirectTo != null) + response.sendRedirect(redirectTo); + } catch (LoginException e) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } } - /** - * To be overridden. The object will be ended by the caller. Does nothing by - * default. - */ - protected void enrichJson(JsonWriter jsonWriter) { - - } - - /** Does nothing by default. */ - protected void loginSucceeded(LoginContext lc, HttpServletRequest request, HttpServletResponse response) { - + protected T extractFrom(Set creds) { + if (creds.size() > 0) + return creds.iterator().next(); + else + return null; } - /** Send HTTP code 403 by default. */ - protected void loginFailed(LoginContext lc, HttpServletRequest request, HttpServletResponse response) { - + /** + * To be overridden in order to return a richer {@link CmsSessionDescriptor} to + * be serialized. + */ + protected CmsSessionDescriptor enrichJson(CmsSessionDescriptor cmsSessionDescriptor) { + return cmsSessionDescriptor; } protected String redirectTo(HttpServletRequest request) { diff --git a/org.argeo.cms/src/org/argeo/cms/integration/CmsLogoutServlet.java b/org.argeo.cms/src/org/argeo/cms/integration/CmsLogoutServlet.java new file mode 100644 index 000000000..50967391c --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/integration/CmsLogoutServlet.java @@ -0,0 +1,74 @@ +package org.argeo.cms.integration; + +import java.io.IOException; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.argeo.cms.auth.CmsSessionId; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.auth.HttpRequestCallback; +import org.argeo.cms.auth.HttpRequestCallbackHandler; +import org.argeo.node.NodeConstants; + +/** Externally authenticate an http session. */ +public class CmsLogoutServlet extends HttpServlet { + private static final long serialVersionUID = 2478080654328751539L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + doPost(request, response); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + LoginContext lc = null; + try { + lc = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, new HttpRequestCallbackHandler(request, response) { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof HttpRequestCallback) { + ((HttpRequestCallback) callback).setRequest(request); + ((HttpRequestCallback) callback).setResponse(response); + } + } + } + }); + lc.login(); + + Subject subject = lc.getSubject(); + CmsSessionId cmsSessionId = extractFrom(subject.getPrivateCredentials(CmsSessionId.class)); + if (cmsSessionId != null) {// logged in + CurrentUser.logoutCmsSession(subject); + } + + } catch (LoginException e) { + // ignore + } + + String redirectTo = redirectTo(request); + if (redirectTo != null) + response.sendRedirect(redirectTo); + } + + protected T extractFrom(Set creds) { + if (creds.size() > 0) + return creds.iterator().next(); + else + return null; + } + + protected String redirectTo(HttpServletRequest request) { + return null; + } +} diff --git a/org.argeo.cms/src/org/argeo/cms/integration/CmsSessionDescriptor.java b/org.argeo.cms/src/org/argeo/cms/integration/CmsSessionDescriptor.java new file mode 100644 index 000000000..19c244375 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/integration/CmsSessionDescriptor.java @@ -0,0 +1,96 @@ +package org.argeo.cms.integration; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; + +import org.argeo.cms.auth.CmsSession; +import org.osgi.service.useradmin.Authorization; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** A serializable descriptor of an internal {@link CmsSession}. */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class CmsSessionDescriptor implements Serializable, Authorization { + private static final long serialVersionUID = 8592162323372641462L; + + private String name; + private String cmsSessionId; + private String displayName; + private String locale; + private Set roles; + + public CmsSessionDescriptor() { + } + + public CmsSessionDescriptor(String name, String cmsSessionId, String[] roles, String displayName, String locale) { + this.name = name; + this.displayName = displayName; + this.cmsSessionId = cmsSessionId; + this.locale = locale; + this.roles = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList(roles))); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getCmsSessionId() { + return cmsSessionId; + } + + public void setCmsSessionId(String cmsSessionId) { + this.cmsSessionId = cmsSessionId; + } + + public Boolean isAnonymous() { + return name == null; + } + + public String getLocale() { + return locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } + + @Override + public boolean hasRole(String name) { + return roles.contains(name); + } + + @Override + public String[] getRoles() { + return roles.toArray(new String[roles.size()]); + } + + public void setRoles(String[] roles) { + this.roles = Collections.unmodifiableSortedSet(new TreeSet<>(Arrays.asList(roles))); + } + + @Override + public int hashCode() { + return cmsSessionId != null ? cmsSessionId.hashCode() : super.hashCode(); + } + + @Override + public String toString() { + return displayName != null ? displayName : name != null ? name : super.toString(); + } + +}