diff --git a/README.md b/README.md index 3ab8d10f..bf89142c 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,29 @@ Check out the [docker section](./docker/README.) Individual test result reports can be found in wisemapping-open-source/wise-webapp/target/failsafe-reports/index.html Test coverage report of unit and integration test can be found in wisemapping-open-source/wise-webapp/target/site/jacoco and wisemapping-open-source/wise-webapp/target/site/jacoco-it folders. Coverage report is generated in the verify phase of [lifecicle](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#introduction-to-the-build-lifecyclea) using [jacoco](https://www.jacoco.org/jacoco/trunk/doc/maven.html) + +## Google authorization + +You must configure the following wisemapping properties (app.properties) in order to get google authorization working + * `security.oauth2.google.callbackUrl`: url where google will redirect after user authentication, tipically {frontendBaseUrl}/c/registration-google. Also, this url must be defined in google app configuration + * `security.oauth2.google.clientId`: client id from google app + * `security.oauth2.google.clientSecret`: client secret from google app + +You must create a Google Application in [Google Cloud](https://console.cloud.google.com) and complete all the information required by Google. Here are the most important properties. + +Oauth consent screen + * Authorized domains: wisemapping domain (ex: wisemapping.com), and you can add domains of other environments if needed + * Permissions + * `https://www.googleapis.com/auth/userinfo.profile` + * `https://www.googleapis.com/auth/userinfo.email` + * Test users: emails for testing, those can be used before the application is validated by Google + +After that, in Credentials, you must create an `Oauth Client Id` credential + * Authorized JavaScript origins: list of authorized domains from which to redirect to Google. Ex: `https://wisemaping.com`, `https://wisemapping-testing.com:8080` + * Authorized redirect URIs: list of allowed urls to which google will redirect after authenticating . Ex: `https://wisemaping.com/c/registration-google`, `https://wisemapping-testing.com:8080/c/registration-google` + +After credential was created, Google will show you the clientId and clientSecret to configure your application. For productive applications, you must **publish** your application, this is a validation process with Google. + ## Members ### Founders diff --git a/config/database/hsql/create-schemas.sql b/config/database/hsql/create-schemas.sql index 3e98ae79..50fe1d76 100644 --- a/config/database/hsql/create-schemas.sql +++ b/config/database/hsql/create-schemas.sql @@ -15,6 +15,9 @@ CREATE TABLE USER ( activation_date DATE, allow_send_email CHAR(1) NOT NULL, locale VARCHAR(5), + google_sync BOOLEAN, + sync_code VARCHAR(255), + google_token VARCHAR(255), FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id) ); diff --git a/config/database/mysql/create-schemas.sql b/config/database/mysql/create-schemas.sql index 974a14a4..e8d67e90 100644 --- a/config/database/mysql/create-schemas.sql +++ b/config/database/mysql/create-schemas.sql @@ -25,6 +25,9 @@ CREATE TABLE USER ( activation_date DATE, allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0, locale VARCHAR(5), + google_sync BOOL, + sync_code VARCHAR(255), + google_token VARCHAR(255), FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id) ON DELETE CASCADE ON UPDATE NO ACTION diff --git a/config/database/postgres/create-schemas.sql b/config/database/postgres/create-schemas.sql index acc839bd..c9ec9b20 100644 --- a/config/database/postgres/create-schemas.sql +++ b/config/database/postgres/create-schemas.sql @@ -15,6 +15,9 @@ CREATE TABLE "user" ( activation_date DATE, allow_send_email TEXT NOT NULL DEFAULT 0, locale VARCHAR(5), + google_sync BOOLEAN, + sync_code VARCHAR(255), + google_token VARCHAR(255), FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id) ON DELETE CASCADE ON UPDATE NO ACTION ); @@ -27,14 +30,6 @@ CREATE TABLE LABEL ( --FOREIGN KEY (creator_id) REFERENCES USER (colaborator_id) ); -CREATE TABLE R_LABEL_MINDMAP ( - mindmap_id INTEGER NOT NULL, - label_id INTEGER NOT NULL, - PRIMARY KEY (mindmap_id, label_id), - FOREIGN KEY (mindmap_id) REFERENCES MINDMAP (id), - FOREIGN KEY (label_id) REFERENCES LABEL (id) ON DELETE CASCADE ON UPDATE NO ACTION -); - CREATE TABLE MINDMAP ( id SERIAL NOT NULL PRIMARY KEY, title VARCHAR(255) NOT NULL, @@ -44,11 +39,17 @@ CREATE TABLE MINDMAP ( creation_date TIMESTAMP, edition_date TIMESTAMP, creator_id INTEGER NOT NULL, - tags VARCHAR(1014), last_editor_id INTEGER NOT NULL --, --FOREIGN KEY(creator_id) REFERENCES "USER"(colaborator_id) ON DELETE CASCADE ON UPDATE NO ACTION ); +CREATE TABLE R_LABEL_MINDMAP ( + mindmap_id INTEGER NOT NULL, + label_id INTEGER NOT NULL, + PRIMARY KEY (mindmap_id, label_id), + FOREIGN KEY (mindmap_id) REFERENCES MINDMAP (id), + FOREIGN KEY (label_id) REFERENCES LABEL (id) ON DELETE CASCADE ON UPDATE NO ACTION +); CREATE TABLE MINDMAP_HISTORY (id SERIAL NOT NULL PRIMARY KEY, @@ -77,14 +78,6 @@ CREATE TABLE COLLABORATION ( FOREIGN KEY (properties_id) REFERENCES COLLABORATION_PROPERTIES (id) ON DELETE CASCADE ON UPDATE NO ACTION ); -CREATE TABLE TAG ( - id SERIAL NOT NULL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - user_id INTEGER NOT NULL --, ---FOREIGN KEY(user_id) REFERENCES "USER"(colaborator_id) ON DELETE CASCADE ON UPDATE NO ACTION -); - - CREATE TABLE ACCESS_AUDITORY ( id SERIAL NOT NULL PRIMARY KEY, login_date DATE, diff --git a/distribution/Dockerfile b/distribution/Dockerfile index dd487f39..e185fa35 100644 --- a/distribution/Dockerfile +++ b/distribution/Dockerfile @@ -2,7 +2,7 @@ # Based on ubuntu:latest, installs WiseMapping (http://ww.wisemapping.org) # Based info setup ... -FROM tomcat:9.0-jdk17-openjdk +FROM tomcat:jdk17 LABEL maintainer="Paulo Gustavo Veiga " # Build variables ... @@ -31,6 +31,9 @@ RUN sed -i 's|\ |' \ /usr/local/tomcat/conf/server.xml +RUN sed -i 's||\ + |' \ + /usr/local/tomcat/conf/context.xml # Copy default HSQL DB for testing ... RUN mkdir -p ${DB_BASE_DIR}/db COPY db/ ${DB_BASE_DIR}/db diff --git a/distribution/README.md b/distribution/README.md index fe858c97..add8f187 100644 --- a/distribution/README.md +++ b/distribution/README.md @@ -42,7 +42,7 @@ Depending on the database your want to configure, you can create initialization The next step is configure the WiseMapping for the database and credentials. Download `app.properties` configuration file and configure the required sections: -> $ curl https://bitbucket.org/wisemapping/wisemapping-open-source/raw/644b7078d790220c7844b732a83d45495f11d64e/wise-webapp/src/main/webapp/WEB-INF/app.properties +> $ curl https://bitbucket.org/wisemapping/wisemapping-open-source/src/master/wise-webapp/src/main/webapp/WEB-INF/app.properties ### Starting the application diff --git a/distribution/mysql-init/0002create-schemas.sql b/distribution/mysql-init/0002create-schemas.sql index eba00226..1fee2371 100644 --- a/distribution/mysql-init/0002create-schemas.sql +++ b/distribution/mysql-init/0002create-schemas.sql @@ -25,6 +25,9 @@ CREATE TABLE USER ( activation_date DATE, allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0, locale VARCHAR(5), + google_sync BOOL, + sync_code VARCHAR(255), + google_token VARCHAR(255), FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id) ON DELETE CASCADE ON UPDATE NO ACTION @@ -113,17 +116,6 @@ CREATE TABLE COLLABORATION ( ) CHARACTER SET utf8; -CREATE TABLE TAG ( - id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, - name VARCHAR(255) - CHARACTER SET utf8 NOT NULL, - user_id INTEGER NOT NULL, - FOREIGN KEY (user_id) REFERENCES USER (colaborator_id) - ON DELETE CASCADE - ON UPDATE NO ACTION -) - CHARACTER SET utf8; - CREATE TABLE ACCESS_AUDITORY ( id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, login_date DATE, diff --git a/distribution/registration-google/update-db-mysql.sql b/distribution/registration-google/update-db-mysql.sql new file mode 100644 index 00000000..ede72a63 --- /dev/null +++ b/distribution/registration-google/update-db-mysql.sql @@ -0,0 +1,4 @@ +ALTER TABLE USER +ADD COLUMN `google_sync` TINYINT(1) NULL, +ADD COLUMN `sync_code` VARCHAR(255) NULL, +ADD COLUMN `google_token` VARCHAR(255) NULL; diff --git a/distribution/registration-google/update-db-postgres.sql b/distribution/registration-google/update-db-postgres.sql new file mode 100644 index 00000000..846add61 --- /dev/null +++ b/distribution/registration-google/update-db-postgres.sql @@ -0,0 +1,4 @@ +ALTER TABLE "user" +ADD COLUMN `google_sync` BOOLEAN NULL, +ADD COLUMN `sync_code` VARCHAR(255) NULL, +ADD COLUMN `google_token` VARCHAR(255) NULL; diff --git a/pom.xml b/pom.xml index 40def38e..771be4c5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd"> - 5.0.14 + 5.0.19 ${project.basedir}/wise-webapps @@ -16,7 +16,7 @@ org.wisemapping wisemapping WiseMapping Project - 5.0.14 + 5.0.19 pom diff --git a/wise-ui/pom.xml b/wise-ui/pom.xml index 8427463b..496b18d7 100644 --- a/wise-ui/pom.xml +++ b/wise-ui/pom.xml @@ -12,7 +12,7 @@ org.wisemapping wisemapping ../pom.xml - 5.0.14 + 5.0.19 @@ -29,11 +29,11 @@ - + - + @@ -42,11 +42,11 @@ - + - + diff --git a/wise-webapp/pom.xml b/wise-webapp/pom.xml index a144681b..6bead038 100644 --- a/wise-webapp/pom.xml +++ b/wise-webapp/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 wise-webapp war @@ -8,13 +9,13 @@ org.wisemapping wisemapping ../pom.xml - 5.0.14 + 5.0.19 - 5.3.22 - 5.6.2 - 5.6.11.Final + 5.3.24 + 5.7.3 + 5.6.12.Final 6.0.21.Final 5.6.1 @@ -41,7 +42,7 @@ com.intellij annotations - 7.0.3 + 12.0 compile @@ -50,6 +51,11 @@ ${org.springframework.version} compile + + org.postgresql + postgresql + 42.5.1 + org.springframework.security spring-security-ldap @@ -195,7 +201,7 @@ mysql mysql-connector-java - 8.0.28 + 8.0.31 runtime @@ -219,10 +225,9 @@ 3.9.9 - log4j - log4j - 1.2.17 - compile + org.apache.logging.log4j + log4j-core + 2.19.0 @@ -240,7 +245,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.1 + 2.13.4.2 @@ -291,15 +296,10 @@ - - mysql - mysql-connector-java - 5.1.5 - org.hsqldb hsqldb - 2.6.1 + 2.7.1 @@ -343,7 +343,7 @@ mysql mysql-connector-java - 5.1.5 + 8.0.31 @@ -498,13 +498,13 @@ - - - - - - - + + + + + + + default-report verify @@ -559,7 +559,9 @@ true false 200 - ${integrationTestArgLine} -Ddatabase.base.url=${project.build.directory} -Djetty.port=8080 + ${integrationTestArgLine} -Ddatabase.base.url=${project.build.directory} + -Djetty.port=8080 + diff --git a/wise-webapp/src/main/java/com/wisemapping/dao/UserManagerImpl.java b/wise-webapp/src/main/java/com/wisemapping/dao/UserManagerImpl.java index d331d2e6..e568f0ad 100644 --- a/wise-webapp/src/main/java/com/wisemapping/dao/UserManagerImpl.java +++ b/wise-webapp/src/main/java/com/wisemapping/dao/UserManagerImpl.java @@ -19,6 +19,7 @@ package com.wisemapping.dao; import com.wisemapping.model.AccessAuditory; +import com.wisemapping.model.AuthenticationType; import com.wisemapping.model.Collaboration; import com.wisemapping.model.Collaborator; import com.wisemapping.model.User; @@ -31,7 +32,6 @@ import org.springframework.orm.hibernate5.HibernateTemplate; import org.springframework.orm.hibernate5.support.HibernateDaoSupport; import org.springframework.security.crypto.password.PasswordEncoder; -import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; @@ -101,7 +101,11 @@ public class UserManagerImpl @Override public void createUser(User user) { assert user != null : "Trying to store a null user"; - user.setPassword(passwordEncoder.encode(user.getPassword())); + if (!AuthenticationType.GOOGLE_OAUTH2.equals(user.getAuthenticationType())) { + user.setPassword(passwordEncoder.encode(user.getPassword())); + } else { + user.setPassword(""); + } getHibernateTemplate().saveOrUpdate(user); } diff --git a/wise-webapp/src/main/java/com/wisemapping/filter/CorsFilter.java b/wise-webapp/src/main/java/com/wisemapping/filter/CorsFilter.java new file mode 100644 index 00000000..5cc60cdb --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/filter/CorsFilter.java @@ -0,0 +1,79 @@ +/* +* Copyright [2022] [wisemapping] +* +* Licensed under WiseMapping Public License, Version 1.0 (the "License"). +* It is basically the Apache License, Version 2.0 (the "License") plus the +* "powered by wisemapping" text requirement on every single page; +* you may not use this file except in compliance with the License. +* You may obtain a copy of the license at +* +* http://www.wisemapping.org/license +* +* 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. +*/ + +package com.wisemapping.filter; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +/** + * + * If your wisemapping customization throws cross domain errores in browser, you can configure this filter in webdefault.xml + * By default it will accept all domains, but you can restrict to the domain you need + * + * + * cross-origin + * com.wisemapping.filter.CorsFilter + * + * allowedOrigins + * * + * + * + * allowedMethods + * GET,POST,HEAD + * + * + * allowedHeaders + * X-Requested-With,Content-Type,Accept,Origin + * + * + * + * cross-origin + * /* + * + * + */ +public class CorsFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) + throws IOException, ServletException { + if (servletResponse != null) { + // Authorize (allow) all domains to consume the content + ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Origin", "*"); + ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Methods","GET, OPTIONS, HEAD, PUT, POST"); + } + + chain.doFilter(servletRequest, servletResponse); + } + + @Override + public void destroy() { + } +} diff --git a/wise-webapp/src/main/java/com/wisemapping/filter/RequestPropertiesInterceptor.java b/wise-webapp/src/main/java/com/wisemapping/filter/RequestPropertiesInterceptor.java index 4317910a..1d257177 100644 --- a/wise-webapp/src/main/java/com/wisemapping/filter/RequestPropertiesInterceptor.java +++ b/wise-webapp/src/main/java/com/wisemapping/filter/RequestPropertiesInterceptor.java @@ -54,6 +54,9 @@ public class RequestPropertiesInterceptor implements HandlerInterceptor { @Value("${security.type}") private String securityType; + @Value("${security.oauth2.google.url}") + private String googleOauth2Url; + @Override public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, Object object) throws Exception { @@ -64,6 +67,8 @@ public class RequestPropertiesInterceptor implements HandlerInterceptor { request.setAttribute("google.recaptcha2.enabled", recaptcha2Enabled); request.setAttribute("google.recaptcha2.siteKey", recaptcha2SiteKey); + request.setAttribute("security.oauth2.google.url", googleOauth2Url); + request.setAttribute("site.homepage", siteHomepage); request.setAttribute("site.static.js.url", siteStaticUrl); diff --git a/wise-webapp/src/main/java/com/wisemapping/listener/UnlockOnExpireListener.java b/wise-webapp/src/main/java/com/wisemapping/listener/UnlockOnExpireListener.java index f49eeab1..008c8155 100644 --- a/wise-webapp/src/main/java/com/wisemapping/listener/UnlockOnExpireListener.java +++ b/wise-webapp/src/main/java/com/wisemapping/listener/UnlockOnExpireListener.java @@ -24,8 +24,10 @@ import com.wisemapping.model.User; import com.wisemapping.security.Utils; import com.wisemapping.service.LockManager; import com.wisemapping.service.MindmapService; -import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; @@ -34,7 +36,7 @@ import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class UnlockOnExpireListener implements HttpSessionListener { - private static final Logger logger = Logger.getLogger(UnlockOnExpireListener.class); + private static final Logger logger = LogManager.getLogger(); @Override public void sessionCreated(@NotNull HttpSessionEvent event) { diff --git a/wise-webapp/src/main/java/com/wisemapping/mail/NotificationService.java b/wise-webapp/src/main/java/com/wisemapping/mail/NotificationService.java index 1a7aaf6c..ce52f098 100644 --- a/wise-webapp/src/main/java/com/wisemapping/mail/NotificationService.java +++ b/wise-webapp/src/main/java/com/wisemapping/mail/NotificationService.java @@ -24,7 +24,8 @@ import com.wisemapping.model.Mindmap; import com.wisemapping.model.User; import com.wisemapping.rest.model.RestLogItem; import org.apache.commons.lang.StringEscapeUtils; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; @@ -42,7 +43,7 @@ import java.util.Map; import java.util.stream.Collectors; final public class NotificationService { - final private static Logger logger = Logger.getLogger(Mailer.class); + final private static Logger logger = LogManager.getLogger(); private ResourceBundleMessageSource messageSource; @Autowired diff --git a/wise-webapp/src/main/java/com/wisemapping/model/AuthenticationType.java b/wise-webapp/src/main/java/com/wisemapping/model/AuthenticationType.java index 92b7725e..f1fd4b71 100644 --- a/wise-webapp/src/main/java/com/wisemapping/model/AuthenticationType.java +++ b/wise-webapp/src/main/java/com/wisemapping/model/AuthenticationType.java @@ -23,7 +23,8 @@ import org.jetbrains.annotations.NotNull; public enum AuthenticationType { DATABASE('D'), LDAP('L'), - OPENID('O'); + GOOGLE_OAUTH2('G'); + private final char schemaCode; AuthenticationType(char schemaCode) { diff --git a/wise-webapp/src/main/java/com/wisemapping/model/Collaboration.java b/wise-webapp/src/main/java/com/wisemapping/model/Collaboration.java index 7446b6f5..40d892b8 100644 --- a/wise-webapp/src/main/java/com/wisemapping/model/Collaboration.java +++ b/wise-webapp/src/main/java/com/wisemapping/model/Collaboration.java @@ -19,13 +19,12 @@ package com.wisemapping.model; -import org.hibernate.annotations.Fetch; -import org.hibernate.annotations.FetchMode; import org.jetbrains.annotations.Nullable; import javax.persistence.*; import javax.validation.constraints.NotNull; import java.io.Serializable; +import java.util.Objects; @Entity @Table(name = "COLLABORATION") @@ -127,16 +126,14 @@ public class Collaboration implements Serializable { Collaboration that = (Collaboration) o; if (id != that.id) return false; - if (collaborator != null ? !collaborator.equals(that.collaborator) : that.collaborator != null) return false; - if (mindMap != null ? !mindMap.equals(that.mindMap) : that.mindMap != null) return false; + if (!Objects.equals(collaborator, that.collaborator)) return false; + if (!Objects.equals(mindMap, that.mindMap)) return false; return role == that.role; } @Override public int hashCode() { - int result = id ^ (id >>> 32); - result = 31 * result + (role != null ? role.hashCode() : 0); - result = 31 * result + (mindMap != null ? mindMap.hashCode() : 0); - return result; + //https://thorben-janssen.com/ultimate-guide-to-implementing-equals-and-hashcode-with-hibernate/ + return 13; } } diff --git a/wise-webapp/src/main/java/com/wisemapping/model/Collaborator.java b/wise-webapp/src/main/java/com/wisemapping/model/Collaborator.java index bf1d1c2e..02e507b7 100755 --- a/wise-webapp/src/main/java/com/wisemapping/model/Collaborator.java +++ b/wise-webapp/src/main/java/com/wisemapping/model/Collaborator.java @@ -105,7 +105,7 @@ public class Collaborator implements Serializable { int id = this.getId(); String email = this.getEmail(); - int result = (int) (id ^ (id >>> 32)); + int result = id ^ (id >>> 32); result = 31 * result + (email != null ? email.hashCode() : 0); return result; } diff --git a/wise-webapp/src/main/java/com/wisemapping/model/Label.java b/wise-webapp/src/main/java/com/wisemapping/model/Label.java index 95e80415..6126560d 100644 --- a/wise-webapp/src/main/java/com/wisemapping/model/Label.java +++ b/wise-webapp/src/main/java/com/wisemapping/model/Label.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable; import javax.persistence.*; import java.io.Serializable; +import java.util.Objects; @Entity @Table(name = "LABEL") @@ -34,17 +35,22 @@ public class Label implements Serializable { @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; - @NotNull private String title; - @NotNull private String color; - @Nullable private String iconName; + @NotNull + private String title; + @NotNull + private String color; + @Nullable + private String iconName; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name="creator_id",nullable = true,unique = true) - @NotNull private User creator; + @JoinColumn(name = "creator_id", nullable = true, unique = true) + @NotNull + private User creator; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name="parent_label_id",nullable = true) - @Nullable private Label parent; + @JoinColumn(name = "parent_label_id", nullable = true) + @Nullable + private Label parent; public void setParent(@Nullable Label parent) { this.parent = parent; @@ -104,17 +110,15 @@ public class Label implements Serializable { if (this == o) return true; if (!(o instanceof Label)) return false; - Label label = (Label) o; - + final Label label = (Label) o; return id == label.id && creator.getId() == label.creator.getId() - && !(parent != null ? !parent.equals(label.parent) : label.parent != null); + && Objects.equals(parent, label.parent); } @Override public int hashCode() { - long result = id; - result = 31 * result + title.hashCode(); - result = 31 * result + (creator!=null?creator.hashCode():0); + long result = title.hashCode(); + result = 31 * result + (creator != null ? creator.hashCode() : 0); result = 31 * result + (parent != null ? parent.hashCode() : 0); return (int) result; } diff --git a/wise-webapp/src/main/java/com/wisemapping/model/Mindmap.java b/wise-webapp/src/main/java/com/wisemapping/model/Mindmap.java index d3cdf366..e134d514 100644 --- a/wise-webapp/src/main/java/com/wisemapping/model/Mindmap.java +++ b/wise-webapp/src/main/java/com/wisemapping/model/Mindmap.java @@ -83,13 +83,9 @@ public class Mindmap implements Serializable { @Basic(fetch = FetchType.LAZY) private byte[] zippedXml; - //~ Constructors ......................................................................................... - public Mindmap() { } - //~ Methods .............................................................................................. - public void setUnzipXml(@NotNull byte[] value) { try { final byte[] zip = ZipUtils.bytesToZip(value); @@ -146,7 +142,9 @@ public class Mindmap implements Serializable { } public void removedCollaboration(@NotNull Collaboration collaboration) { - collaborations.remove(collaboration); + // https://stackoverflow.com/questions/25125210/hibernate-persistentset-remove-operation-not-working + this.collaborations.remove(collaboration); + collaboration.setMindMap(null); } public void removedCollaboration(@NotNull Set collaborations) { @@ -316,11 +314,20 @@ public class Mindmap implements Serializable { final StringBuilder result = new StringBuilder(); result.append(""); result.append(""); return result.toString(); } + static private String escapeXmlAttribute(String attValue) { + // Hack: Find out of the box function. + String result = attValue.replace("&", "&"); + result = result.replace("<", "<"); + result = result.replace("gt", ">"); + result = result.replace("\"", """); + return result; + } + public Mindmap shallowClone() { final Mindmap result = new Mindmap(); result.setDescription(this.getDescription()); @@ -351,18 +358,6 @@ public class Mindmap implements Serializable { return false; } - @Nullable - public Label findLabel(int labelId) { - Label result = null; - for (Label label : this.labels) { - if (label.getId() == labelId) { - result = label; - break; - } - } - return result; - } - public void removeLabel(@NotNull final Label label) { this.labels.remove(label); } diff --git a/wise-webapp/src/main/java/com/wisemapping/model/User.java b/wise-webapp/src/main/java/com/wisemapping/model/User.java index 261471a8..4040e4f2 100644 --- a/wise-webapp/src/main/java/com/wisemapping/model/User.java +++ b/wise-webapp/src/main/java/com/wisemapping/model/User.java @@ -18,15 +18,12 @@ package com.wisemapping.model; -import org.hibernate.annotations.CacheConcurrencyStrategy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.persistence.*; import java.io.Serializable; import java.util.Calendar; -import java.util.HashSet; -import java.util.Set; @Entity @Table(name = "USER") @@ -39,21 +36,30 @@ public class User private String lastname; private String password; private String locale; - + @Column(name = "activation_code") private long activationCode; - + @Column(name = "activation_date") private Calendar activationDate; - + @Column(name = "allow_send_email") private boolean allowSendEmail = false; - + @Column(name = "authentication_type") private Character authenticationTypeCode = AuthenticationType.DATABASE.getCode(); - + @Column(name = "authenticator_uri") private String authenticatorUri; + + @Column(name = "google_sync") + private Boolean googleSync; + + @Column(name = "sync_code") + private String syncCode; + + @Column(name = "google_token") + private String googleToken; public User() { } @@ -151,7 +157,35 @@ public class User this.authenticatorUri = authenticatorUri; } - @Override + public void setAuthenticationTypeCode(Character authenticationTypeCode) { + this.authenticationTypeCode = authenticationTypeCode; + } + + public Boolean getGoogleSync() { + return googleSync; + } + + public void setGoogleSync(Boolean googleSync) { + this.googleSync = googleSync; + } + + public String getSyncCode() { + return syncCode; + } + + public void setSyncCode(String syncCode) { + this.syncCode = syncCode; + } + + public String getGoogleToken() { + return googleToken; + } + + public void setGoogleToken(String googleToken) { + this.googleToken = googleToken; + } + + @Override public String toString() { return "User{" + "firstname='" + firstname + '\'' + diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/AccountController.java b/wise-webapp/src/main/java/com/wisemapping/rest/AccountController.java index d6f0921b..bccece0d 100644 --- a/wise-webapp/src/main/java/com/wisemapping/rest/AccountController.java +++ b/wise-webapp/src/main/java/com/wisemapping/rest/AccountController.java @@ -19,19 +19,15 @@ package com.wisemapping.rest; import com.wisemapping.exceptions.WiseMappingException; -import com.wisemapping.mail.NotificationService; import com.wisemapping.model.Collaboration; import com.wisemapping.model.Label; import com.wisemapping.model.Mindmap; import com.wisemapping.model.User; -import com.wisemapping.rest.model.RestLogItem; import com.wisemapping.rest.model.RestUser; import com.wisemapping.security.Utils; import com.wisemapping.service.LabelService; import com.wisemapping.service.MindmapService; import com.wisemapping.service.UserService; -import org.apache.log4j.Logger; -import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpStatus; @@ -41,7 +37,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseStatus; -import javax.servlet.http.HttpServletRequest; import java.util.List; @Controller @@ -58,10 +53,6 @@ public class AccountController extends BaseController { @Autowired private LabelService labelService; - @Autowired - private NotificationService notificationService; - - @RequestMapping(method = RequestMethod.PUT, value = "account/password", consumes = {"text/plain"}) @ResponseStatus(value = HttpStatus.NO_CONTENT) public void changePassword(@RequestBody String password) { diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/BaseController.java b/wise-webapp/src/main/java/com/wisemapping/rest/BaseController.java index 5c9f0fde..2eadc4a9 100644 --- a/wise-webapp/src/main/java/com/wisemapping/rest/BaseController.java +++ b/wise-webapp/src/main/java/com/wisemapping/rest/BaseController.java @@ -24,7 +24,8 @@ import com.wisemapping.model.User; import com.wisemapping.rest.model.RestErrors; import com.wisemapping.security.Utils; import com.wisemapping.service.RegistrationException; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -42,7 +43,7 @@ import java.util.Locale; public class BaseController { - final private Logger logger = Logger.getLogger(BaseController.class); + final private Logger logger = LogManager.getLogger(); @Qualifier("messageSource") @Autowired diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/MindmapController.java b/wise-webapp/src/main/java/com/wisemapping/rest/MindmapController.java index 416233f8..2b01d6ec 100644 --- a/wise-webapp/src/main/java/com/wisemapping/rest/MindmapController.java +++ b/wise-webapp/src/main/java/com/wisemapping/rest/MindmapController.java @@ -25,7 +25,8 @@ import com.wisemapping.security.Utils; import com.wisemapping.service.*; import com.wisemapping.validator.MapInfoValidator; import org.apache.commons.validator.routines.EmailValidator; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -46,7 +47,7 @@ import java.util.stream.Collectors; @Controller public class MindmapController extends BaseController { - final Logger logger = Logger.getLogger(MindmapController.class); + final Logger logger = LogManager.getLogger(); private static final String LATEST_HISTORY_REVISION = "latest"; diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/Oauth2Controller.java b/wise-webapp/src/main/java/com/wisemapping/rest/Oauth2Controller.java new file mode 100644 index 00000000..a3381a3c --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/rest/Oauth2Controller.java @@ -0,0 +1,88 @@ +/* + * Copyright [2022] [wisemapping] + * + * Licensed under WiseMapping Public License, Version 1.0 (the "License"). + * It is basically the Apache License, Version 2.0 (the "License") plus the + * "powered by wisemapping" text requirement on every single page; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the license at + * + * http://www.wisemapping.org/license + * + * 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. + */ + +package com.wisemapping.rest; + +import com.wisemapping.exceptions.WiseMappingException; +import com.wisemapping.model.User; +import com.wisemapping.rest.model.RestOath2CallbackResponse; +import com.wisemapping.service.*; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + + +@Controller +@CrossOrigin +public class Oauth2Controller extends BaseController { + @Qualifier("userService") + @Autowired + private UserService userService; + + @Qualifier("authenticationManager") + @Autowired + private AuthenticationManager authManager; + + @Value("${google.recaptcha2.enabled}") + private Boolean recatchaEnabled; + + @Value("${accounts.exclusion.domain:''}") + private String domainBanExclusion; + + private void doLogin(HttpServletRequest request, String email) { + PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(email,null); + Authentication auth = authManager.authenticate(token); + SecurityContextHolder.getContext().setAuthentication(auth); + // update spring mvc session + HttpSession session = request.getSession(true); + session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext()); + } + + @RequestMapping(method = RequestMethod.POST, value = "/oauth2/googlecallback", produces = { "application/json" }) + @ResponseStatus(value = HttpStatus.OK) + public RestOath2CallbackResponse processGoogleCallback(@NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException { + User user = userService.createUserFromGoogle(code); + if (user.getGoogleSync() != null && user.getGoogleSync().booleanValue()) { + doLogin(request, user.getEmail()); + } + RestOath2CallbackResponse response = new RestOath2CallbackResponse(); + response.setEmail(user.getEmail()); + response.setGoogleSync(user.getGoogleSync()); + response.setSyncCode(user.getSyncCode()); + return response; + } + + @RequestMapping(method = RequestMethod.PUT, value = "/oauth2/confirmaccountsync", produces = { "application/json" }) + @ResponseStatus(value = HttpStatus.OK) + public void confirmAccountSync(@NotNull @RequestParam String email, @NotNull @RequestParam String code, @NotNull HttpServletRequest request) throws WiseMappingException { + userService.confirmAccountSync(email, code); + doLogin(request, email); + } + +} diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/UserController.java b/wise-webapp/src/main/java/com/wisemapping/rest/UserController.java index 2574ae79..234c6b09 100644 --- a/wise-webapp/src/main/java/com/wisemapping/rest/UserController.java +++ b/wise-webapp/src/main/java/com/wisemapping/rest/UserController.java @@ -22,110 +22,122 @@ import com.wisemapping.exceptions.EmailNotExistsException; import com.wisemapping.exceptions.WiseMappingException; import com.wisemapping.model.AuthenticationType; import com.wisemapping.model.User; +import com.wisemapping.rest.model.RestResetPasswordResponse; import com.wisemapping.rest.model.RestUserRegistration; import com.wisemapping.service.*; import com.wisemapping.validator.Messages; import com.wisemapping.validator.UserValidator; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.stereotype.Controller; import org.springframework.validation.BindException; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import java.util.Arrays; import java.util.List; @Controller @CrossOrigin public class UserController extends BaseController { - @Qualifier("userService") - @Autowired - private UserService userService; + @Qualifier("userService") + @Autowired + private UserService userService; - @Autowired - private RecaptchaService captchaService; + @Autowired + private RecaptchaService captchaService; - @Value("${google.recaptcha2.enabled}") - private Boolean recatchaEnabled; + @Qualifier("authenticationManager") + @Autowired + private AuthenticationManager authManager; - @Value("${accounts.exclusion.domain:''}") - private String domainBanExclusion; + @Value("${google.recaptcha2.enabled}") + private Boolean recatchaEnabled; - private static final Logger logger = Logger.getLogger(UserController.class); - private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP"; + @Value("${accounts.exclusion.domain:''}") + private String domainBanExclusion; - @RequestMapping(method = RequestMethod.POST, value = "/users", produces = {"application/json"}) - @ResponseStatus(value = HttpStatus.CREATED) - public void registerUser(@RequestBody RestUserRegistration registration, @NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws WiseMappingException, BindException { - logger.debug("Register new user:" + registration.getEmail()); + private static final Logger logger = LogManager.getLogger(); + private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP"; - // If tomcat is behind a reverse proxy, ip needs to be found in other header. - String remoteIp = request.getHeader(REAL_IP_ADDRESS_HEADER); - if (remoteIp == null || remoteIp.isEmpty()) { - remoteIp = request.getRemoteAddr(); - } - logger.debug("Remote address" + remoteIp); + @RequestMapping(method = RequestMethod.POST, value = "/users", produces = { "application/json" }) + @ResponseStatus(value = HttpStatus.CREATED) + public void registerUser(@RequestBody RestUserRegistration registration, @NotNull HttpServletRequest request, + @NotNull HttpServletResponse response) throws WiseMappingException, BindException { + logger.debug("Register new user:" + registration.getEmail()); - verify(registration, remoteIp); + // If tomcat is behind a reverse proxy, ip needs to be found in other header. + String remoteIp = request.getHeader(REAL_IP_ADDRESS_HEADER); + if (remoteIp == null || remoteIp.isEmpty()) { + remoteIp = request.getRemoteAddr(); + } + logger.debug("Remote address" + remoteIp); - final User user = new User(); - user.setEmail(registration.getEmail().trim()); - user.setFirstname(registration.getFirstname()); - user.setLastname(registration.getLastname()); - user.setPassword(registration.getPassword()); + verify(registration, remoteIp); - user.setAuthenticationType(AuthenticationType.DATABASE); - userService.createUser(user, false, true); - response.setHeader("Location", "/service/users/" + user.getId()); - } + final User user = new User(); + user.setEmail(registration.getEmail().trim()); + user.setFirstname(registration.getFirstname()); + user.setLastname(registration.getLastname()); + user.setPassword(registration.getPassword()); - @RequestMapping(method = RequestMethod.PUT, value = "/users/resetPassword", produces = {"application/json"}) - @ResponseStatus(value = HttpStatus.OK) - public void resetPassword(@RequestParam String email) throws InvalidAuthSchemaException, EmailNotExistsException { - try { - userService.resetPassword(email); - } catch (InvalidUserEmailException e) { - throw new EmailNotExistsException(e); - } - } + user.setAuthenticationType(AuthenticationType.DATABASE); + userService.createUser(user, false, true); + response.setHeader("Location", "/service/users/" + user.getId()); + } - private void verify(@NotNull final RestUserRegistration registration, @NotNull String remoteAddress) throws BindException { + @RequestMapping(method = RequestMethod.PUT, value = "/users/resetPassword", produces = { "application/json" }) + @ResponseStatus(value = HttpStatus.OK) + public RestResetPasswordResponse resetPassword(@RequestParam String email) throws InvalidAuthSchemaException, EmailNotExistsException { + try { + return userService.resetPassword(email); + } catch (InvalidUserEmailException e) { + throw new EmailNotExistsException(e); + } + } - final BindException errors = new RegistrationException(registration, "registration"); - final UserValidator validator = new UserValidator(); - validator.setUserService(userService); - validator.validate(registration, errors); + private void verify(@NotNull final RestUserRegistration registration, @NotNull String remoteAddress) + throws BindException { - // If captcha is enabled, generate it ... - if (recatchaEnabled) { - final String recaptcha = registration.getRecaptcha(); - if (recaptcha != null) { - final String reCaptchaResponse = captchaService.verifyRecaptcha(remoteAddress, recaptcha); - if (reCaptchaResponse != null && !reCaptchaResponse.isEmpty()) { - errors.rejectValue("recaptcha", reCaptchaResponse); - } - } else { - errors.rejectValue("recaptcha", Messages.CAPTCHA_LOADING_ERROR); - } - } else { - logger.warn("captchaEnabled is enabled.Recommend to enable it for production environments."); - } + final BindException errors = new RegistrationException(registration, "registration"); + final UserValidator validator = new UserValidator(); + validator.setUserService(userService); + validator.validate(registration, errors); - if (errors.hasErrors()) { - throw errors; - } + // If captcha is enabled, generate it ... + if (recatchaEnabled) { + final String recaptcha = registration.getRecaptcha(); + if (recaptcha != null) { + final String reCaptchaResponse = captchaService.verifyRecaptcha(remoteAddress, recaptcha); + if (reCaptchaResponse != null && !reCaptchaResponse.isEmpty()) { + errors.rejectValue("recaptcha", reCaptchaResponse); + } + } else { + errors.rejectValue("recaptcha", Messages.CAPTCHA_LOADING_ERROR); + } + } else { + logger.warn("captchaEnabled is enabled.Recommend to enable it for production environments."); + } - // Is excluded ?. - final List excludedDomains = Arrays.asList(domainBanExclusion.split(",")); - final String emailDomain = registration.getEmail().split("@")[1]; - if (excludedDomains.contains(emailDomain)) { - throw new IllegalArgumentException("Email is part of ban exclusion list due to abuse. Please, contact site admin if you think this is an error." + emailDomain); - } - } + if (errors.hasErrors()) { + throw errors; + } + + // Is excluded ?. + final List excludedDomains = Arrays.asList(domainBanExclusion.split(",")); + final String emailDomain = registration.getEmail().split("@")[1]; + if (excludedDomains.contains(emailDomain)) { + throw new IllegalArgumentException( + "Email is part of ban exclusion list due to abuse. Please, contact site admin if you think this is an error." + + emailDomain); + } + } } diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/model/RestOath2CallbackResponse.java b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestOath2CallbackResponse.java new file mode 100644 index 00000000..f8b040f3 --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestOath2CallbackResponse.java @@ -0,0 +1,33 @@ +package com.wisemapping.rest.model; + +public class RestOath2CallbackResponse { + + private String email; + private Boolean googleSync; + private String syncCode; + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Boolean getGoogleSync() { + return googleSync; + } + + public void setGoogleSync(Boolean googleSync) { + this.googleSync = googleSync; + } + + public String getSyncCode() { + return syncCode; + } + + public void setSyncCode(String syncCode) { + this.syncCode = syncCode; + } + +} diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordAction.java b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordAction.java new file mode 100644 index 00000000..b5467616 --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordAction.java @@ -0,0 +1,7 @@ +package com.wisemapping.rest.model; + +public enum RestResetPasswordAction { + + EMAIL_SENT, OAUTH2_USER + +} diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordResponse.java b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordResponse.java new file mode 100644 index 00000000..38e68cc1 --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestResetPasswordResponse.java @@ -0,0 +1,15 @@ +package com.wisemapping.rest.model; + +public class RestResetPasswordResponse { + + RestResetPasswordAction action; + + public RestResetPasswordAction getAction() { + return action; + } + + public void setAction(RestResetPasswordAction action) { + this.action = action; + } + +} diff --git a/wise-webapp/src/main/java/com/wisemapping/rest/model/RestUser.java b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestUser.java index 949d3aea..30766566 100644 --- a/wise-webapp/src/main/java/com/wisemapping/rest/model/RestUser.java +++ b/wise-webapp/src/main/java/com/wisemapping/rest/model/RestUser.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +import com.wisemapping.model.AuthenticationType; import com.wisemapping.model.User; import org.jetbrains.annotations.NotNull; @@ -102,6 +103,10 @@ public class RestUser { return this.user; } + public AuthenticationType getAuthenticationType() { + return user.getAuthenticationType(); + } + @Override public boolean equals(Object o) { if (!(o instanceof RestUser)) { diff --git a/wise-webapp/src/main/java/com/wisemapping/security/GoogleAuthenticationProvider.java b/wise-webapp/src/main/java/com/wisemapping/security/GoogleAuthenticationProvider.java new file mode 100644 index 00000000..89e94077 --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/security/GoogleAuthenticationProvider.java @@ -0,0 +1,62 @@ +package com.wisemapping.security; + +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; + +import com.wisemapping.model.User; + +public class GoogleAuthenticationProvider implements org.springframework.security.authentication.AuthenticationProvider { + + private UserDetailsService userDetailsService; + + public UserDetailsService getUserDetailsService() { + return userDetailsService; + } + + public void setUserDetailsService(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + /** + * Authenticate the given PreAuthenticatedAuthenticationToken. + * + * If the principal contained in the authentication object is null, the request will + * be ignored to allow other providers to authenticate it. + */ + @Override + public Authentication authenticate(Authentication inputToken) throws AuthenticationException { + if (!supports(inputToken.getClass())) { + return null; + } + if (inputToken.getPrincipal() == null) { + throw new BadCredentialsException("No pre-authenticated principal found in request."); + } + UserDetails userDetails = userDetailsService.loadUserByUsername(inputToken.getName()); + final User user = userDetails.getUser(); + + if (!user.isActive()) { + throw new BadCredentialsException("User has been disabled for login " + inputToken.getName()); + } + + PreAuthenticatedAuthenticationToken resultToken = new PreAuthenticatedAuthenticationToken(userDetails, + inputToken.getCredentials(), userDetails.getAuthorities()); + resultToken.setDetails(userDetails); + + userDetailsService.getUserService().auditLogin(user); + + return resultToken; + } + + /** + * Indicate that this provider only supports PreAuthenticatedAuthenticationToken + * (sub)classes. + */ + @Override + public final boolean supports(Class authentication) { + return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication); + } + + +} diff --git a/wise-webapp/src/main/java/com/wisemapping/security/LegacyPasswordEncoder.java b/wise-webapp/src/main/java/com/wisemapping/security/LegacyPasswordEncoder.java index eb89d119..45ac1c39 100755 --- a/wise-webapp/src/main/java/com/wisemapping/security/LegacyPasswordEncoder.java +++ b/wise-webapp/src/main/java/com/wisemapping/security/LegacyPasswordEncoder.java @@ -18,7 +18,9 @@ package com.wisemapping.security; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import org.springframework.security.crypto.codec.Base64; import org.springframework.security.crypto.codec.Hex; import org.springframework.security.crypto.codec.Utf8; @@ -29,7 +31,7 @@ import java.security.NoSuchAlgorithmException; public class LegacyPasswordEncoder implements PasswordEncoder { - final private static Logger logger = Logger.getLogger(LegacyPasswordEncoder.class); + final private static Logger logger = LogManager.getLogger(); public static final String ENC_PREFIX = "ENC:"; private final ShaPasswordEncoder sha1Encoder = new ShaPasswordEncoder(); diff --git a/wise-webapp/src/main/java/com/wisemapping/security/UserDetailsService.java b/wise-webapp/src/main/java/com/wisemapping/security/UserDetailsService.java index 4a32bf35..0d86c946 100644 --- a/wise-webapp/src/main/java/com/wisemapping/security/UserDetailsService.java +++ b/wise-webapp/src/main/java/com/wisemapping/security/UserDetailsService.java @@ -23,7 +23,6 @@ import com.wisemapping.model.User; import com.wisemapping.service.UserService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DataAccessException; import org.springframework.security.core.userdetails.UsernameNotFoundException; diff --git a/wise-webapp/src/main/java/com/wisemapping/security/aop/ViewBaseSecurityAdvise.java b/wise-webapp/src/main/java/com/wisemapping/security/aop/ViewBaseSecurityAdvise.java index c9807d61..52c59944 100755 --- a/wise-webapp/src/main/java/com/wisemapping/security/aop/ViewBaseSecurityAdvise.java +++ b/wise-webapp/src/main/java/com/wisemapping/security/aop/ViewBaseSecurityAdvise.java @@ -38,7 +38,6 @@ public class ViewBaseSecurityAdvise @Override protected boolean isAllowed(@Nullable User user, Mindmap map) { - System.out.println("VIEWWWWWWWWWWWWW"); return getMindmapService().hasPermissions(user, map, CollaborationRole.VIEWER); } diff --git a/wise-webapp/src/main/java/com/wisemapping/service/LockManagerImpl.java b/wise-webapp/src/main/java/com/wisemapping/service/LockManagerImpl.java index b7247559..526ec36f 100644 --- a/wise-webapp/src/main/java/com/wisemapping/service/LockManagerImpl.java +++ b/wise-webapp/src/main/java/com/wisemapping/service/LockManagerImpl.java @@ -23,18 +23,21 @@ import com.wisemapping.exceptions.LockException; import com.wisemapping.model.CollaborationRole; import com.wisemapping.model.Mindmap; import com.wisemapping.model.User; -import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -import java.util.*; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; class LockManagerImpl implements LockManager { private static final int ONE_MINUTE_MILLISECONDS = 1000 * 60; private final Map lockInfoByMapId; private final static Timer expirationTimer = new Timer(); - final private static Logger logger = Logger.getLogger(LockManagerImpl.class); + final private static Logger logger = LogManager.getLogger(); @Override public boolean isLocked(@NotNull Mindmap mindmap) { diff --git a/wise-webapp/src/main/java/com/wisemapping/service/MindmapServiceImpl.java b/wise-webapp/src/main/java/com/wisemapping/service/MindmapServiceImpl.java index 8207a662..7a4a763f 100755 --- a/wise-webapp/src/main/java/com/wisemapping/service/MindmapServiceImpl.java +++ b/wise-webapp/src/main/java/com/wisemapping/service/MindmapServiceImpl.java @@ -143,15 +143,13 @@ public class MindmapServiceImpl public void removeCollaboration(@NotNull Mindmap mindmap, @NotNull Collaboration collaboration) throws CollaborationException { // remove collaborator association final Mindmap mindMap = collaboration.getMindMap(); - final Set collaborations = mindMap.getCollaborations(); - final User creator = mindMap.getCreator(); if (creator.identityEquality(collaboration.getCollaborator())) { throw new CollaborationException("User is the creator and must have ownership permissions.Creator Email:" + mindMap.getCreator().getEmail() + ",Collaborator:" + collaboration.getCollaborator().getEmail()); } // When you delete an object from hibernate you have to delete it from *all* collections it exists in... - collaborations.remove(collaboration); + mindMap.removedCollaboration(collaboration); mindmapManager.removeCollaboration(collaboration); } @@ -249,7 +247,7 @@ public class MindmapServiceImpl @Override public void revertChange(@NotNull Mindmap mindmap, int historyId) - throws WiseMappingException, IOException { + throws WiseMappingException { final MindMapHistory history = mindmapManager.getHistory(historyId); mindmap.setZippedXml(history.getZippedXml()); updateMindmap(mindmap, true); diff --git a/wise-webapp/src/main/java/com/wisemapping/service/RecaptchaService.java b/wise-webapp/src/main/java/com/wisemapping/service/RecaptchaService.java index 2064734d..e947090f 100644 --- a/wise-webapp/src/main/java/com/wisemapping/service/RecaptchaService.java +++ b/wise-webapp/src/main/java/com/wisemapping/service/RecaptchaService.java @@ -17,13 +17,15 @@ */ package com.wisemapping.service; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import com.fasterxml.jackson.databind.ObjectMapper; import com.wisemapping.validator.Messages; import org.apache.commons.lang.StringUtils; import org.apache.http.NameValuePair; import org.apache.http.client.fluent.Form; import org.apache.http.client.fluent.Request; -import org.apache.log4j.Logger; import org.jetbrains.annotations.Nullable; import javax.validation.constraints.NotNull; @@ -35,7 +37,7 @@ import java.util.Map; public class RecaptchaService { - final private static Logger logger = Logger.getLogger(RecaptchaService.class); + final private static Logger logger = LogManager.getLogger(); final private static String GOOGLE_RECAPTCHA_VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify"; diff --git a/wise-webapp/src/main/java/com/wisemapping/service/UserService.java b/wise-webapp/src/main/java/com/wisemapping/service/UserService.java index ef0cf222..6fbda735 100755 --- a/wise-webapp/src/main/java/com/wisemapping/service/UserService.java +++ b/wise-webapp/src/main/java/com/wisemapping/service/UserService.java @@ -20,6 +20,8 @@ package com.wisemapping.service; import com.wisemapping.exceptions.WiseMappingException; import com.wisemapping.model.User; +import com.wisemapping.rest.model.RestResetPasswordResponse; + import org.jetbrains.annotations.NotNull; public interface UserService { @@ -28,6 +30,10 @@ public interface UserService { User createUser(@NotNull User user, boolean emailConfirmEnabled, boolean welcomeEmail) throws WiseMappingException; + User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException; + + User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException; + void changePassword(@NotNull User user); User getUserBy(String email); @@ -36,7 +42,7 @@ public interface UserService { void updateUser(User user); - void resetPassword(@NotNull String email) throws InvalidUserEmailException, InvalidAuthSchemaException; + RestResetPasswordResponse resetPassword(@NotNull String email) throws InvalidUserEmailException, InvalidAuthSchemaException; void removeUser(@NotNull User user); diff --git a/wise-webapp/src/main/java/com/wisemapping/service/UserServiceImpl.java b/wise-webapp/src/main/java/com/wisemapping/service/UserServiceImpl.java index 07866c1e..a3e78721 100755 --- a/wise-webapp/src/main/java/com/wisemapping/service/UserServiceImpl.java +++ b/wise-webapp/src/main/java/com/wisemapping/service/UserServiceImpl.java @@ -23,6 +23,10 @@ import com.wisemapping.exceptions.InvalidMindmapException; import com.wisemapping.exceptions.WiseMappingException; import com.wisemapping.mail.NotificationService; import com.wisemapping.model.*; +import com.wisemapping.rest.model.RestResetPasswordAction; +import com.wisemapping.rest.model.RestResetPasswordResponse; +import com.wisemapping.service.google.GoogleAccountBasicData; +import com.wisemapping.service.google.GoogleService; import com.wisemapping.util.VelocityEngineUtils; import com.wisemapping.util.VelocityEngineWrapper; import org.jetbrains.annotations.NotNull; @@ -39,7 +43,7 @@ public class UserServiceImpl private NotificationService notificationService; private MessageSource messageSource; private VelocityEngineWrapper velocityEngineWrapper; - + private GoogleService googleService; @Override public void activateAccount(long code) @@ -56,10 +60,15 @@ public class UserServiceImpl } @Override - public void resetPassword(@NotNull String email) + public RestResetPasswordResponse resetPassword(@NotNull String email) throws InvalidUserEmailException, InvalidAuthSchemaException { final User user = userManager.getUserBy(email); if (user != null) { + RestResetPasswordResponse response = new RestResetPasswordResponse(); + if (user.getAuthenticationType().equals(AuthenticationType.GOOGLE_OAUTH2)) { + response.setAction(RestResetPasswordAction.OAUTH2_USER); + return response; + } if (user.getAuthenticationType() != AuthenticationType.DATABASE) { throw new InvalidAuthSchemaException("Could not change password for " + user.getAuthenticationType().getCode()); @@ -72,6 +81,9 @@ public class UserServiceImpl // Send an email with the new temporal password ... notificationService.resetPassword(user, password); + + response.setAction(RestResetPasswordAction.EMAIL_SENT); + return response; } else { throw new InvalidUserEmailException("The email '" + email + "' does not exists."); } @@ -147,6 +159,55 @@ public class UserServiceImpl return user; } + @NotNull + public User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException { + try { + GoogleAccountBasicData data = googleService.processCallback(callbackCode); + User existingUser = userManager.getUserBy(data.getEmail()); + if (existingUser == null) { + User newUser = new User(); + // new registrations from google starts synched + newUser.setGoogleSync(true); + newUser.setEmail(data.getEmail()); + newUser.setFirstname(data.getName()); + newUser.setLastname(data.getLastName()); + newUser.setAuthenticationType(AuthenticationType.GOOGLE_OAUTH2); + newUser.setGoogleToken(data.getAccessToken()); + existingUser = this.createUser(newUser, false, true); + } else { + // user exists and doesnt have confirmed account linking, I must wait for confirmation + if (existingUser.getGoogleSync() == null) { + existingUser.setGoogleSync(false); + existingUser.setSyncCode(callbackCode); + existingUser.setGoogleToken(data.getAccessToken()); + userManager.updateUser(existingUser); + } + + } + return existingUser; + } catch (Exception e) { + throw new WiseMappingException("Cant create user", e); + } + } + + public User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException { + User existingUser = userManager.getUserBy(email); + // additional security check + if (existingUser == null || !existingUser.getSyncCode().equals(code)) { + throw new WiseMappingException("User not found / incorrect code"); + } + existingUser.setGoogleSync(true); + existingUser.setSyncCode(null); + // user will not be able to login again with usr/pwd schema + existingUser.setAuthenticationType(AuthenticationType.GOOGLE_OAUTH2); + existingUser.setPassword(""); + userManager.updateUser(existingUser); + + return existingUser; + } + + + public Mindmap buildTutorialMindmap(@NotNull String firstName) throws InvalidMindmapException { //To change body of created methods use File | Settings | File Templates. final Locale locale = LocaleContextHolder.getLocale(); @@ -209,7 +270,11 @@ public class UserServiceImpl this.velocityEngineWrapper = velocityEngineWrapper; } - @Override + public void setGoogleService(GoogleService googleService) { + this.googleService = googleService; + } + + @Override public User getCasUserBy(String uid) { // TODO Auto-generated method stub return null; diff --git a/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleAccountBasicData.java b/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleAccountBasicData.java new file mode 100644 index 00000000..f8f43f76 --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleAccountBasicData.java @@ -0,0 +1,66 @@ +package com.wisemapping.service.google; + +public class GoogleAccountBasicData { + + private String email; + private String accountId; + private String name; + private String lastName; + private String accessToken; + private String refreshToken; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + @Override + public String toString() { + return "GoogleAccountBasicData [email=" + email + ", accountId=" + accountId + ", name=" + name + ", lastName=" + + lastName + ", accessToken=" + accessToken + ", refreshToken=" + refreshToken + "]"; + } + +} diff --git a/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleService.java b/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleService.java new file mode 100644 index 00000000..2a4e8f6d --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/service/google/GoogleService.java @@ -0,0 +1,106 @@ +package com.wisemapping.service.google; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.wisemapping.service.http.HttpInvoker; +import com.wisemapping.service.http.HttpInvokerContentType; +import com.wisemapping.service.http.HttpInvokerException; +import com.wisemapping.service.http.HttpMethod; + +@Service +public class GoogleService { + private HttpInvoker httpInvoker; + private String optinConfirmUrl; + private String accountBasicDataUrl; + private String clientId; + private String clientSecret; + private String callbackUrl; + + public void setHttpInvoker(HttpInvoker httpInvoker) { + this.httpInvoker = httpInvoker; + } + + public void setOptinConfirmUrl(String optinConfirmUrl) { + this.optinConfirmUrl = optinConfirmUrl; + } + + public void setAccountBasicDataUrl(String accountBasicDataUrl) { + this.accountBasicDataUrl = accountBasicDataUrl; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public void setCallbackUrl(String callbackUrl) { + this.callbackUrl = callbackUrl; + } + + private String getNodeAsString(JsonNode node, String fieldName) { + return getNodeAsString(node, fieldName, null); + } + + private String getNodeAsString(JsonNode node, String fieldName, String defaultValue) { + JsonNode subNode = node.get(fieldName); + return subNode != null ? subNode.asText() : defaultValue; + } + + private Map getHeaders(String token) { + Map headers = new HashMap(); + headers.put("Content-type", "application/json"); + headers.put("Authorization", "Bearer " + token); + return headers; + } + + private GoogleAccountBasicData getAccountBasicData(String token) throws HttpInvokerException { + JsonNode response = httpInvoker.invoke(accountBasicDataUrl, null, HttpMethod.GET, this.getHeaders(token), null, + null); + GoogleAccountBasicData data = new GoogleAccountBasicData(); + data.setEmail(getNodeAsString(response, "email")); + data.setAccountId(getNodeAsString(response, "id")); + data.setName(getNodeAsString(response, "given_name", data.getEmail())); + data.setLastName(getNodeAsString(response, "family_name")); + return data; + } + + private Map getOptinConfirmBody(String code) { + Map result = new HashMap(); + result.put("client_id", clientId); + result.put("client_secret", clientSecret); + result.put("code", code); + result.put("redirect_uri", callbackUrl); + result.put("grant_type", "authorization_code"); + return result; + } + + public GoogleAccountBasicData processCallback(String code) + throws HttpInvokerException, JsonMappingException, JsonProcessingException { + Map body = this.getOptinConfirmBody(code); + JsonNode optinConfirmResponse = httpInvoker.invoke( + optinConfirmUrl, + HttpInvokerContentType.FORM_ENCODED, + HttpMethod.POST, + null, + null, + body); + + String accessToken = getNodeAsString(optinConfirmResponse, "access_token"); + String refreshToken = getNodeAsString(optinConfirmResponse, "refresh_token"); + + GoogleAccountBasicData data = this.getAccountBasicData(accessToken); + data.setAccessToken(accessToken); + data.setRefreshToken(refreshToken); + return data; + } + +} \ No newline at end of file diff --git a/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvoker.java b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvoker.java new file mode 100644 index 00000000..be05d8a4 --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvoker.java @@ -0,0 +1,147 @@ +package com.wisemapping.service.http; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Service +public class HttpInvoker { + + protected static Logger logger = LogManager.getLogger(HttpInvoker.class); + + private ObjectMapper mapper = new ObjectMapper(); + + public HttpInvoker() { + super(); + } + + public JsonNode invoke( + String url, + HttpInvokerContentType requestContentType, + HttpMethod method, + Map headers, + String jsonPayload, + Map formData) + throws HttpInvokerException { + String responseBody = null; + try { + if (logger.isDebugEnabled()) { + logger.debug("finalUrl: " + url); + logger.debug("method: " + method); + logger.debug("payload: " + jsonPayload); + logger.debug("header: " + headers); + } + + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpRequestBase httpRequst = null; + + // build request + if (method.equals(HttpMethod.POST)) + httpRequst = new HttpPost(url); + else if (method.equals(HttpMethod.PUT)) + httpRequst = new HttpPut(url); + else if (method.equals(HttpMethod.GET)) + httpRequst = new HttpGet(url); + else if (method.equals(HttpMethod.DELETE)) + httpRequst = new HttpDelete(url); + else + throw new HttpInvokerException("Method " + method + " not suppoprted by http connector"); + + if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) { + HttpEntity entity = null; + if (requestContentType.equals(HttpInvokerContentType.JSON)) { + if (jsonPayload == null) + throw new HttpInvokerException("Json content is required"); + entity = new StringEntity(jsonPayload, Charset.forName("UTF-8")); + ((HttpEntityEnclosingRequestBase) httpRequst).setEntity(entity); + } + if (requestContentType.equals(HttpInvokerContentType.FORM_ENCODED)) { + List nameValuePairs = new ArrayList(); + Set keys = formData.keySet(); + for (String key : keys) { + nameValuePairs.add(new BasicNameValuePair(key, formData.get(key).toString())); + } + entity = new UrlEncodedFormEntity(nameValuePairs); + ((HttpEntityEnclosingRequestBase) httpRequst).setEntity(entity); + } + if (entity == null) + throw new HttpInvokerException("Cant build entity to send"); + } + + if (headers != null) { + Set keys = headers.keySet(); + for (String key : keys) { + httpRequst.setHeader(key, headers.get(key)); + } + } + + if (requestContentType != null) + httpRequst.setHeader("Content-Type", requestContentType.getHttpContentType()); + + // invoke + CloseableHttpResponse response = httpClient.execute(httpRequst); + // response process + JsonNode root = null; + responseBody = response.getEntity() != null && response.getEntity().getContent() != null + ? IOUtils.toString(response.getEntity().getContent(), (String) null) + : null; + if (responseBody != null) { + if (logger.isDebugEnabled()) { + logger.debug("response plain: " + responseBody); + } + try { + root = mapper.readTree(responseBody); + } catch (Exception e) { + int returnCode = response.getStatusLine().getStatusCode(); + throw new HttpInvokerException("cant transform response to JSON. RQ: " + jsonPayload + ", RS: " + + responseBody + ", status: " + returnCode, e); + } + } + + if (response.getStatusLine().getStatusCode() >= 400) { + logger.error("error response: " + responseBody); + throw new HttpInvokerException("error invoking " + url + ", response: " + responseBody + ", status: " + + response.getStatusLine().getStatusCode()); + } + + httpRequst.releaseConnection(); + response.close(); + httpClient.close(); + + return root; + } catch (HttpInvokerException e) { + throw e; + } catch (Exception e) { + logger.error("cant invoke service " + url); + logger.error("response: " + responseBody, e); + throw new HttpInvokerException("cant invoke service " + url, e); + } + } + + + +} diff --git a/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerContentType.java b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerContentType.java new file mode 100644 index 00000000..e80da507 --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerContentType.java @@ -0,0 +1,18 @@ +package com.wisemapping.service.http; + +public enum HttpInvokerContentType { + + JSON("application/json"), + FORM_ENCODED("application/x-www-form-urlencoded"); + + private String httpContentType; + + private HttpInvokerContentType(String type) { + this.httpContentType = type; + } + + public String getHttpContentType() { + return httpContentType; + } + +} \ No newline at end of file diff --git a/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerException.java b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerException.java new file mode 100644 index 00000000..8ee37835 --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpInvokerException.java @@ -0,0 +1,13 @@ +package com.wisemapping.service.http; + +public class HttpInvokerException extends Exception { + + public HttpInvokerException(String message) { + super(message); + } + + public HttpInvokerException(String message, Throwable cause) { + super(message, cause); + } + +} \ No newline at end of file diff --git a/wise-webapp/src/main/java/com/wisemapping/service/http/HttpMethod.java b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpMethod.java new file mode 100644 index 00000000..42553bfa --- /dev/null +++ b/wise-webapp/src/main/java/com/wisemapping/service/http/HttpMethod.java @@ -0,0 +1,5 @@ +package com.wisemapping.service.http; + +public enum HttpMethod { + POST, GET, DELETE, PUT +} diff --git a/wise-webapp/src/main/java/com/wisemapping/webmvc/UsersController.java b/wise-webapp/src/main/java/com/wisemapping/webmvc/UsersController.java index 2db75cd5..e760c017 100644 --- a/wise-webapp/src/main/java/com/wisemapping/webmvc/UsersController.java +++ b/wise-webapp/src/main/java/com/wisemapping/webmvc/UsersController.java @@ -39,6 +39,11 @@ public class UsersController { return new ModelAndView("forgot-password"); } + @RequestMapping(value = "registration-google", method = RequestMethod.GET) + public ModelAndView processGoogleCallback() { + return new ModelAndView("registration-google"); + } + @RequestMapping(value = "registration", method = RequestMethod.GET) public ModelAndView showRegistrationPage() { return new ModelAndView("registration"); diff --git a/wise-webapp/src/main/resources/messages_de.properties b/wise-webapp/src/main/resources/messages_de.properties index 8df1841d..c6819fae 100644 --- a/wise-webapp/src/main/resources/messages_de.properties +++ b/wise-webapp/src/main/resources/messages_de.properties @@ -67,4 +67,7 @@ TOO_BIG_MINDMAP=Sie haben das Limit von 5000 Themen in einer Mindmap erreicht. SHARE_MAP.EMAIL_SUBJECT={0} hat eine Mindmap mit Ihnen geteilt EMAIL.DO_NOT_REPLAY=Wichtig: Antworten Sie nicht auf diese E-Mail. Wenn Sie weitere Hilfe benötigen oder Bedenken bezüglich Ihres Kontos haben, kontaktieren Sie uns hier. EMAIL.GREETINGS=Hallo -OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. \ No newline at end of file +OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. +ZOOM_TO_FIT=Zum Beheben zoomen +ZOOM_OUT=Rauszoomen +ZOOM_IN=Hineinzoomen \ No newline at end of file diff --git a/wise-webapp/src/main/resources/messages_en.properties b/wise-webapp/src/main/resources/messages_en.properties index 9faf64fc..12008527 100644 --- a/wise-webapp/src/main/resources/messages_en.properties +++ b/wise-webapp/src/main/resources/messages_en.properties @@ -69,4 +69,7 @@ SHARE_MAP.EMAIL_SUBJECT={0} has shared a mind map with you EMAIL.DO_NOT_REPLAY=Important: Do not reply this email. If you need further help or have any concerns regarding your account, contact us to here. EMAIL.GREETINGS=Hi TOO_MANY_INACTIVE_ACCOUNTS=You have shared your mindmaps to more than 20 user that have not registered yet. Please, remove inactive accounts or ask them to register. -OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. \ No newline at end of file +OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. +ZOOM_TO_FIT=Zoom to fit +ZOOM_OUT=Zoom out +ZOOM_IN=Zoom in \ No newline at end of file diff --git a/wise-webapp/src/main/resources/messages_es.properties b/wise-webapp/src/main/resources/messages_es.properties index 5709f524..bdf231d2 100644 --- a/wise-webapp/src/main/resources/messages_es.properties +++ b/wise-webapp/src/main/resources/messages_es.properties @@ -67,4 +67,7 @@ PASSWORD_CHANGED.EMAIL_BODY=

Esto es solo una notificación de que su contrase SHARE_MAP.EMAIL_SUBJECT={0} te ha compartido un mapa mental EMAIL.DO_NOT_REPLAY=Importante: No responda este correo electrónico. Si necesita más ayuda o tiene alguna inquietud con respecto a su cuenta, comuníquese con nosotros a aquí. EMAIL.GREETINGS=Hola -OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. \ No newline at end of file +OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. +ZOOM_TO_FIT=Centrar +ZOOM_OUT=Alejar +ZOOM_IN=Acercar \ No newline at end of file diff --git a/wise-webapp/src/main/resources/messages_fr.properties b/wise-webapp/src/main/resources/messages_fr.properties index 417e99cb..7ce438b6 100644 --- a/wise-webapp/src/main/resources/messages_fr.properties +++ b/wise-webapp/src/main/resources/messages_fr.properties @@ -67,4 +67,7 @@ TOO_BIG_MINDMAP=Vous avez atteint la limite de 5000 sujets dans une carte mental SHARE_MAP.EMAIL_SUBJECT={0} a partagé une carte mentale avec vous EMAIL.DO_NOT_REPLAY=Important : Ne répondez pas à cet e-mail. Si vous avez besoin d'aide supplémentaire ou si vous avez des inquiétudes concernant votre compte, contactez-nous ici. EMAIL.GREETINGS=Salut -OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. \ No newline at end of file +OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. +ZOOM_TO_FIT=Zoomer pour s'adapter +ZOOM_OUT=Dézoomer +ZOOM_IN=Agrandir \ No newline at end of file diff --git a/wise-webapp/src/main/webapp/WEB-INF/app.properties b/wise-webapp/src/main/webapp/WEB-INF/app.properties index 3cbab059..82720249 100755 --- a/wise-webapp/src/main/webapp/WEB-INF/app.properties +++ b/wise-webapp/src/main/webapp/WEB-INF/app.properties @@ -134,11 +134,29 @@ security.ldap.auth.attribute=mail security.ldap.lastName.attribute=sn security.ldap.firstName.attribute=givenName +####################################################################################### +# Google OAuth Authentication +####################################################################################### +# OAuth Client id +#security.oauth2.google.clientId= +# OAuth Client secret +#security.oauth2.google.clientSecret= +# Redirect to this url, this url must be configured in the google app {baseurl}/c/registration-google +#security.oauth2.google.callbackUrl= + +# Google service for finish registration process, ie. exchange temporal code for user token +security.oauth2.google.confirmUrl=https://oauth2.googleapis.com/token +# Google service for get user data (name, email, etc) +security.oauth2.google.userinfoUrl=https://www.googleapis.com/oauth2/v3/userinfo +# Url for starting auth process with google +security.oauth2.google.url=https://accounts.google.com/o/oauth2/v2/auth?redirect_uri=${security.oauth2.google.callbackUrl}&prompt=consent&response_type=code&client_id=${security.oauth2.google.clientId}&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&access_type=offline&state=wisemapping&include_granted_scopes=true + + + + +####################################################################################### # User Account filtering policies +####################################################################################### # Coma separated list of domains and emails ban #accounts.exclusion.domain= - - - - diff --git a/wise-webapp/src/main/webapp/WEB-INF/defs/definitions.xml b/wise-webapp/src/main/webapp/WEB-INF/defs/definitions.xml index 805880c2..0277589b 100644 --- a/wise-webapp/src/main/webapp/WEB-INF/defs/definitions.xml +++ b/wise-webapp/src/main/webapp/WEB-INF/defs/definitions.xml @@ -8,6 +8,7 @@ + diff --git a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-db.xml b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-db.xml index 4d63d585..695f7b62 100644 --- a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-db.xml +++ b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-db.xml @@ -11,7 +11,8 @@ - + + @@ -19,4 +20,7 @@ + + + \ No newline at end of file diff --git a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-ldap.xml b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-ldap.xml index 297e8432..12691441 100644 --- a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-ldap.xml +++ b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security-ldap.xml @@ -7,6 +7,7 @@ http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> + diff --git a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security.xml b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security.xml index 5f23acc0..53b5b092 100644 --- a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security.xml +++ b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-security.xml @@ -3,12 +3,12 @@ - + @@ -34,6 +34,9 @@ + + + @@ -47,6 +50,7 @@ + diff --git a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-service.xml b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-service.xml index bedf0bdd..41d18444 100755 --- a/wise-webapp/src/main/webapp/WEB-INF/wisemapping-service.xml +++ b/wise-webapp/src/main/webapp/WEB-INF/wisemapping-service.xml @@ -18,12 +18,25 @@ + + + + + + + + + + + + + diff --git a/wise-webapp/src/main/webapp/css/viewonly.css b/wise-webapp/src/main/webapp/css/viewonly.css index bd9ef513..d879940b 100644 --- a/wise-webapp/src/main/webapp/css/viewonly.css +++ b/wise-webapp/src/main/webapp/css/viewonly.css @@ -3,7 +3,7 @@ /********************************************************************************/ body { margin: 0; - font-family:Arial; + font-family:Montserrat; } div#mindplot { diff --git a/wise-webapp/src/main/webapp/jsp/init.jsp b/wise-webapp/src/main/webapp/jsp/init.jsp index faf48a0e..ff6cef0a 100644 --- a/wise-webapp/src/main/webapp/jsp/init.jsp +++ b/wise-webapp/src/main/webapp/jsp/init.jsp @@ -2,6 +2,8 @@ <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/functions" prefix = "fn" %> + <% request.setAttribute("principal", com.wisemapping.security.Utils.getUser()); %> diff --git a/wise-webapp/src/main/webapp/jsp/mindmapEditor.jsp b/wise-webapp/src/main/webapp/jsp/mindmapEditor.jsp index e56e97b2..3e6fa9c6 100644 --- a/wise-webapp/src/main/webapp/jsp/mindmapEditor.jsp +++ b/wise-webapp/src/main/webapp/jsp/mindmapEditor.jsp @@ -9,15 +9,13 @@ <%--@elvariable id="lockInfo" type="com.wisemapping.service.LockInfo"--%> - + - - - + <%@ include file="/jsp/pageHeaders.jsf" %> Loading ... | WiseMapping @@ -47,8 +45,8 @@

- - + + diff --git a/wise-webapp/src/main/webapp/jsp/mindmapViewonly.jsp b/wise-webapp/src/main/webapp/jsp/mindmapViewonly.jsp index 0a0ade84..588ce612 100644 --- a/wise-webapp/src/main/webapp/jsp/mindmapViewonly.jsp +++ b/wise-webapp/src/main/webapp/jsp/mindmapViewonly.jsp @@ -5,17 +5,16 @@ - + - - + + ${mindmap.title} | <spring:message code="SITE.TITLE"/> - <%@ include file="/jsp/pageHeaders.jsf" %> - - - - -