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
Test coverage report of unit and integration test can be found in wisemapping-open-source/wise-webapp/target/site/jacoco and wisemapping-open-source/wise-webapp/target/site/jacoco-it folders. Coverage report is generated in the verify phase of [lifecicle](https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#introduction-to-the-build-lifecyclea) using [jacoco](https://www.jacoco.org/jacoco/trunk/doc/maven.html)
## Google authorization
You must configure the following wisemapping properties (app.properties) in order to get google authorization working
* `security.oauth2.google.callbackUrl`: url where google will redirect after user authentication, tipically {frontendBaseUrl}/c/registration-google. Also, this url must be defined in google app configuration
* `security.oauth2.google.clientId`: client id from google app
* `security.oauth2.google.clientSecret`: client secret from google app
You must create a Google Application in [Google Cloud](https://console.cloud.google.com) and complete all the information required by Google. Here are the most important properties.
Oauth consent screen
* Authorized domains: wisemapping domain (ex: wisemapping.com), and you can add domains of other environments if needed
* Permissions
* `https://www.googleapis.com/auth/userinfo.profile`
* `https://www.googleapis.com/auth/userinfo.email`
* Test users: emails for testing, those can be used before the application is validated by Google
After that, in Credentials, you must create an `Oauth Client Id` credential
* Authorized JavaScript origins: list of authorized domains from which to redirect to Google. Ex: `https://wisemaping.com`, `https://wisemapping-testing.com:8080`
* Authorized redirect URIs: list of allowed urls to which google will redirect after authenticating . Ex: `https://wisemaping.com/c/registration-google`, `https://wisemapping-testing.com:8080/c/registration-google`
After credential was created, Google will show you the clientId and clientSecret to configure your application. For productive applications, you must **publish** your application, this is a validation process with Google.
## Members
### Founders

View File

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

View File

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

View File

@ -15,6 +15,9 @@ CREATE TABLE "user" (
activation_date DATE,
allow_send_email TEXT NOT NULL DEFAULT 0,
locale VARCHAR(5),
google_sync BOOLEAN,
sync_code VARCHAR(255),
google_token VARCHAR(255),
FOREIGN KEY (colaborator_id) REFERENCES COLLABORATOR (id) ON DELETE CASCADE ON UPDATE NO ACTION
);
@ -27,14 +30,6 @@ CREATE TABLE LABEL (
--FOREIGN KEY (creator_id) REFERENCES USER (colaborator_id)
);
CREATE TABLE R_LABEL_MINDMAP (
mindmap_id INTEGER NOT NULL,
label_id INTEGER NOT NULL,
PRIMARY KEY (mindmap_id, label_id),
FOREIGN KEY (mindmap_id) REFERENCES MINDMAP (id),
FOREIGN KEY (label_id) REFERENCES LABEL (id) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE TABLE MINDMAP (
id SERIAL NOT NULL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
@ -44,11 +39,17 @@ CREATE TABLE MINDMAP (
creation_date TIMESTAMP,
edition_date TIMESTAMP,
creator_id INTEGER NOT NULL,
tags VARCHAR(1014),
last_editor_id INTEGER NOT NULL --,
--FOREIGN KEY(creator_id) REFERENCES "USER"(colaborator_id) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE TABLE R_LABEL_MINDMAP (
mindmap_id INTEGER NOT NULL,
label_id INTEGER NOT NULL,
PRIMARY KEY (mindmap_id, label_id),
FOREIGN KEY (mindmap_id) REFERENCES MINDMAP (id),
FOREIGN KEY (label_id) REFERENCES LABEL (id) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE TABLE MINDMAP_HISTORY
(id SERIAL NOT NULL PRIMARY KEY,
@ -77,14 +78,6 @@ CREATE TABLE COLLABORATION (
FOREIGN KEY (properties_id) REFERENCES COLLABORATION_PROPERTIES (id) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE TABLE TAG (
id SERIAL NOT NULL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
user_id INTEGER NOT NULL --,
--FOREIGN KEY(user_id) REFERENCES "USER"(colaborator_id) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE TABLE ACCESS_AUDITORY (
id SERIAL NOT NULL PRIMARY KEY,
login_date DATE,

View File

@ -2,7 +2,7 @@
# Based on ubuntu:latest, installs WiseMapping (http://ww.wisemapping.org)
# Based info setup ...
FROM tomcat:9.0-jdk17-openjdk
FROM tomcat:jdk17
LABEL maintainer="Paulo Gustavo Veiga <pveiga@wisemapping.com>"
# Build variables ...
@ -31,6 +31,9 @@ RUN sed -i 's|\
</Host>|' \
/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 ...
RUN mkdir -p ${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.
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

View File

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

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">
<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>
</properties>
@ -16,7 +16,7 @@
<groupId>org.wisemapping</groupId>
<artifactId>wisemapping</artifactId>
<name>WiseMapping Project</name>
<version>5.0.14</version>
<version>5.0.19</version>
<packaging>pom</packaging>
<licenses>

View File

@ -12,7 +12,7 @@
<groupId>org.wisemapping</groupId>
<artifactId>wisemapping</artifactId>
<relativePath>../pom.xml</relativePath>
<version>5.0.14</version>
<version>5.0.19</version>
</parent>
<build>
@ -29,11 +29,11 @@
<mkdir dir="target/wisemapping-mindplot"/>
<exec executable="npm" dir="target" failonerror="true">
<arg value="pack"/>
<arg value="@wisemapping/mindplot@5.0.17"/>
<arg value="@wisemapping/mindplot@5.0.19"/>
</exec>
<exec executable="tar" dir="target" failonerror="true">
<arg value="-xvzf"/>
<arg value="wisemapping-mindplot-5.0.17.tgz"/>
<arg value="wisemapping-mindplot-5.0.19.tgz"/>
<arg value="-C"/>
<arg value="wisemapping-mindplot"/>
</exec>
@ -42,11 +42,11 @@
<mkdir dir="target/wisemapping-webapp"/>
<exec executable="npm" dir="target" failonerror="true">
<arg value="pack"/>
<arg value="@wisemapping/webapp@5.0.18"/>
<arg value="@wisemapping/webapp@5.0.19"/>
</exec>
<exec executable="tar" dir="target" failonerror="true">
<arg value="-xvzf"/>
<arg value="wisemapping-webapp-5.0.18.tgz"/>
<arg value="wisemapping-webapp-5.0.19.tgz"/>
<arg value="-C"/>
<arg value="wisemapping-webapp"/>
</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>
<artifactId>wise-webapp</artifactId>
<packaging>war</packaging>
@ -8,13 +9,13 @@
<groupId>org.wisemapping</groupId>
<artifactId>wisemapping</artifactId>
<relativePath>../pom.xml</relativePath>
<version>5.0.14</version>
<version>5.0.19</version>
</parent>
<properties>
<org.springframework.version>5.3.22</org.springframework.version>
<org.springframework.addons>5.6.2</org.springframework.addons>
<hibernate.version>5.6.11.Final</hibernate.version>
<org.springframework.version>5.3.24</org.springframework.version>
<org.springframework.addons>5.7.3</org.springframework.addons>
<hibernate.version>5.6.12.Final</hibernate.version>
<hibernate-validator.version>6.0.21.Final</hibernate-validator.version>
<spring-security-taglibs.version>5.6.1</spring-security-taglibs.version>
</properties>
@ -41,7 +42,7 @@
<dependency>
<groupId>com.intellij</groupId>
<artifactId>annotations</artifactId>
<version>7.0.3</version>
<version>12.0</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -50,6 +51,11 @@
<version>${org.springframework.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
@ -195,7 +201,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
<version>8.0.31</version>
<scope>runtime</scope>
</dependency>
<dependency>
@ -219,10 +225,9 @@
<version>3.9.9</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<scope>compile</scope>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
<dependency>
@ -240,7 +245,7 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
<version>2.13.4.2</version>
</dependency>
<dependency>
<!-- This is required in case of Tomcat, do not remove -->
@ -291,15 +296,10 @@
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.5</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.6.1</version>
<version>2.7.1</version>
</dependency>
</dependencies>
@ -343,7 +343,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.5</version>
<version>8.0.31</version>
</dependency>
</dependencies>
<executions>
@ -498,13 +498,13 @@
</configuration>
</execution>
<!-- Confirm why there is a NPE -->
<!-- <execution>-->
<!-- <id>default-report-integration</id>-->
<!-- <phase>verify</phase>-->
<!-- <goals>-->
<!-- <goal>report-integration</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- <execution>-->
<!-- <id>default-report-integration</id>-->
<!-- <phase>verify</phase>-->
<!-- <goals>-->
<!-- <goal>report-integration</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<execution>
<id>default-report</id>
<phase>verify</phase>
@ -559,7 +559,9 @@
<daemon>true</daemon>
<waitForChild>false</waitForChild>
<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>
</execution>
<execution>

View File

@ -19,6 +19,7 @@
package com.wisemapping.dao;
import com.wisemapping.model.AccessAuditory;
import com.wisemapping.model.AuthenticationType;
import com.wisemapping.model.Collaboration;
import com.wisemapping.model.Collaborator;
import com.wisemapping.model.User;
@ -31,7 +32,6 @@ import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
@ -101,7 +101,11 @@ public class UserManagerImpl
@Override
public void createUser(User user) {
assert user != null : "Trying to store a null user";
user.setPassword(passwordEncoder.encode(user.getPassword()));
if (!AuthenticationType.GOOGLE_OAUTH2.equals(user.getAuthenticationType())) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
} else {
user.setPassword("");
}
getHibernateTemplate().saveOrUpdate(user);
}

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -83,13 +83,9 @@ public class Mindmap implements Serializable {
@Basic(fetch = FetchType.LAZY)
private byte[] zippedXml;
//~ Constructors .........................................................................................
public Mindmap() {
}
//~ Methods ..............................................................................................
public void setUnzipXml(@NotNull byte[] value) {
try {
final byte[] zip = ZipUtils.bytesToZip(value);
@ -146,7 +142,9 @@ public class Mindmap implements Serializable {
}
public void removedCollaboration(@NotNull Collaboration collaboration) {
collaborations.remove(collaboration);
// https://stackoverflow.com/questions/25125210/hibernate-persistentset-remove-operation-not-working
this.collaborations.remove(collaboration);
collaboration.setMindMap(null);
}
public void removedCollaboration(@NotNull Set<Collaboration> collaborations) {
@ -316,11 +314,20 @@ public class Mindmap implements Serializable {
final StringBuilder result = new StringBuilder();
result.append("<map version=\"tango\">");
result.append("<topic central=\"true\" text=\"");
result.append(StringEscapeUtils.escapeXml(title));
result.append(escapeXmlAttribute(title));
result.append("\"/></map>");
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() {
final Mindmap result = new Mindmap();
result.setDescription(this.getDescription());
@ -351,18 +358,6 @@ public class Mindmap implements Serializable {
return false;
}
@Nullable
public Label findLabel(int labelId) {
Label result = null;
for (Label label : this.labels) {
if (label.getId() == labelId) {
result = label;
break;
}
}
return result;
}
public void removeLabel(@NotNull final Label label) {
this.labels.remove(label);
}

View File

@ -18,15 +18,12 @@
package com.wisemapping.model;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "USER")
@ -39,21 +36,30 @@ public class User
private String lastname;
private String password;
private String locale;
@Column(name = "activation_code")
private long activationCode;
@Column(name = "activation_date")
private Calendar activationDate;
@Column(name = "allow_send_email")
private boolean allowSendEmail = false;
@Column(name = "authentication_type")
private Character authenticationTypeCode = AuthenticationType.DATABASE.getCode();
@Column(name = "authenticator_uri")
private String authenticatorUri;
@Column(name = "google_sync")
private Boolean googleSync;
@Column(name = "sync_code")
private String syncCode;
@Column(name = "google_token")
private String googleToken;
public User() {
}
@ -151,7 +157,35 @@ public class User
this.authenticatorUri = authenticatorUri;
}
@Override
public void setAuthenticationTypeCode(Character authenticationTypeCode) {
this.authenticationTypeCode = authenticationTypeCode;
}
public Boolean getGoogleSync() {
return googleSync;
}
public void setGoogleSync(Boolean googleSync) {
this.googleSync = googleSync;
}
public String getSyncCode() {
return syncCode;
}
public void setSyncCode(String syncCode) {
this.syncCode = syncCode;
}
public String getGoogleToken() {
return googleToken;
}
public void setGoogleToken(String googleToken) {
this.googleToken = googleToken;
}
@Override
public String toString() {
return "User{" +
"firstname='" + firstname + '\'' +

View File

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

View File

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

View File

@ -25,7 +25,8 @@ import com.wisemapping.security.Utils;
import com.wisemapping.service.*;
import com.wisemapping.validator.MapInfoValidator;
import org.apache.commons.validator.routines.EmailValidator;
import org.apache.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@ -46,7 +47,7 @@ import java.util.stream.Collectors;
@Controller
public class MindmapController extends BaseController {
final Logger logger = Logger.getLogger(MindmapController.class);
final Logger logger = LogManager.getLogger();
private static final String LATEST_HISTORY_REVISION = "latest";

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

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;
import org.apache.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.codec.Utf8;
@ -29,7 +31,7 @@ import java.security.NoSuchAlgorithmException;
public class LegacyPasswordEncoder implements PasswordEncoder {
final private static Logger logger = Logger.getLogger(LegacyPasswordEncoder.class);
final private static Logger logger = LogManager.getLogger();
public static final String ENC_PREFIX = "ENC:";
private final ShaPasswordEncoder sha1Encoder = new ShaPasswordEncoder();

View File

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

View File

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

View File

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

View File

@ -143,15 +143,13 @@ public class MindmapServiceImpl
public void removeCollaboration(@NotNull Mindmap mindmap, @NotNull Collaboration collaboration) throws CollaborationException {
// remove collaborator association
final Mindmap mindMap = collaboration.getMindMap();
final Set<Collaboration> collaborations = mindMap.getCollaborations();
final User creator = mindMap.getCreator();
if (creator.identityEquality(collaboration.getCollaborator())) {
throw new CollaborationException("User is the creator and must have ownership permissions.Creator Email:" + mindMap.getCreator().getEmail() + ",Collaborator:" + collaboration.getCollaborator().getEmail());
}
// When you delete an object from hibernate you have to delete it from *all* collections it exists in...
collaborations.remove(collaboration);
mindMap.removedCollaboration(collaboration);
mindmapManager.removeCollaboration(collaboration);
}
@ -249,7 +247,7 @@ public class MindmapServiceImpl
@Override
public void revertChange(@NotNull Mindmap mindmap, int historyId)
throws WiseMappingException, IOException {
throws WiseMappingException {
final MindMapHistory history = mindmapManager.getHistory(historyId);
mindmap.setZippedXml(history.getZippedXml());
updateMindmap(mindmap, true);

View File

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

View File

@ -20,6 +20,8 @@ package com.wisemapping.service;
import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.model.User;
import com.wisemapping.rest.model.RestResetPasswordResponse;
import org.jetbrains.annotations.NotNull;
public interface UserService {
@ -28,6 +30,10 @@ public interface UserService {
User createUser(@NotNull User user, boolean emailConfirmEnabled, boolean welcomeEmail) throws WiseMappingException;
User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException;
User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException;
void changePassword(@NotNull User user);
User getUserBy(String email);
@ -36,7 +42,7 @@ public interface UserService {
void updateUser(User user);
void resetPassword(@NotNull String email) throws InvalidUserEmailException, InvalidAuthSchemaException;
RestResetPasswordResponse resetPassword(@NotNull String email) throws InvalidUserEmailException, InvalidAuthSchemaException;
void removeUser(@NotNull User user);

View File

@ -23,6 +23,10 @@ import com.wisemapping.exceptions.InvalidMindmapException;
import com.wisemapping.exceptions.WiseMappingException;
import com.wisemapping.mail.NotificationService;
import com.wisemapping.model.*;
import com.wisemapping.rest.model.RestResetPasswordAction;
import com.wisemapping.rest.model.RestResetPasswordResponse;
import com.wisemapping.service.google.GoogleAccountBasicData;
import com.wisemapping.service.google.GoogleService;
import com.wisemapping.util.VelocityEngineUtils;
import com.wisemapping.util.VelocityEngineWrapper;
import org.jetbrains.annotations.NotNull;
@ -39,7 +43,7 @@ public class UserServiceImpl
private NotificationService notificationService;
private MessageSource messageSource;
private VelocityEngineWrapper velocityEngineWrapper;
private GoogleService googleService;
@Override
public void activateAccount(long code)
@ -56,10 +60,15 @@ public class UserServiceImpl
}
@Override
public void resetPassword(@NotNull String email)
public RestResetPasswordResponse resetPassword(@NotNull String email)
throws InvalidUserEmailException, InvalidAuthSchemaException {
final User user = userManager.getUserBy(email);
if (user != null) {
RestResetPasswordResponse response = new RestResetPasswordResponse();
if (user.getAuthenticationType().equals(AuthenticationType.GOOGLE_OAUTH2)) {
response.setAction(RestResetPasswordAction.OAUTH2_USER);
return response;
}
if (user.getAuthenticationType() != AuthenticationType.DATABASE) {
throw new InvalidAuthSchemaException("Could not change password for " + user.getAuthenticationType().getCode());
@ -72,6 +81,9 @@ public class UserServiceImpl
// Send an email with the new temporal password ...
notificationService.resetPassword(user, password);
response.setAction(RestResetPasswordAction.EMAIL_SENT);
return response;
} else {
throw new InvalidUserEmailException("The email '" + email + "' does not exists.");
}
@ -147,6 +159,55 @@ public class UserServiceImpl
return user;
}
@NotNull
public User createUserFromGoogle(@NotNull String callbackCode) throws WiseMappingException {
try {
GoogleAccountBasicData data = googleService.processCallback(callbackCode);
User existingUser = userManager.getUserBy(data.getEmail());
if (existingUser == null) {
User newUser = new User();
// new registrations from google starts synched
newUser.setGoogleSync(true);
newUser.setEmail(data.getEmail());
newUser.setFirstname(data.getName());
newUser.setLastname(data.getLastName());
newUser.setAuthenticationType(AuthenticationType.GOOGLE_OAUTH2);
newUser.setGoogleToken(data.getAccessToken());
existingUser = this.createUser(newUser, false, true);
} else {
// user exists and doesnt have confirmed account linking, I must wait for confirmation
if (existingUser.getGoogleSync() == null) {
existingUser.setGoogleSync(false);
existingUser.setSyncCode(callbackCode);
existingUser.setGoogleToken(data.getAccessToken());
userManager.updateUser(existingUser);
}
}
return existingUser;
} catch (Exception e) {
throw new WiseMappingException("Cant create user", e);
}
}
public User confirmAccountSync(@NotNull String email, @NotNull String code) throws WiseMappingException {
User existingUser = userManager.getUserBy(email);
// additional security check
if (existingUser == null || !existingUser.getSyncCode().equals(code)) {
throw new WiseMappingException("User not found / incorrect code");
}
existingUser.setGoogleSync(true);
existingUser.setSyncCode(null);
// user will not be able to login again with usr/pwd schema
existingUser.setAuthenticationType(AuthenticationType.GOOGLE_OAUTH2);
existingUser.setPassword("");
userManager.updateUser(existingUser);
return existingUser;
}
public Mindmap buildTutorialMindmap(@NotNull String firstName) throws InvalidMindmapException {
//To change body of created methods use File | Settings | File Templates.
final Locale locale = LocaleContextHolder.getLocale();
@ -209,7 +270,11 @@ public class UserServiceImpl
this.velocityEngineWrapper = velocityEngineWrapper;
}
@Override
public void setGoogleService(GoogleService googleService) {
this.googleService = googleService;
}
@Override
public User getCasUserBy(String uid) {
// TODO Auto-generated method stub
return null;

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");
}
@RequestMapping(value = "registration-google", method = RequestMethod.GET)
public ModelAndView processGoogleCallback() {
return new ModelAndView("registration-google");
}
@RequestMapping(value = "registration", method = RequestMethod.GET)
public ModelAndView showRegistrationPage() {
return new ModelAndView("registration");

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
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
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.GREETINGS=Hi
TOO_MANY_INACTIVE_ACCOUNTS=You have shared your mindmaps to more than 20 user that have not registered yet. Please, remove inactive accounts or ask them to register.
OWNER_ROLE_CAN_NOT_BE_CHANGED=Owner role can not be change. Please, remove owner from the change list.
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
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
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
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
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.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
#######################################################################################
# Coma separated list of domains and emails ban
#accounts.exclusion.domain=

View File

@ -8,6 +8,7 @@
<definition name="login" 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="mindmapList" template="/jsp/reactInclude.jsp"/>

View File

@ -11,7 +11,8 @@
<bean id="passwordEncoder" class="com.wisemapping.security.DefaultPasswordEncoderFactories" factory-method="createDelegatingPasswordEncoder"/>
<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-manager>
@ -19,4 +20,7 @@
<property name="userDetailsService" ref="userDetailsService"/>
<property name="encoder" ref="passwordEncoder"/>
</bean>
<bean id="googleAuthenticationProvider" class="com.wisemapping.security.GoogleAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</beans>

View File

@ -7,6 +7,7 @@
http://www.springframework.org/schema/security
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-provider ref="ldapAuthProvider"/>

View File

@ -3,12 +3,12 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
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/security
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"/>
</bean>
@ -34,6 +34,9 @@
<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/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/database/**" access="isAuthenticated() and hasRole('ROLE_ADMIN')"/>
@ -47,6 +50,7 @@
<sec:intercept-url pattern="/c/login" access="permitAll"/>
<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-google" access="permitAll"/>
<sec:intercept-url pattern="/c/forgot-password" 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"/>
</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">
<property name="userManager" ref="userManager"/>
<property name="mindmapService" ref="mindMapServiceTarget"/>
<property name="notificationService" ref="notificationService"/>
<property name="messageSource" ref="messageSource"/>
<property name="velocityEngineWrapper" ref="velocityEngineWrapper"/>
<property name="googleService" ref="googleService"/>
</bean>
<bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

View File

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

View File

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

View File

@ -9,15 +9,13 @@
<%--@elvariable id="lockInfo" type="com.wisemapping.service.LockInfo"--%>
<!DOCTYPE html>
<html lang="en">
<html lang="${fn:substring(locale,0,2)}">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta charset="utf-8" />
<base href="${requestScope['site.baseurl']}/static/webapp/">
<link rel="preconnect" href="https://fonts.googleapis.com" 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="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>
<%@ include file="/jsp/pageHeaders.jsf" %>
<title>Loading ... | WiseMapping</title>
@ -47,8 +45,8 @@
<noscript>You need to enable JavaScript to run this app.</noscript>
<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/app.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" defer></script>
</body>

View File

@ -5,17 +5,16 @@
<!DOCTYPE HTML>
<html>
<html lang="${fn:substring(locale,0,2)}">
<head>
<meta name="viewport" content="initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<base href="${requestScope['site.baseurl']}/static/mindplot/">
<link rel="preconnect" href="https://fonts.googleapis.com" 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="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="preload" href="../../css/viewonly.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<title>${mindmap.title} | <spring:message code="SITE.TITLE"/></title>
<link rel="stylesheet" href="../../css/viewonly.css"/>
<%@ include file="/jsp/pageHeaders.jsf" %>
<script type="text/javascript">
@ -40,23 +39,6 @@
</script>
</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>
body {
height: 100vh;
@ -76,37 +58,36 @@
</head>
<body>
<div id="root" class="mindplot-root">
<mindplot-component id="mindmap-comp"></mindplot-component>
<div id="mindplot-tooltips" className="wise-editor"></div>
<a href="${requestScope['site.homepage']}" target="new">
<div id="footerLogo"></div>
</a>
<div id="mapDetails">
<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>
</div>
<script type="text/javascript" src="${requestScope['site.static.js.url']}/mindplot/loader.js" crossorigin="anonymous"></script>
<div id="floating-panel">
<div id="zoom-button">
<button id="zoom-plus" title="Zoom Out">
<img src="../../images/add.svg" width="24" height="24"/>
</button>
<button id="zoom-minus" title="Zoom In">
<img src="../../images/minus.svg" width="24" height="24"/>
</button>
<div id="position">
<button id="position-button" title="Center">
<img src="../../images/center_focus.svg" width="24" height="24"/>
</button>
</div>
</div>
</div>
</div>
<mindplot-component id="mindmap-comp"/>
</div>
<div id="mindplot-tooltips" className="wise-editor"></div>
<a href="${requestScope['site.homepage']}" target="new" aria-label="WiseMapping Homepage">
<div id="footerLogo"></div>
</a>
<div id="mapDetails">
<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>
</div>
<script type="text/javascript" src="${requestScope['site.static.js.url']}/mindplot/loader.js" crossorigin="anonymous" defer></script>
<div id="floating-panel">
<div id="zoom-button">
<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" alt="<spring:message code="ZOOM_IN"/>"/>
</button>
<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" alt="<spring:message code="ZOOM_OUT"/>"/>
</button>
<div id="position">
<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" alt="<spring:message code="ZOOM_TO_FIT"/>"/>
</button>
</div>
</div>
</div>
<script type="text/javascript">
// 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" %>
<!DOCTYPE html>
<html lang="en">
<html lang="${fn:substring(locale,0,2)}">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta charset="utf-8" />
<base href="${requestScope['site.baseurl']}/static/webapp/">
<link rel="preconnect" href="https://fonts.googleapis.com" 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="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>
<%@ include file="/jsp/pageHeaders.jsf" %>
<title>Loading | WiseMapping</title>
<title>WiseMapping</title>
<script>
window.serverconfig = {
@ -20,7 +19,8 @@
analyticsAccount: '${requestScope['google.analytics.account']}',
clientType: 'rest',
recaptcha2Enabled: ${requestScope['google.recaptcha2.enabled']},
recaptcha2SiteKey: '${requestScope['google.recaptcha2.siteKey']}'
recaptcha2SiteKey: '${requestScope['google.recaptcha2.siteKey']}',
googleOauth2Url: '${requestScope['security.oauth2.google.url']}'
};
</script>
@ -34,7 +34,7 @@
element.async = true;
element.src = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js";
document.body.appendChild(element);
}, 2000);
}, 50);
};
window.addEventListener("load", downloadJsAtOnload, false);
@ -46,8 +46,8 @@
<noscript>You need to enable JavaScript to run this app.</noscript>
<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/app.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" async></script>
</body>
</html>