Merge branch 'develop'

This commit is contained in:
Paulo Gustavo Veiga 2023-01-02 19:44:54 -08:00
commit 41ea26e0a3
63 changed files with 1132 additions and 300 deletions

View File

@ -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 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) 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 ## Members
### Founders ### Founders

View File

@ -15,6 +15,9 @@ CREATE TABLE USER (
activation_date DATE, activation_date DATE,
allow_send_email CHAR(1) NOT NULL, allow_send_email CHAR(1) NOT NULL,
locale VARCHAR(5), locale VARCHAR(5),
google_sync BOOLEAN,
sync_code VARCHAR(255),
google_token VARCHAR(255),
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id) FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
); );

View File

@ -25,6 +25,9 @@ CREATE TABLE USER (
activation_date DATE, activation_date DATE,
allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0, allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0,
locale VARCHAR(5), locale VARCHAR(5),
google_sync BOOL,
sync_code VARCHAR(255),
google_token VARCHAR(255),
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id) FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
ON DELETE CASCADE ON DELETE CASCADE
ON UPDATE NO ACTION ON UPDATE NO ACTION

View File

@ -15,6 +15,9 @@ CREATE TABLE "user" (
activation_date DATE, activation_date DATE,
allow_send_email TEXT NOT NULL DEFAULT 0, allow_send_email TEXT NOT NULL DEFAULT 0,
locale VARCHAR(5), 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 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) --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 ( CREATE TABLE MINDMAP (
id SERIAL NOT NULL PRIMARY KEY, id SERIAL NOT NULL PRIMARY KEY,
title VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL,
@ -44,11 +39,17 @@ CREATE TABLE MINDMAP (
creation_date TIMESTAMP, creation_date TIMESTAMP,
edition_date TIMESTAMP, edition_date TIMESTAMP,
creator_id INTEGER NOT NULL, creator_id INTEGER NOT NULL,
tags VARCHAR(1014),
last_editor_id INTEGER NOT NULL --, last_editor_id INTEGER NOT NULL --,
--FOREIGN KEY(creator_id) REFERENCES "USER"(colaborator_id) ON DELETE CASCADE ON UPDATE NO ACTION --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 CREATE TABLE MINDMAP_HISTORY
(id SERIAL NOT NULL PRIMARY KEY, (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 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 ( CREATE TABLE ACCESS_AUDITORY (
id SERIAL NOT NULL PRIMARY KEY, id SERIAL NOT NULL PRIMARY KEY,
login_date DATE, login_date DATE,

View File

@ -2,7 +2,7 @@
# Based on ubuntu:latest, installs WiseMapping (http://ww.wisemapping.org) # Based on ubuntu:latest, installs WiseMapping (http://ww.wisemapping.org)
# Based info setup ... # Based info setup ...
FROM tomcat:9.0-jdk17-openjdk FROM tomcat:jdk17
LABEL maintainer="Paulo Gustavo Veiga <pveiga@wisemapping.com>" LABEL maintainer="Paulo Gustavo Veiga <pveiga@wisemapping.com>"
# Build variables ... # Build variables ...
@ -31,6 +31,9 @@ RUN sed -i 's|\
</Host>|' \ </Host>|' \
/usr/local/tomcat/conf/server.xml /usr/local/tomcat/conf/server.xml
RUN sed -i 's|<Context>|<Context>\
<Loader jakartaConverter="TOMCAT" />|' \
/usr/local/tomcat/conf/context.xml
# Copy default HSQL DB for testing ... # Copy default HSQL DB for testing ...
RUN mkdir -p ${DB_BASE_DIR}/db RUN mkdir -p ${DB_BASE_DIR}/db
COPY db/ ${DB_BASE_DIR}/db COPY db/ ${DB_BASE_DIR}/db

View File

@ -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. The next step is configure the WiseMapping for the database and credentials.
Download `app.properties` configuration file and configure the required sections: 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 ### Starting the application

View File

@ -25,6 +25,9 @@ CREATE TABLE USER (
activation_date DATE, activation_date DATE,
allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0, allow_send_email CHAR(1) CHARACTER SET utf8 NOT NULL DEFAULT 0,
locale VARCHAR(5), locale VARCHAR(5),
google_sync BOOL,
sync_code VARCHAR(255),
google_token VARCHAR(255),
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id) FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id)
ON DELETE CASCADE ON DELETE CASCADE
ON UPDATE NO ACTION ON UPDATE NO ACTION
@ -113,17 +116,6 @@ CREATE TABLE COLLABORATION (
) )
CHARACTER SET utf8; 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 ( CREATE TABLE ACCESS_AUDITORY (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
login_date DATE, login_date DATE,

View File

@ -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;

View File

@ -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;

View File

@ -4,7 +4,7 @@
http://maven.apache.org/xsd/maven-4.0.0.xsd"> http://maven.apache.org/xsd/maven-4.0.0.xsd">
<properties> <properties>
<com.wisemapping.version>5.0.14</com.wisemapping.version> <com.wisemapping.version>5.0.19</com.wisemapping.version>
<superpom.dir>${project.basedir}/wise-webapps</superpom.dir> <superpom.dir>${project.basedir}/wise-webapps</superpom.dir>
</properties> </properties>
@ -16,7 +16,7 @@
<groupId>org.wisemapping</groupId> <groupId>org.wisemapping</groupId>
<artifactId>wisemapping</artifactId> <artifactId>wisemapping</artifactId>
<name>WiseMapping Project</name> <name>WiseMapping Project</name>
<version>5.0.14</version> <version>5.0.19</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<licenses> <licenses>

View File

@ -12,7 +12,7 @@
<groupId>org.wisemapping</groupId> <groupId>org.wisemapping</groupId>
<artifactId>wisemapping</artifactId> <artifactId>wisemapping</artifactId>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
<version>5.0.14</version> <version>5.0.19</version>
</parent> </parent>
<build> <build>
@ -29,11 +29,11 @@
<mkdir dir="target/wisemapping-mindplot"/> <mkdir dir="target/wisemapping-mindplot"/>
<exec executable="npm" dir="target" failonerror="true"> <exec executable="npm" dir="target" failonerror="true">
<arg value="pack"/> <arg value="pack"/>
<arg value="@wisemapping/mindplot@5.0.17"/> <arg value="@wisemapping/mindplot@5.0.19"/>
</exec> </exec>
<exec executable="tar" dir="target" failonerror="true"> <exec executable="tar" dir="target" failonerror="true">
<arg value="-xvzf"/> <arg value="-xvzf"/>
<arg value="wisemapping-mindplot-5.0.17.tgz"/> <arg value="wisemapping-mindplot-5.0.19.tgz"/>
<arg value="-C"/> <arg value="-C"/>
<arg value="wisemapping-mindplot"/> <arg value="wisemapping-mindplot"/>
</exec> </exec>
@ -42,11 +42,11 @@
<mkdir dir="target/wisemapping-webapp"/> <mkdir dir="target/wisemapping-webapp"/>
<exec executable="npm" dir="target" failonerror="true"> <exec executable="npm" dir="target" failonerror="true">
<arg value="pack"/> <arg value="pack"/>
<arg value="@wisemapping/webapp@5.0.18"/> <arg value="@wisemapping/webapp@5.0.19"/>
</exec> </exec>
<exec executable="tar" dir="target" failonerror="true"> <exec executable="tar" dir="target" failonerror="true">
<arg value="-xvzf"/> <arg value="-xvzf"/>
<arg value="wisemapping-webapp-5.0.18.tgz"/> <arg value="wisemapping-webapp-5.0.19.tgz"/>
<arg value="-C"/> <arg value="-C"/>
<arg value="wisemapping-webapp"/> <arg value="wisemapping-webapp"/>
</exec> </exec>

View File

@ -1,4 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>wise-webapp</artifactId> <artifactId>wise-webapp</artifactId>
<packaging>war</packaging> <packaging>war</packaging>
@ -8,13 +9,13 @@
<groupId>org.wisemapping</groupId> <groupId>org.wisemapping</groupId>
<artifactId>wisemapping</artifactId> <artifactId>wisemapping</artifactId>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
<version>5.0.14</version> <version>5.0.19</version>
</parent> </parent>
<properties> <properties>
<org.springframework.version>5.3.22</org.springframework.version> <org.springframework.version>5.3.24</org.springframework.version>
<org.springframework.addons>5.6.2</org.springframework.addons> <org.springframework.addons>5.7.3</org.springframework.addons>
<hibernate.version>5.6.11.Final</hibernate.version> <hibernate.version>5.6.12.Final</hibernate.version>
<hibernate-validator.version>6.0.21.Final</hibernate-validator.version> <hibernate-validator.version>6.0.21.Final</hibernate-validator.version>
<spring-security-taglibs.version>5.6.1</spring-security-taglibs.version> <spring-security-taglibs.version>5.6.1</spring-security-taglibs.version>
</properties> </properties>
@ -41,7 +42,7 @@
<dependency> <dependency>
<groupId>com.intellij</groupId> <groupId>com.intellij</groupId>
<artifactId>annotations</artifactId> <artifactId>annotations</artifactId>
<version>7.0.3</version> <version>12.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -50,6 +51,11 @@
<version>${org.springframework.version}</version> <version>${org.springframework.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.5.1</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId> <artifactId>spring-security-ldap</artifactId>
@ -195,7 +201,7 @@
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version> <version>8.0.31</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -219,10 +225,9 @@
<version>3.9.9</version> <version>3.9.9</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j</artifactId> <artifactId>log4j-core</artifactId>
<version>1.2.17</version> <version>2.19.0</version>
<scope>compile</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/commons-validator/commons-validator --> <!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
<dependency> <dependency>
@ -240,7 +245,7 @@
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>2.13.1</version> <version>2.13.4.2</version>
</dependency> </dependency>
<dependency> <dependency>
<!-- This is required in case of Tomcat, do not remove --> <!-- This is required in case of Tomcat, do not remove -->
@ -291,15 +296,10 @@
</configuration> </configuration>
<dependencies> <dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.5</version>
</dependency>
<dependency> <dependency>
<groupId>org.hsqldb</groupId> <groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId> <artifactId>hsqldb</artifactId>
<version>2.6.1</version> <version>2.7.1</version>
</dependency> </dependency>
</dependencies> </dependencies>
@ -343,7 +343,7 @@
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>5.1.5</version> <version>8.0.31</version>
</dependency> </dependency>
</dependencies> </dependencies>
<executions> <executions>
@ -498,13 +498,13 @@
</configuration> </configuration>
</execution> </execution>
<!-- Confirm why there is a NPE --> <!-- Confirm why there is a NPE -->
<!-- <execution>--> <!-- <execution>-->
<!-- <id>default-report-integration</id>--> <!-- <id>default-report-integration</id>-->
<!-- <phase>verify</phase>--> <!-- <phase>verify</phase>-->
<!-- <goals>--> <!-- <goals>-->
<!-- <goal>report-integration</goal>--> <!-- <goal>report-integration</goal>-->
<!-- </goals>--> <!-- </goals>-->
<!-- </execution>--> <!-- </execution>-->
<execution> <execution>
<id>default-report</id> <id>default-report</id>
<phase>verify</phase> <phase>verify</phase>
@ -559,7 +559,9 @@
<daemon>true</daemon> <daemon>true</daemon>
<waitForChild>false</waitForChild> <waitForChild>false</waitForChild>
<maxStartupLines>200</maxStartupLines> <maxStartupLines>200</maxStartupLines>
<jvmArgs>${integrationTestArgLine} -Ddatabase.base.url=${project.build.directory} -Djetty.port=8080</jvmArgs> <jvmArgs>${integrationTestArgLine} -Ddatabase.base.url=${project.build.directory}
-Djetty.port=8080
</jvmArgs>
</configuration> </configuration>
</execution> </execution>
<execution> <execution>

View File

@ -19,6 +19,7 @@
package com.wisemapping.dao; package com.wisemapping.dao;
import com.wisemapping.model.AccessAuditory; import com.wisemapping.model.AccessAuditory;
import com.wisemapping.model.AuthenticationType;
import com.wisemapping.model.Collaboration; import com.wisemapping.model.Collaboration;
import com.wisemapping.model.Collaborator; import com.wisemapping.model.Collaborator;
import com.wisemapping.model.User; 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.orm.hibernate5.support.HibernateDaoSupport;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
@ -101,7 +101,11 @@ public class UserManagerImpl
@Override @Override
public void createUser(User user) { public void createUser(User user) {
assert user != null : "Trying to store a null 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); getHibernateTemplate().saveOrUpdate(user);
} }

View File

@ -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
*
* <filter>
* <filter-name>cross-origin</filter-name>
* <filter-class>com.wisemapping.filter.CorsFilter</filter-class>
* <init-param>
* <param-name>allowedOrigins</param-name>
* <param-value>*</param-value>
* </init-param>
* <init-param>
* <param-name>allowedMethods</param-name>
* <param-value>GET,POST,HEAD</param-value>
* </init-param>
* <init-param>
* <param-name>allowedHeaders</param-name>
* <param-value>X-Requested-With,Content-Type,Accept,Origin</param-value>
* </init-param>
* </filter>
* <filter-mapping>
* <filter-name>cross-origin</filter-name>
* <url-pattern>/*</url-pattern>
* </filter-mapping>
*
*/
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() {
}
}

View File

@ -54,6 +54,9 @@ public class RequestPropertiesInterceptor implements HandlerInterceptor {
@Value("${security.type}") @Value("${security.type}")
private String securityType; private String securityType;
@Value("${security.oauth2.google.url}")
private String googleOauth2Url;
@Override @Override
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, Object object) throws Exception { 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.enabled", recaptcha2Enabled);
request.setAttribute("google.recaptcha2.siteKey", recaptcha2SiteKey); request.setAttribute("google.recaptcha2.siteKey", recaptcha2SiteKey);
request.setAttribute("security.oauth2.google.url", googleOauth2Url);
request.setAttribute("site.homepage", siteHomepage); request.setAttribute("site.homepage", siteHomepage);
request.setAttribute("site.static.js.url", siteStaticUrl); request.setAttribute("site.static.js.url", siteStaticUrl);

View File

@ -24,8 +24,10 @@ import com.wisemapping.model.User;
import com.wisemapping.security.Utils; import com.wisemapping.security.Utils;
import com.wisemapping.service.LockManager; import com.wisemapping.service.LockManager;
import com.wisemapping.service.MindmapService; import com.wisemapping.service.MindmapService;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull; 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.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.context.support.WebApplicationContextUtils;
@ -34,7 +36,7 @@ import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener; import javax.servlet.http.HttpSessionListener;
public class UnlockOnExpireListener implements HttpSessionListener { public class UnlockOnExpireListener implements HttpSessionListener {
private static final Logger logger = Logger.getLogger(UnlockOnExpireListener.class); private static final Logger logger = LogManager.getLogger();
@Override @Override
public void sessionCreated(@NotNull HttpSessionEvent event) { public void sessionCreated(@NotNull HttpSessionEvent event) {

View File

@ -24,7 +24,8 @@ import com.wisemapping.model.Mindmap;
import com.wisemapping.model.User; import com.wisemapping.model.User;
import com.wisemapping.rest.model.RestLogItem; import com.wisemapping.rest.model.RestLogItem;
import org.apache.commons.lang.StringEscapeUtils; 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.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -42,7 +43,7 @@ import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
final public class NotificationService { final public class NotificationService {
final private static Logger logger = Logger.getLogger(Mailer.class); final private static Logger logger = LogManager.getLogger();
private ResourceBundleMessageSource messageSource; private ResourceBundleMessageSource messageSource;
@Autowired @Autowired

View File

@ -23,7 +23,8 @@ import org.jetbrains.annotations.NotNull;
public enum AuthenticationType { public enum AuthenticationType {
DATABASE('D'), DATABASE('D'),
LDAP('L'), LDAP('L'),
OPENID('O'); GOOGLE_OAUTH2('G');
private final char schemaCode; private final char schemaCode;
AuthenticationType(char schemaCode) { AuthenticationType(char schemaCode) {

View File

@ -19,13 +19,12 @@
package com.wisemapping.model; package com.wisemapping.model;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import javax.persistence.*; import javax.persistence.*;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects;
@Entity @Entity
@Table(name = "COLLABORATION") @Table(name = "COLLABORATION")
@ -127,16 +126,14 @@ public class Collaboration implements Serializable {
Collaboration that = (Collaboration) o; Collaboration that = (Collaboration) o;
if (id != that.id) return false; if (id != that.id) return false;
if (collaborator != null ? !collaborator.equals(that.collaborator) : that.collaborator != null) return false; if (!Objects.equals(collaborator, that.collaborator)) return false;
if (mindMap != null ? !mindMap.equals(that.mindMap) : that.mindMap != null) return false; if (!Objects.equals(mindMap, that.mindMap)) return false;
return role == that.role; return role == that.role;
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = id ^ (id >>> 32); //https://thorben-janssen.com/ultimate-guide-to-implementing-equals-and-hashcode-with-hibernate/
result = 31 * result + (role != null ? role.hashCode() : 0); return 13;
result = 31 * result + (mindMap != null ? mindMap.hashCode() : 0);
return result;
} }
} }

View File

@ -105,7 +105,7 @@ public class Collaborator implements Serializable {
int id = this.getId(); int id = this.getId();
String email = this.getEmail(); String email = this.getEmail();
int result = (int) (id ^ (id >>> 32)); int result = id ^ (id >>> 32);
result = 31 * result + (email != null ? email.hashCode() : 0); result = 31 * result + (email != null ? email.hashCode() : 0);
return result; return result;
} }

View File

@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable;
import javax.persistence.*; import javax.persistence.*;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects;
@Entity @Entity
@Table(name = "LABEL") @Table(name = "LABEL")
@ -34,17 +35,22 @@ public class Label implements Serializable {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private int id; private int id;
@NotNull private String title; @NotNull
@NotNull private String color; private String title;
@Nullable private String iconName; @NotNull
private String color;
@Nullable
private String iconName;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="creator_id",nullable = true,unique = true) @JoinColumn(name = "creator_id", nullable = true, unique = true)
@NotNull private User creator; @NotNull
private User creator;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="parent_label_id",nullable = true) @JoinColumn(name = "parent_label_id", nullable = true)
@Nullable private Label parent; @Nullable
private Label parent;
public void setParent(@Nullable Label parent) { public void setParent(@Nullable Label parent) {
this.parent = parent; this.parent = parent;
@ -104,17 +110,15 @@ public class Label implements Serializable {
if (this == o) return true; if (this == o) return true;
if (!(o instanceof Label)) return false; if (!(o instanceof Label)) return false;
Label label = (Label) o; final Label label = (Label) o;
return id == label.id && creator.getId() == label.creator.getId() return id == label.id && creator.getId() == label.creator.getId()
&& !(parent != null ? !parent.equals(label.parent) : label.parent != null); && Objects.equals(parent, label.parent);
} }
@Override @Override
public int hashCode() { public int hashCode() {
long result = id; long result = title.hashCode();
result = 31 * result + title.hashCode(); result = 31 * result + (creator != null ? creator.hashCode() : 0);
result = 31 * result + (creator!=null?creator.hashCode():0);
result = 31 * result + (parent != null ? parent.hashCode() : 0); result = 31 * result + (parent != null ? parent.hashCode() : 0);
return (int) result; return (int) result;
} }

View File

@ -83,13 +83,9 @@ public class Mindmap implements Serializable {
@Basic(fetch = FetchType.LAZY) @Basic(fetch = FetchType.LAZY)
private byte[] zippedXml; private byte[] zippedXml;
//~ Constructors .........................................................................................
public Mindmap() { public Mindmap() {
} }
//~ Methods ..............................................................................................
public void setUnzipXml(@NotNull byte[] value) { public void setUnzipXml(@NotNull byte[] value) {
try { try {
final byte[] zip = ZipUtils.bytesToZip(value); final byte[] zip = ZipUtils.bytesToZip(value);
@ -146,7 +142,9 @@ public class Mindmap implements Serializable {
} }
public void removedCollaboration(@NotNull Collaboration collaboration) { 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<Collaboration> collaborations) { public void removedCollaboration(@NotNull Set<Collaboration> collaborations) {
@ -316,11 +314,20 @@ public class Mindmap implements Serializable {
final StringBuilder result = new StringBuilder(); final StringBuilder result = new StringBuilder();
result.append("<map version=\"tango\">"); result.append("<map version=\"tango\">");
result.append("<topic central=\"true\" text=\""); result.append("<topic central=\"true\" text=\"");
result.append(StringEscapeUtils.escapeXml(title)); result.append(escapeXmlAttribute(title));
result.append("\"/></map>"); result.append("\"/></map>");
return result.toString(); return result.toString();
} }
static private String escapeXmlAttribute(String attValue) {
// Hack: Find out of the box function.
String result = attValue.replace("&", "&amp;");
result = result.replace("<", "&lt;");
result = result.replace("gt", "&gt;");
result = result.replace("\"", "&quot;");
return result;
}
public Mindmap shallowClone() { public Mindmap shallowClone() {
final Mindmap result = new Mindmap(); final Mindmap result = new Mindmap();
result.setDescription(this.getDescription()); result.setDescription(this.getDescription());
@ -351,18 +358,6 @@ public class Mindmap implements Serializable {
return false; 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) { public void removeLabel(@NotNull final Label label) {
this.labels.remove(label); this.labels.remove(label);
} }

View File

@ -18,15 +18,12 @@
package com.wisemapping.model; package com.wisemapping.model;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import javax.persistence.*; import javax.persistence.*;
import java.io.Serializable; import java.io.Serializable;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
@Entity @Entity
@Table(name = "USER") @Table(name = "USER")
@ -39,21 +36,30 @@ public class User
private String lastname; private String lastname;
private String password; private String password;
private String locale; private String locale;
@Column(name = "activation_code") @Column(name = "activation_code")
private long activationCode; private long activationCode;
@Column(name = "activation_date") @Column(name = "activation_date")
private Calendar activationDate; private Calendar activationDate;
@Column(name = "allow_send_email") @Column(name = "allow_send_email")
private boolean allowSendEmail = false; private boolean allowSendEmail = false;
@Column(name = "authentication_type") @Column(name = "authentication_type")
private Character authenticationTypeCode = AuthenticationType.DATABASE.getCode(); private Character authenticationTypeCode = AuthenticationType.DATABASE.getCode();
@Column(name = "authenticator_uri") @Column(name = "authenticator_uri")
private String authenticatorUri; 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() { public User() {
} }
@ -151,7 +157,35 @@ public class User
this.authenticatorUri = authenticatorUri; 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() { public String toString() {
return "User{" + return "User{" +
"firstname='" + firstname + '\'' + "firstname='" + firstname + '\'' +

View File

@ -19,19 +19,15 @@
package com.wisemapping.rest; package com.wisemapping.rest;
import com.wisemapping.exceptions.WiseMappingException; import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.mail.NotificationService;
import com.wisemapping.model.Collaboration; import com.wisemapping.model.Collaboration;
import com.wisemapping.model.Label; import com.wisemapping.model.Label;
import com.wisemapping.model.Mindmap; import com.wisemapping.model.Mindmap;
import com.wisemapping.model.User; import com.wisemapping.model.User;
import com.wisemapping.rest.model.RestLogItem;
import com.wisemapping.rest.model.RestUser; import com.wisemapping.rest.model.RestUser;
import com.wisemapping.security.Utils; import com.wisemapping.security.Utils;
import com.wisemapping.service.LabelService; import com.wisemapping.service.LabelService;
import com.wisemapping.service.MindmapService; import com.wisemapping.service.MindmapService;
import com.wisemapping.service.UserService; 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.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus; 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.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import javax.servlet.http.HttpServletRequest;
import java.util.List; import java.util.List;
@Controller @Controller
@ -58,10 +53,6 @@ public class AccountController extends BaseController {
@Autowired @Autowired
private LabelService labelService; private LabelService labelService;
@Autowired
private NotificationService notificationService;
@RequestMapping(method = RequestMethod.PUT, value = "account/password", consumes = {"text/plain"}) @RequestMapping(method = RequestMethod.PUT, value = "account/password", consumes = {"text/plain"})
@ResponseStatus(value = HttpStatus.NO_CONTENT) @ResponseStatus(value = HttpStatus.NO_CONTENT)
public void changePassword(@RequestBody String password) { public void changePassword(@RequestBody String password) {

View File

@ -24,7 +24,8 @@ import com.wisemapping.model.User;
import com.wisemapping.rest.model.RestErrors; import com.wisemapping.rest.model.RestErrors;
import com.wisemapping.security.Utils; import com.wisemapping.security.Utils;
import com.wisemapping.service.RegistrationException; 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.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@ -42,7 +43,7 @@ import java.util.Locale;
public class BaseController { public class BaseController {
final private Logger logger = Logger.getLogger(BaseController.class); final private Logger logger = LogManager.getLogger();
@Qualifier("messageSource") @Qualifier("messageSource")
@Autowired @Autowired

View File

@ -25,7 +25,8 @@ import com.wisemapping.security.Utils;
import com.wisemapping.service.*; import com.wisemapping.service.*;
import com.wisemapping.validator.MapInfoValidator; import com.wisemapping.validator.MapInfoValidator;
import org.apache.commons.validator.routines.EmailValidator; 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.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@ -46,7 +47,7 @@ import java.util.stream.Collectors;
@Controller @Controller
public class MindmapController extends BaseController { 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"; private static final String LATEST_HISTORY_REVISION = "latest";

View File

@ -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);
}
}

View File

@ -22,110 +22,122 @@ import com.wisemapping.exceptions.EmailNotExistsException;
import com.wisemapping.exceptions.WiseMappingException; import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.model.AuthenticationType; import com.wisemapping.model.AuthenticationType;
import com.wisemapping.model.User; import com.wisemapping.model.User;
import com.wisemapping.rest.model.RestResetPasswordResponse;
import com.wisemapping.rest.model.RestUserRegistration; import com.wisemapping.rest.model.RestUserRegistration;
import com.wisemapping.service.*; import com.wisemapping.service.*;
import com.wisemapping.validator.Messages; import com.wisemapping.validator.Messages;
import com.wisemapping.validator.UserValidator; 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.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@Controller @Controller
@CrossOrigin @CrossOrigin
public class UserController extends BaseController { public class UserController extends BaseController {
@Qualifier("userService") @Qualifier("userService")
@Autowired @Autowired
private UserService userService; private UserService userService;
@Autowired @Autowired
private RecaptchaService captchaService; private RecaptchaService captchaService;
@Value("${google.recaptcha2.enabled}") @Qualifier("authenticationManager")
private Boolean recatchaEnabled; @Autowired
private AuthenticationManager authManager;
@Value("${accounts.exclusion.domain:''}") @Value("${google.recaptcha2.enabled}")
private String domainBanExclusion; private Boolean recatchaEnabled;
private static final Logger logger = Logger.getLogger(UserController.class); @Value("${accounts.exclusion.domain:''}")
private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP"; private String domainBanExclusion;
@RequestMapping(method = RequestMethod.POST, value = "/users", produces = {"application/json"}) private static final Logger logger = LogManager.getLogger();
@ResponseStatus(value = HttpStatus.CREATED) private static final String REAL_IP_ADDRESS_HEADER = "X-Real-IP";
public void registerUser(@RequestBody RestUserRegistration registration, @NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws WiseMappingException, BindException {
logger.debug("Register new user:" + registration.getEmail());
// If tomcat is behind a reverse proxy, ip needs to be found in other header. @RequestMapping(method = RequestMethod.POST, value = "/users", produces = { "application/json" })
String remoteIp = request.getHeader(REAL_IP_ADDRESS_HEADER); @ResponseStatus(value = HttpStatus.CREATED)
if (remoteIp == null || remoteIp.isEmpty()) { public void registerUser(@RequestBody RestUserRegistration registration, @NotNull HttpServletRequest request,
remoteIp = request.getRemoteAddr(); @NotNull HttpServletResponse response) throws WiseMappingException, BindException {
} logger.debug("Register new user:" + registration.getEmail());
logger.debug("Remote address" + remoteIp);
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(); verify(registration, remoteIp);
user.setEmail(registration.getEmail().trim());
user.setFirstname(registration.getFirstname());
user.setLastname(registration.getLastname());
user.setPassword(registration.getPassword());
user.setAuthenticationType(AuthenticationType.DATABASE); final User user = new User();
userService.createUser(user, false, true); user.setEmail(registration.getEmail().trim());
response.setHeader("Location", "/service/users/" + user.getId()); user.setFirstname(registration.getFirstname());
} user.setLastname(registration.getLastname());
user.setPassword(registration.getPassword());
@RequestMapping(method = RequestMethod.PUT, value = "/users/resetPassword", produces = {"application/json"}) user.setAuthenticationType(AuthenticationType.DATABASE);
@ResponseStatus(value = HttpStatus.OK) userService.createUser(user, false, true);
public void resetPassword(@RequestParam String email) throws InvalidAuthSchemaException, EmailNotExistsException { response.setHeader("Location", "/service/users/" + user.getId());
try { }
userService.resetPassword(email);
} catch (InvalidUserEmailException e) {
throw new EmailNotExistsException(e);
}
}
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"); private void verify(@NotNull final RestUserRegistration registration, @NotNull String remoteAddress)
final UserValidator validator = new UserValidator(); throws BindException {
validator.setUserService(userService);
validator.validate(registration, errors);
// If captcha is enabled, generate it ... final BindException errors = new RegistrationException(registration, "registration");
if (recatchaEnabled) { final UserValidator validator = new UserValidator();
final String recaptcha = registration.getRecaptcha(); validator.setUserService(userService);
if (recaptcha != null) { validator.validate(registration, errors);
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.");
}
if (errors.hasErrors()) { // If captcha is enabled, generate it ...
throw errors; 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 ?. if (errors.hasErrors()) {
final List<String> excludedDomains = Arrays.asList(domainBanExclusion.split(",")); throw errors;
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); // Is excluded ?.
} final List<String> 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);
}
}
} }

View File

@ -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;
}
}

View File

@ -0,0 +1,7 @@
package com.wisemapping.rest.model;
public enum RestResetPasswordAction {
EMAIL_SENT, OAUTH2_USER
}

View File

@ -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;
}
}

View File

@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.wisemapping.model.AuthenticationType;
import com.wisemapping.model.User; import com.wisemapping.model.User;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -102,6 +103,10 @@ public class RestUser {
return this.user; return this.user;
} }
public AuthenticationType getAuthenticationType() {
return user.getAuthenticationType();
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (!(o instanceof RestUser)) { if (!(o instanceof RestUser)) {

View File

@ -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);
}
}

View File

@ -18,7 +18,9 @@
package com.wisemapping.security; 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.Base64;
import org.springframework.security.crypto.codec.Hex; import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.codec.Utf8; import org.springframework.security.crypto.codec.Utf8;
@ -29,7 +31,7 @@ import java.security.NoSuchAlgorithmException;
public class LegacyPasswordEncoder implements PasswordEncoder { 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:"; public static final String ENC_PREFIX = "ENC:";
private final ShaPasswordEncoder sha1Encoder = new ShaPasswordEncoder(); private final ShaPasswordEncoder sha1Encoder = new ShaPasswordEncoder();

View File

@ -23,7 +23,6 @@ import com.wisemapping.model.User;
import com.wisemapping.service.UserService; import com.wisemapping.service.UserService;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;

View File

@ -38,7 +38,6 @@ public class ViewBaseSecurityAdvise
@Override @Override
protected boolean isAllowed(@Nullable User user, Mindmap map) { protected boolean isAllowed(@Nullable User user, Mindmap map) {
System.out.println("VIEWWWWWWWWWWWWW");
return getMindmapService().hasPermissions(user, map, CollaborationRole.VIEWER); return getMindmapService().hasPermissions(user, map, CollaborationRole.VIEWER);
} }

View File

@ -23,18 +23,21 @@ import com.wisemapping.exceptions.LockException;
import com.wisemapping.model.CollaborationRole; import com.wisemapping.model.CollaborationRole;
import com.wisemapping.model.Mindmap; import com.wisemapping.model.Mindmap;
import com.wisemapping.model.User; import com.wisemapping.model.User;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull; 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; import java.util.concurrent.ConcurrentHashMap;
class LockManagerImpl implements LockManager { class LockManagerImpl implements LockManager {
private static final int ONE_MINUTE_MILLISECONDS = 1000 * 60; private static final int ONE_MINUTE_MILLISECONDS = 1000 * 60;
private final Map<Integer, LockInfo> lockInfoByMapId; private final Map<Integer, LockInfo> lockInfoByMapId;
private final static Timer expirationTimer = new Timer(); private final static Timer expirationTimer = new Timer();
final private static Logger logger = Logger.getLogger(LockManagerImpl.class); final private static Logger logger = LogManager.getLogger();
@Override @Override
public boolean isLocked(@NotNull Mindmap mindmap) { public boolean isLocked(@NotNull Mindmap mindmap) {

View File

@ -143,15 +143,13 @@ public class MindmapServiceImpl
public void removeCollaboration(@NotNull Mindmap mindmap, @NotNull Collaboration collaboration) throws CollaborationException { public void removeCollaboration(@NotNull Mindmap mindmap, @NotNull Collaboration collaboration) throws CollaborationException {
// remove collaborator association // remove collaborator association
final Mindmap mindMap = collaboration.getMindMap(); final Mindmap mindMap = collaboration.getMindMap();
final Set<Collaboration> collaborations = mindMap.getCollaborations();
final User creator = mindMap.getCreator(); final User creator = mindMap.getCreator();
if (creator.identityEquality(collaboration.getCollaborator())) { 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()); 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... // 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); mindmapManager.removeCollaboration(collaboration);
} }
@ -249,7 +247,7 @@ public class MindmapServiceImpl
@Override @Override
public void revertChange(@NotNull Mindmap mindmap, int historyId) public void revertChange(@NotNull Mindmap mindmap, int historyId)
throws WiseMappingException, IOException { throws WiseMappingException {
final MindMapHistory history = mindmapManager.getHistory(historyId); final MindMapHistory history = mindmapManager.getHistory(historyId);
mindmap.setZippedXml(history.getZippedXml()); mindmap.setZippedXml(history.getZippedXml());
updateMindmap(mindmap, true); updateMindmap(mindmap, true);

View File

@ -17,13 +17,15 @@
*/ */
package com.wisemapping.service; package com.wisemapping.service;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.wisemapping.validator.Messages; import com.wisemapping.validator.Messages;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
import org.apache.http.client.fluent.Form; import org.apache.http.client.fluent.Form;
import org.apache.http.client.fluent.Request; import org.apache.http.client.fluent.Request;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@ -35,7 +37,7 @@ import java.util.Map;
public class RecaptchaService { 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 = final private static String GOOGLE_RECAPTCHA_VERIFY_URL =
"https://www.google.com/recaptcha/api/siteverify"; "https://www.google.com/recaptcha/api/siteverify";

View File

@ -20,6 +20,8 @@ package com.wisemapping.service;
import com.wisemapping.exceptions.WiseMappingException; import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.model.User; import com.wisemapping.model.User;
import com.wisemapping.rest.model.RestResetPasswordResponse;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public interface UserService { public interface UserService {
@ -28,6 +30,10 @@ public interface UserService {
User createUser(@NotNull User user, boolean emailConfirmEnabled, boolean welcomeEmail) throws WiseMappingException; 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); void changePassword(@NotNull User user);
User getUserBy(String email); User getUserBy(String email);
@ -36,7 +42,7 @@ public interface UserService {
void updateUser(User user); 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); void removeUser(@NotNull User user);

View File

@ -23,6 +23,10 @@ import com.wisemapping.exceptions.InvalidMindmapException;
import com.wisemapping.exceptions.WiseMappingException; import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.mail.NotificationService; import com.wisemapping.mail.NotificationService;
import com.wisemapping.model.*; 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.VelocityEngineUtils;
import com.wisemapping.util.VelocityEngineWrapper; import com.wisemapping.util.VelocityEngineWrapper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -39,7 +43,7 @@ public class UserServiceImpl
private NotificationService notificationService; private NotificationService notificationService;
private MessageSource messageSource; private MessageSource messageSource;
private VelocityEngineWrapper velocityEngineWrapper; private VelocityEngineWrapper velocityEngineWrapper;
private GoogleService googleService;
@Override @Override
public void activateAccount(long code) public void activateAccount(long code)
@ -56,10 +60,15 @@ public class UserServiceImpl
} }
@Override @Override
public void resetPassword(@NotNull String email) public RestResetPasswordResponse resetPassword(@NotNull String email)
throws InvalidUserEmailException, InvalidAuthSchemaException { throws InvalidUserEmailException, InvalidAuthSchemaException {
final User user = userManager.getUserBy(email); final User user = userManager.getUserBy(email);
if (user != null) { 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) { if (user.getAuthenticationType() != AuthenticationType.DATABASE) {
throw new InvalidAuthSchemaException("Could not change password for " + user.getAuthenticationType().getCode()); 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 ... // Send an email with the new temporal password ...
notificationService.resetPassword(user, password); notificationService.resetPassword(user, password);
response.setAction(RestResetPasswordAction.EMAIL_SENT);
return response;
} else { } else {
throw new InvalidUserEmailException("The email '" + email + "' does not exists."); throw new InvalidUserEmailException("The email '" + email + "' does not exists.");
} }
@ -147,6 +159,55 @@ public class UserServiceImpl
return user; 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 { public Mindmap buildTutorialMindmap(@NotNull String firstName) throws InvalidMindmapException {
//To change body of created methods use File | Settings | File Templates. //To change body of created methods use File | Settings | File Templates.
final Locale locale = LocaleContextHolder.getLocale(); final Locale locale = LocaleContextHolder.getLocale();
@ -209,7 +270,11 @@ public class UserServiceImpl
this.velocityEngineWrapper = velocityEngineWrapper; this.velocityEngineWrapper = velocityEngineWrapper;
} }
@Override public void setGoogleService(GoogleService googleService) {
this.googleService = googleService;
}
@Override
public User getCasUserBy(String uid) { public User getCasUserBy(String uid) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;

View File

@ -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 + "]";
}
}

View File

@ -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<String, String> getHeaders(String token) {
Map<String, String> headers = new HashMap<String, String>();
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<String, String> getOptinConfirmBody(String code) {
Map<String, String> result = new HashMap<String, String>();
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<String, String> 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;
}
}

View File

@ -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<String, String> headers,
String jsonPayload,
Map<String, String> 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<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
Set<String> 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<String> 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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,5 @@
package com.wisemapping.service.http;
public enum HttpMethod {
POST, GET, DELETE, PUT
}

View File

@ -39,6 +39,11 @@ public class UsersController {
return new ModelAndView("forgot-password"); 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) @RequestMapping(value = "registration", method = RequestMethod.GET)
public ModelAndView showRegistrationPage() { public ModelAndView showRegistrationPage() {
return new ModelAndView("registration"); return new ModelAndView("registration");

View File

@ -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 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 <a href="mailto:{0}">hier</a>. 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 <a href="mailto:{0}">hier</a>.
EMAIL.GREETINGS=Hallo EMAIL.GREETINGS=Hallo
OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. 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

View File

@ -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 <a href="mailto:{0}">here</a>. 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 <a href="mailto:{0}">here</a>.
EMAIL.GREETINGS=Hi 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. 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. 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

View File

@ -67,4 +67,7 @@ PASSWORD_CHANGED.EMAIL_BODY=<p>Esto es solo una notificación de que su contrase
SHARE_MAP.EMAIL_SUBJECT={0} te ha compartido un mapa mental 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 <a href="mailto:{0}">aquí</a>. 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 <a href="mailto:{0}">aquí</a>.
EMAIL.GREETINGS=Hola EMAIL.GREETINGS=Hola
OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. 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

View File

@ -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 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 <a href="mailto:{0}">ici</a>. 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 <a href="mailto:{0}">ici</a>.
EMAIL.GREETINGS=Salut EMAIL.GREETINGS=Salut
OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list. 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

View File

@ -134,11 +134,29 @@ security.ldap.auth.attribute=mail
security.ldap.lastName.attribute=sn security.ldap.lastName.attribute=sn
security.ldap.firstName.attribute=givenName security.ldap.firstName.attribute=givenName
#######################################################################################
# Google OAuth Authentication
#######################################################################################
# OAuth Client id
#security.oauth2.google.clientId=<config settings>
# OAuth Client secret
#security.oauth2.google.clientSecret=<oauth client>
# Redirect to this url, this url must be configured in the google app {baseurl}/c/registration-google
#security.oauth2.google.callbackUrl=<oauth callback url>
# 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 # User Account filtering policies
#######################################################################################
# Coma separated list of domains and emails ban # Coma separated list of domains and emails ban
#accounts.exclusion.domain= #accounts.exclusion.domain=

View File

@ -8,6 +8,7 @@
<definition name="login" template="/jsp/reactInclude.jsp"/> <definition name="login" template="/jsp/reactInclude.jsp"/>
<definition name="registration" template="/jsp/reactInclude.jsp"/> <definition name="registration" template="/jsp/reactInclude.jsp"/>
<definition name="registration-google" template="/jsp/reactInclude.jsp"/>
<definition name="forgot-password" template="/jsp/reactInclude.jsp"/> <definition name="forgot-password" template="/jsp/reactInclude.jsp"/>
<definition name="mindmapList" template="/jsp/reactInclude.jsp"/> <definition name="mindmapList" template="/jsp/reactInclude.jsp"/>

View File

@ -11,7 +11,8 @@
<bean id="passwordEncoder" class="com.wisemapping.security.DefaultPasswordEncoderFactories" factory-method="createDelegatingPasswordEncoder"/> <bean id="passwordEncoder" class="com.wisemapping.security.DefaultPasswordEncoderFactories" factory-method="createDelegatingPasswordEncoder"/>
<sec:authentication-manager alias="authenticationManager"> <sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="dbAuthenticationProvider"/> <sec:authentication-provider ref="dbAuthenticationProvider" />
<sec:authentication-provider ref="googleAuthenticationProvider" />
<sec:authentication-provider user-service-ref="userDetailsService"/> <sec:authentication-provider user-service-ref="userDetailsService"/>
</sec:authentication-manager> </sec:authentication-manager>
@ -19,4 +20,7 @@
<property name="userDetailsService" ref="userDetailsService"/> <property name="userDetailsService" ref="userDetailsService"/>
<property name="encoder" ref="passwordEncoder"/> <property name="encoder" ref="passwordEncoder"/>
</bean> </bean>
<bean id="googleAuthenticationProvider" class="com.wisemapping.security.GoogleAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</beans> </beans>

View File

@ -7,6 +7,7 @@
http://www.springframework.org/schema/security http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd"> http://www.springframework.org/schema/security/spring-security.xsd">
<bean id="passwordEncoder" class="com.wisemapping.security.DefaultPasswordEncoderFactories" factory-method="createDelegatingPasswordEncoder"/>
<sec:authentication-manager> <sec:authentication-manager>
<sec:authentication-provider ref="ldapAuthProvider"/> <sec:authentication-provider ref="ldapAuthProvider"/>

View File

@ -3,12 +3,12 @@
<beans xmlns="http://www.springframework.org/schema/beans" <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security" xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd"> http://www.springframework.org/schema/security/spring-security.xsd">
<bean id="custom-firewall" class="org.springframework.security.web.firewall.StrictHttpFirewall"> <bean id="custom-firewall" class="org.springframework.security.web.firewall.StrictHttpFirewall">
<property name="allowSemicolon" value="true"/> <property name="allowSemicolon" value="true"/>
</bean> </bean>
@ -34,6 +34,9 @@
<sec:intercept-url pattern="/service/users/" method="POST" access="permitAll"/> <sec:intercept-url pattern="/service/users/" method="POST" access="permitAll"/>
<sec:intercept-url pattern="/service/users/resetPassword" method="PUT" access="permitAll"/> <sec:intercept-url pattern="/service/users/resetPassword" method="PUT" access="permitAll"/>
<sec:intercept-url pattern="/service/oauth2/googlecallback" method="POST" access="permitAll"/>
<sec:intercept-url pattern="/service/oauth2/confirmaccountsync" method="PUT" access="permitAll"/>
<sec:intercept-url pattern="/service/admin/users/**" access="isAuthenticated() and hasRole('ROLE_ADMIN')"/> <sec:intercept-url pattern="/service/admin/users/**" access="isAuthenticated() and hasRole('ROLE_ADMIN')"/>
<sec:intercept-url pattern="/service/admin/database/**" access="isAuthenticated() and hasRole('ROLE_ADMIN')"/> <sec:intercept-url pattern="/service/admin/database/**" access="isAuthenticated() and hasRole('ROLE_ADMIN')"/>
@ -47,6 +50,7 @@
<sec:intercept-url pattern="/c/login" access="permitAll"/> <sec:intercept-url pattern="/c/login" access="permitAll"/>
<sec:intercept-url pattern="/c/registration" access="hasRole('ANONYMOUS')"/> <sec:intercept-url pattern="/c/registration" access="hasRole('ANONYMOUS')"/>
<sec:intercept-url pattern="/c/registration-success" access="hasRole('ANONYMOUS')"/> <sec:intercept-url pattern="/c/registration-success" access="hasRole('ANONYMOUS')"/>
<sec:intercept-url pattern="/c/registration-google" access="permitAll"/>
<sec:intercept-url pattern="/c/forgot-password" access="hasRole('ANONYMOUS')"/> <sec:intercept-url pattern="/c/forgot-password" access="hasRole('ANONYMOUS')"/>
<sec:intercept-url pattern="/c/forgot-password-success" access="hasRole('ANONYMOUS')"/> <sec:intercept-url pattern="/c/forgot-password-success" access="hasRole('ANONYMOUS')"/>

View File

@ -18,12 +18,25 @@
<property name="velocityEngineWrapper" ref="velocityEngineWrapper"/> <property name="velocityEngineWrapper" ref="velocityEngineWrapper"/>
</bean> </bean>
<bean id="httpInvoker" class="com.wisemapping.service.http.HttpInvoker">
</bean>
<bean id="googleService" class="com.wisemapping.service.google.GoogleService">
<property name="httpInvoker" ref="httpInvoker"/>
<property name="optinConfirmUrl" value="${security.oauth2.google.confirmUrl}"/>
<property name="accountBasicDataUrl" value="${security.oauth2.google.userinfoUrl}"/>
<property name="clientId" value="${security.oauth2.google.clientId}"/>
<property name="clientSecret" value="${security.oauth2.google.clientSecret}"/>
<property name="callbackUrl" value="${security.oauth2.google.callbackUrl}"/>
</bean>
<bean id="userServiceTarget" class="com.wisemapping.service.UserServiceImpl"> <bean id="userServiceTarget" class="com.wisemapping.service.UserServiceImpl">
<property name="userManager" ref="userManager"/> <property name="userManager" ref="userManager"/>
<property name="mindmapService" ref="mindMapServiceTarget"/> <property name="mindmapService" ref="mindMapServiceTarget"/>
<property name="notificationService" ref="notificationService"/> <property name="notificationService" ref="notificationService"/>
<property name="messageSource" ref="messageSource"/> <property name="messageSource" ref="messageSource"/>
<property name="velocityEngineWrapper" ref="velocityEngineWrapper"/> <property name="velocityEngineWrapper" ref="velocityEngineWrapper"/>
<property name="googleService" ref="googleService"/>
</bean> </bean>
<bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

View File

@ -3,7 +3,7 @@
/********************************************************************************/ /********************************************************************************/
body { body {
margin: 0; margin: 0;
font-family:Arial; font-family:Montserrat;
} }
div#mindplot { div#mindplot {

View File

@ -2,6 +2,8 @@
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@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()); request.setAttribute("principal", com.wisemapping.security.Utils.getUser());
%> %>

View File

@ -9,15 +9,13 @@
<%--@elvariable id="lockInfo" type="com.wisemapping.service.LockInfo"--%> <%--@elvariable id="lockInfo" type="com.wisemapping.service.LockInfo"--%>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="${fn:substring(locale,0,2)}">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta charset="utf-8" /> <meta charset="utf-8" />
<base href="${requestScope['site.baseurl']}/static/webapp/"> <base href="${requestScope['site.baseurl']}/static/webapp/">
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin> <link rel="preload" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;600&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'" crossorigin>
<link rel="stylesheet" media="print" onload="this.onload=null;this.removeAttribute('media');" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;600&display=swap"/>
<%@ include file="/jsp/pageHeaders.jsf" %> <%@ include file="/jsp/pageHeaders.jsf" %>
<title>Loading ... | WiseMapping</title> <title>Loading ... | WiseMapping</title>
@ -47,8 +45,8 @@
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="mindplot-root"></div> <div id="root" class="mindplot-root"></div>
<script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/vendors.bundle.js" crossorigin="anonymous"></script> <script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/vendors.bundle.js" crossorigin="anonymous" defer></script>
<script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/app.bundle.js" crossorigin="anonymous"></script> <script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/app.bundle.js" crossorigin="anonymous" defer></script>
</body> </body>

View File

@ -5,17 +5,16 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html> <html lang="${fn:substring(locale,0,2)}">
<head> <head>
<meta name="viewport" content="initial-scale=1"> <meta name="viewport" content="initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<base href="${requestScope['site.baseurl']}/static/mindplot/"> <base href="${requestScope['site.baseurl']}/static/mindplot/">
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin> <link rel="preload" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;600&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'" crossorigin>
<link rel="stylesheet" media="print" onload="this.onload=null;this.removeAttribute('media');" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;600&display=swap"/> <link rel="preload" href="../../css/viewonly.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<title>${mindmap.title} | <spring:message code="SITE.TITLE"/></title> <title>${mindmap.title} | <spring:message code="SITE.TITLE"/></title>
<link rel="stylesheet" href="../../css/viewonly.css"/>
<%@ include file="/jsp/pageHeaders.jsf" %> <%@ include file="/jsp/pageHeaders.jsf" %>
<script type="text/javascript"> <script type="text/javascript">
@ -40,23 +39,6 @@
</script> </script>
</c:if> </c:if>
<c:if test="${requestScope['google.analytics.enabled']}">
<!-- Google Ads Sense Config. Lazy loading optimization -->
<script type="text/javascript">
function downloadJsAtOnload() {
setTimeout(function downloadJs() {
var element = document.createElement("script");
element.setAttribute("data-ad-client", "ca-pub-4996113942657337");
element.async = true;
element.src = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js";
document.body.appendChild(element);
}, 2000);
};
window.addEventListener("load", downloadJsAtOnload, false);
</script>
</c:if>
<style> <style>
body { body {
height: 100vh; height: 100vh;
@ -76,37 +58,36 @@
</head> </head>
<body> <body>
<div id="root" class="mindplot-root"> <div id="root" class="mindplot-root">
<mindplot-component id="mindmap-comp"></mindplot-component> <mindplot-component id="mindmap-comp"/>
<div id="mindplot-tooltips" className="wise-editor"></div> </div>
<div id="mindplot-tooltips" className="wise-editor"></div>
<a href="${requestScope['site.homepage']}" target="new"> <a href="${requestScope['site.homepage']}" target="new" aria-label="WiseMapping Homepage">
<div id="footerLogo"></div> <div id="footerLogo"></div>
</a> </a>
<div id="mapDetails"> <div id="mapDetails">
<span class="title"><spring:message code="CREATOR"/>:</span><span>${mindmap.creator.fullName}</span> <span class="title"><spring:message code="CREATOR"/>:</span><span>${mindmap.creator.fullName}</span>
<span class="title"><spring:message code="DESCRIPTION"/>:</span><span>${mindmap.title}</span> <span class="title"><spring:message code="DESCRIPTION"/>:</span><span>${mindmap.title}</span>
</div> </div>
<script type="text/javascript" src="${requestScope['site.static.js.url']}/mindplot/loader.js" crossorigin="anonymous"></script> <script type="text/javascript" src="${requestScope['site.static.js.url']}/mindplot/loader.js" crossorigin="anonymous" defer></script>
<div id="floating-panel"> <div id="floating-panel">
<div id="zoom-button"> <div id="zoom-button">
<button id="zoom-plus" title="Zoom Out"> <button id="zoom-plus" title="<spring:message code="ZOOM_IN"/>" alt="<spring:message code="ZOOM_IN"/>">
<img src="../../images/add.svg" width="24" height="24"/> <img src="../../images/add.svg" width="24" height="24" alt="<spring:message code="ZOOM_IN"/>"/>
</button> </button>
<button id="zoom-minus" title="Zoom In"> <button id="zoom-minus" title="<spring:message code="ZOOM_OUT"/>" alt="<spring:message code="ZOOM_OUT"/>">
<img src="../../images/minus.svg" width="24" height="24"/> <img src="../../images/minus.svg" width="24" height="24" alt="<spring:message code="ZOOM_OUT"/>"/>
</button> </button>
<div id="position"> <div id="position">
<button id="position-button" title="Center"> <button id="position-button" title="<spring:message code="ZOOM_TO_FIT"/>" alt="<spring:message code="ZOOM_TO_FIT"/>">
<img src="../../images/center_focus.svg" width="24" height="24"/> <img src="../../images/center_focus.svg" width="24" height="24" alt="<spring:message code="ZOOM_TO_FIT"/>"/>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div>
<script type="text/javascript"> <script type="text/javascript">
// Hook zoom events ... // Hook zoom events ...

View File

@ -1,18 +1,17 @@
<%@ taglib uri = "http://java.sun.com/jsp/jstl/functions" prefix = "fn" %>
<%@ page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %> <%@ page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="${fn:substring(locale,0,2)}">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta charset="utf-8" /> <meta charset="utf-8" />
<base href="${requestScope['site.baseurl']}/static/webapp/"> <base href="${requestScope['site.baseurl']}/static/webapp/">
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin> <link rel="preload" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;600&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'" crossorigin>
<link rel="stylesheet" media="print" onload="this.onload=null;this.removeAttribute('media');" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;600&display=swap"/>
<%@ include file="/jsp/pageHeaders.jsf" %> <%@ include file="/jsp/pageHeaders.jsf" %>
<title>Loading | WiseMapping</title> <title>WiseMapping</title>
<script> <script>
window.serverconfig = { window.serverconfig = {
@ -20,7 +19,8 @@
analyticsAccount: '${requestScope['google.analytics.account']}', analyticsAccount: '${requestScope['google.analytics.account']}',
clientType: 'rest', clientType: 'rest',
recaptcha2Enabled: ${requestScope['google.recaptcha2.enabled']}, recaptcha2Enabled: ${requestScope['google.recaptcha2.enabled']},
recaptcha2SiteKey: '${requestScope['google.recaptcha2.siteKey']}' recaptcha2SiteKey: '${requestScope['google.recaptcha2.siteKey']}',
googleOauth2Url: '${requestScope['security.oauth2.google.url']}'
}; };
</script> </script>
@ -34,7 +34,7 @@
element.async = true; element.async = true;
element.src = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"; element.src = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js";
document.body.appendChild(element); document.body.appendChild(element);
}, 2000); }, 50);
}; };
window.addEventListener("load", downloadJsAtOnload, false); window.addEventListener("load", downloadJsAtOnload, false);
@ -46,8 +46,8 @@
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/vendors.bundle.js" crossorigin="anonymous"></script> <script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/vendors.bundle.js" crossorigin="anonymous" async></script>
<script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/app.bundle.js" crossorigin="anonymous"></script> <script type="text/javascript" src="${requestScope['site.static.js.url']}/webapp/app.bundle.js" crossorigin="anonymous" async></script>
</body> </body>
</html> </html>