add java code
parent
6d84626599
commit
671cfdd486
@ -0,0 +1,127 @@
|
|||||||
|
buildscript {
|
||||||
|
ext {
|
||||||
|
versionSpringBoot = '1.5.10.RELEASE'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath group: 'org.springframework.boot', name: 'spring-boot-gradle-plugin', version: versionSpringBoot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'ru.ulstu'
|
||||||
|
version '0.1.0-SNAPSHOT'
|
||||||
|
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'org.springframework.boot'
|
||||||
|
apply plugin: 'io.spring.dependency-management'
|
||||||
|
apply plugin: 'checkstyle'
|
||||||
|
|
||||||
|
build.dependsOn checkstyleMain
|
||||||
|
bootRun.dependsOn checkstyleMain
|
||||||
|
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
targetCompatibility = 1.8
|
||||||
|
|
||||||
|
checkstyle {
|
||||||
|
|
||||||
|
project.ext.checkstyleVersion = '8.8'
|
||||||
|
project.ext.sevntuChecksVersion = '1.28.0'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
ignoreFailures = false
|
||||||
|
showViolations = true
|
||||||
|
maxErrors = 1
|
||||||
|
maxWarnings = 1
|
||||||
|
configFile = file("${project.rootDir}/checkstyle.xml")
|
||||||
|
|
||||||
|
//sourceSets = [sourceSets.main]
|
||||||
|
//showViolations = true
|
||||||
|
//reportsDir = file("$project.buildDir/checkstyleReports")
|
||||||
|
//configProperties = ['baseDir': "$project.projectDir"]
|
||||||
|
|
||||||
|
//https://discuss.gradle.org/t/some-checkstyle-rules-dont-work-in-gradle/16102/4
|
||||||
|
checkstyleMain {
|
||||||
|
source = sourceSets.main.allSource
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
checkstyle
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies{
|
||||||
|
assert project.hasProperty("checkstyleVersion")
|
||||||
|
|
||||||
|
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||||
|
checkstyle "com.github.sevntu-checkstyle:sevntu-checks:${sevntuChecksVersion}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task health(dependsOn: [
|
||||||
|
'checkstyleMain'
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
jar {
|
||||||
|
baseName = 'ng-tracker'
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
compile.exclude module: "spring-boot-starter-tomcat"
|
||||||
|
compile.exclude module: "bcmail-jdk14"
|
||||||
|
compile.exclude module: "bcprov-jdk14"
|
||||||
|
compile.exclude module: "bctsp-jdk14"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'
|
||||||
|
compile group: 'org.springframework.boot', name: 'spring-boot-starter-security'
|
||||||
|
compile group: 'org.springframework.boot', name: 'spring-boot-starter-aop'
|
||||||
|
compile group: 'org.springframework.boot', name: 'spring-boot-starter-mail'
|
||||||
|
compile group: 'org.springframework.boot', name: 'spring-boot-starter-jetty'
|
||||||
|
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa'
|
||||||
|
compile group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'
|
||||||
|
compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity4'
|
||||||
|
compile group: 'com.fasterxml.jackson.module', name: 'jackson-module-afterburner'
|
||||||
|
compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5'
|
||||||
|
|
||||||
|
compile group: 'postgresql', name: 'postgresql', version: '9.1-901.jdbc4'
|
||||||
|
|
||||||
|
compile group: 'org.liquibase', name: 'liquibase-core', version: '3.5.3'
|
||||||
|
compile group: 'com.mattbertolini', name: 'liquibase-slf4j', version: '2.0.0'
|
||||||
|
|
||||||
|
compile group: 'org.apache.poi', name: 'poi', version: '3.9'
|
||||||
|
compile group: 'org.apache.poi', name: 'poi-ooxml', version: '3.9'
|
||||||
|
|
||||||
|
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
|
||||||
|
|
||||||
|
compile group: 'com.lowagie', name: 'itext', version: '2.1.7'
|
||||||
|
|
||||||
|
compile group: 'org.webjars', name: 'bootstrap', version: '3.3.7-1'
|
||||||
|
compile group: 'org.webjars', name: 'bootstrap-select', version: '1.12.4'
|
||||||
|
compile group: 'org.webjars', name: 'jquery', version: '3.3.1-1'
|
||||||
|
compile group: 'org.webjars', name: 'font-awesome', version: '4.7.0'
|
||||||
|
compile group: 'org.webjars', name: 'jstree', version: '3.3.3'
|
||||||
|
|
||||||
|
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.5.0'
|
||||||
|
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.5.0'
|
||||||
|
|
||||||
|
testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test'
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||||
|
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Checkstyle configuration that checks the sun coding conventions from:
|
||||||
|
|
||||||
|
- the Java Language Specification at
|
||||||
|
http://java.sun.com/docs/books/jls/second_edition/html/index.html
|
||||||
|
|
||||||
|
- the Sun Code Conventions at http://java.sun.com/docs/codeconv/
|
||||||
|
|
||||||
|
- the Javadoc guidelines at
|
||||||
|
http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
|
||||||
|
|
||||||
|
- the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
|
||||||
|
|
||||||
|
- some best practices
|
||||||
|
|
||||||
|
Checkstyle is very configurable. Be sure to read the documentation at
|
||||||
|
http://checkstyle.sf.net (or in your downloaded distribution).
|
||||||
|
|
||||||
|
Most Checks are configurable, be sure to consult the documentation.
|
||||||
|
|
||||||
|
To completely disable a check, just comment it out or delete it from the file.
|
||||||
|
|
||||||
|
Finally, it is worth reading the documentation.
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name="Checker">
|
||||||
|
<!--
|
||||||
|
If you set the basedir property below, then all reported file
|
||||||
|
names will be relative to the specified directory. See
|
||||||
|
http://checkstyle.sourceforge.net/5.x/config.html#Checker
|
||||||
|
|
||||||
|
<property name="basedir" value="${basedir}"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<property name="fileExtensions" value="java, properties, xml"/>
|
||||||
|
|
||||||
|
<!-- Checks that a package-info.java file exists for each package. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage -->
|
||||||
|
<!--<module name="JavadocPackage"/>-->
|
||||||
|
|
||||||
|
<!-- Checks whether files end with a new line. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
|
||||||
|
<!--<module name="NewlineAtEndOfFile"/>-->
|
||||||
|
|
||||||
|
<!-- Checks that property files contain the same keys. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
|
||||||
|
<module name="Translation"/>
|
||||||
|
|
||||||
|
<!-- Checks for Size Violations. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_sizes.html -->
|
||||||
|
<module name="FileLength"/>
|
||||||
|
|
||||||
|
<!-- Checks for whitespace -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
||||||
|
<module name="FileTabCharacter"/>
|
||||||
|
|
||||||
|
<!-- Miscellaneous other checks. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_misc.html -->
|
||||||
|
<!--<module name="RegexpSingleline">
|
||||||
|
<property name="format" value="\s+$"/>
|
||||||
|
<property name="minimum" value="0"/>
|
||||||
|
<property name="maximum" value="0"/>
|
||||||
|
<property name="message" value="Line has trailing spaces."/>
|
||||||
|
</module>-->
|
||||||
|
|
||||||
|
<!-- Checks for Headers -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_header.html -->
|
||||||
|
<!-- <module name="Header"> -->
|
||||||
|
<!-- <property name="headerFile" value="${checkstyle.header.file}"/> -->
|
||||||
|
<!-- <property name="fileExtensions" value="java"/> -->
|
||||||
|
<!-- </module> -->
|
||||||
|
|
||||||
|
<module name="TreeWalker">
|
||||||
|
|
||||||
|
<!-- Checks for Javadoc comments. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
||||||
|
<!--<module name="JavadocMethod"/>-->
|
||||||
|
<!--<module name="JavadocType"/>-->
|
||||||
|
<!--<module name="JavadocVariable"/>-->
|
||||||
|
<!--<module name="JavadocStyle"/>-->
|
||||||
|
|
||||||
|
<!-- Checks for Naming Conventions. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_naming.html -->
|
||||||
|
<module name="ConstantName"/>
|
||||||
|
<module name="LocalFinalVariableName"/>
|
||||||
|
<module name="LocalVariableName"/>
|
||||||
|
<module name="MemberName"/>
|
||||||
|
<module name="MethodName"/>
|
||||||
|
<module name="PackageName"/>
|
||||||
|
<module name="ParameterName"/>
|
||||||
|
<module name="StaticVariableName"/>
|
||||||
|
<module name="TypeName"/>
|
||||||
|
|
||||||
|
<!-- Checks for imports -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_import.html -->
|
||||||
|
<!--<module name="AvoidStarImport"/>-->
|
||||||
|
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
|
||||||
|
<module name="RedundantImport"/>
|
||||||
|
<!--module name="UnusedImports">
|
||||||
|
<property name="processJavadoc" value="false"/>
|
||||||
|
</module-->
|
||||||
|
|
||||||
|
<!-- Checks for Size Violations. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_sizes.html -->
|
||||||
|
<!--<module name="LineLength"/>-->
|
||||||
|
<module name="MethodLength"/>
|
||||||
|
<!--<module name="ParameterNumber"/>-->
|
||||||
|
|
||||||
|
<!-- Checks for whitespace -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
||||||
|
<module name="EmptyForIteratorPad"/>
|
||||||
|
<!--<module name="GenericWhitespace"/>-->
|
||||||
|
<!--<module name="MethodParamPad"/>-->
|
||||||
|
<!--<module name="NoWhitespaceAfter"/>-->
|
||||||
|
<module name="NoWhitespaceBefore"/>
|
||||||
|
<!--<module name="OperatorWrap"/>-->
|
||||||
|
<module name="ParenPad"/>
|
||||||
|
<module name="TypecastParenPad"/>
|
||||||
|
<!--<module name="WhitespaceAfter"/>-->
|
||||||
|
<module name="WhitespaceAround"/>
|
||||||
|
|
||||||
|
<!-- Modifier Checks -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_modifiers.html -->
|
||||||
|
<!--<module name="ModifierOrder"/>-->
|
||||||
|
<module name="RedundantModifier"/>
|
||||||
|
|
||||||
|
<!-- Checks for blocks. You know, those {}'s -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_blocks.html -->
|
||||||
|
<module name="AvoidNestedBlocks"/>
|
||||||
|
<module name="EmptyBlock"/>
|
||||||
|
<module name="LeftCurly"/>
|
||||||
|
<!--<module name="NeedBraces"/>-->
|
||||||
|
<module name="RightCurly"/>
|
||||||
|
|
||||||
|
<!-- Checks for common coding problems -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_coding.html -->
|
||||||
|
<!--<module name="AvoidInlineConditionals"/>-->
|
||||||
|
<module name="EmptyStatement"/>
|
||||||
|
<module name="EqualsHashCode"/>
|
||||||
|
<!--<module name="HiddenField"/>-->
|
||||||
|
<module name="IllegalInstantiation"/>
|
||||||
|
<module name="InnerAssignment"/>
|
||||||
|
<!--<module name="MagicNumber"/>-->
|
||||||
|
<!--<module name="MissingSwitchDefault"/>-->
|
||||||
|
<module name="SimplifyBooleanExpression"/>
|
||||||
|
<module name="SimplifyBooleanReturn"/>
|
||||||
|
|
||||||
|
<!-- Checks for class design -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_design.html -->
|
||||||
|
<!--<module name="DesignForExtension"/>-->
|
||||||
|
<!--<module name="FinalClass"/>-->
|
||||||
|
<!--<module name="HideUtilityClassConstructor"/>-->
|
||||||
|
<!--<module name="InterfaceIsType"/>-->
|
||||||
|
<!--<module name="VisibilityModifier"/>-->
|
||||||
|
|
||||||
|
<!-- Miscellaneous other checks. -->
|
||||||
|
<!-- See http://checkstyle.sf.net/config_misc.html -->
|
||||||
|
<module name="ArrayTypeStyle"/>
|
||||||
|
<!--<module name="FinalParameters"/>-->
|
||||||
|
<!--<module name="TodoComment"/>-->
|
||||||
|
<!--<module name="UpperEll"/>-->
|
||||||
|
|
||||||
|
</module>
|
||||||
|
|
||||||
|
</module>
|
Binary file not shown.
@ -0,0 +1,6 @@
|
|||||||
|
#Tue Feb 06 11:48:53 SAMT 2018
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-bin.zip
|
@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
|
function splitJvmOpts() {
|
||||||
|
JVM_OPTS=("$@")
|
||||||
|
}
|
||||||
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
@ -0,0 +1,84 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
@ -0,0 +1,14 @@
|
|||||||
|
package ru.ulstu;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import ru.ulstu.core.repository.JpaDetachableRepositoryImpl;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableJpaRepositories(repositoryBaseClass = JpaDetachableRepositoryImpl.class)
|
||||||
|
public class NgTrackerApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(NgTrackerApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package ru.ulstu.commit.controller;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
import ru.ulstu.core.model.response.PageableItems;
|
||||||
|
import ru.ulstu.commit.model.CommitListDto;
|
||||||
|
import ru.ulstu.commit.service.CommitService;
|
||||||
|
import ru.ulstu.core.model.response.Response;
|
||||||
|
import ru.ulstu.odin.controller.OdinController;
|
||||||
|
import ru.ulstu.odin.model.OdinVoid;
|
||||||
|
|
||||||
|
import static ru.ulstu.commit.controller.CommitController.URL;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(URL)
|
||||||
|
public class CommitController extends OdinController<CommitListDto, OdinVoid> {
|
||||||
|
public static final String URL = Constants.API_1_0 + "commits";
|
||||||
|
private final CommitService commitService;
|
||||||
|
|
||||||
|
public CommitController(CommitService commitService) {
|
||||||
|
super(CommitListDto.class);
|
||||||
|
this.commitService = commitService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("")
|
||||||
|
public Response<PageableItems<CommitListDto>> getCommits(@RequestParam(value = "offset", defaultValue = "0") int offset,
|
||||||
|
@RequestParam(value = "count", defaultValue = "10") int count) {
|
||||||
|
return new Response<>(commitService.getCommits(offset, count));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package ru.ulstu.commit.model;
|
||||||
|
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinDate;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class CommitListDto {
|
||||||
|
private final static String DELIMITER = ";";
|
||||||
|
@OdinCaption("Дата")
|
||||||
|
@OdinDate(type = OdinDate.OdinDateType.DATETIME)
|
||||||
|
private final Date date;
|
||||||
|
@OdinCaption("Пользователь")
|
||||||
|
private final String userName;
|
||||||
|
@OdinCaption("Сообщение")
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
public CommitListDto(String data) {
|
||||||
|
List<String> datas = Arrays.stream(data.split(DELIMITER))
|
||||||
|
.filter(d -> d != null && !d.isEmpty())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (datas.size() > 2) {
|
||||||
|
this.userName = datas.get(0);
|
||||||
|
this.message = datas.get(2);
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd hh:mm:ss yyyy Z", Locale.US);
|
||||||
|
try {
|
||||||
|
this.date = format.parse(datas.get(1));
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new RuntimeException("wrong commits date");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("wrong commits data");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package ru.ulstu.commit.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.commit.model.CommitListDto;
|
||||||
|
import ru.ulstu.core.model.response.PageableItems;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CommitService {
|
||||||
|
private static final String COMMITS_FILE_NAME = "/commits.log";
|
||||||
|
|
||||||
|
private final List<CommitListDto> commits;
|
||||||
|
|
||||||
|
public CommitService() {
|
||||||
|
commits = getCommits();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageableItems<CommitListDto> getCommits(int offset, int count) {
|
||||||
|
return new PageableItems<>(commits.size(), commits.stream()
|
||||||
|
.skip(offset)
|
||||||
|
.limit(count)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<CommitListDto> getCommits() {
|
||||||
|
return getFileContent()
|
||||||
|
.stream()
|
||||||
|
.map(CommitListDto::new)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.sorted((c1, c2) -> c2.getDate().compareTo(c1.getDate()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getFileContent() {
|
||||||
|
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
|
||||||
|
//Get file from resources folder
|
||||||
|
InputStream is = getClass().getResourceAsStream(COMMITS_FILE_NAME);
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||||
|
String line;
|
||||||
|
try {
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
result.add(line);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Collections.reverse(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import org.hibernate.validator.constraints.NotBlank;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "ng-tracker")
|
||||||
|
@Validated
|
||||||
|
public class ApplicationProperties {
|
||||||
|
@NotBlank
|
||||||
|
private String baseUrl;
|
||||||
|
@NotBlank
|
||||||
|
private String undeadUserLogin;
|
||||||
|
private boolean devMode;
|
||||||
|
|
||||||
|
public String getBaseUrl() {
|
||||||
|
return baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBaseUrl(String baseUrl) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUndeadUserLogin() {
|
||||||
|
return undeadUserLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUndeadUserLogin(String undeadUserLogin) {
|
||||||
|
this.undeadUserLogin = undeadUserLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDevMode() {
|
||||||
|
return devMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDevMode(boolean devMode) {
|
||||||
|
this.devMode = devMode;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||||
|
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableAsync
|
||||||
|
@EnableScheduling
|
||||||
|
public class AsyncConfiguration implements AsyncConfigurer {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Bean(name = "taskExecutor")
|
||||||
|
public Executor getAsyncExecutor() {
|
||||||
|
log.debug("Creating Async Task Executor");
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setCorePoolSize(2);
|
||||||
|
executor.setMaxPoolSize(2);
|
||||||
|
executor.setQueueCapacity(500);
|
||||||
|
executor.setThreadNamePrefix("balance-executor-");
|
||||||
|
executor.initialize();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
||||||
|
return new SimpleAsyncUncaughtExceptionHandler();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
public class Constants {
|
||||||
|
public static final String API_1_0 = "/api/1.0/";
|
||||||
|
|
||||||
|
public static final String MAIL_ACTIVATE = "Account activation";
|
||||||
|
public static final String MAIL_RESET = "Password reset";
|
||||||
|
public static final int MIN_PASSWORD_LENGTH = 6;
|
||||||
|
|
||||||
|
public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$";
|
||||||
|
|
||||||
|
public static final String COOKIES_NAME = "JSESSIONID";
|
||||||
|
public static final String LOGOUT_URL = "/login?logout";
|
||||||
|
public static final String SESSION_ID_ATTR = "sessionId";
|
||||||
|
public static final int SESSION_TIMEOUT_SECONDS = 30 * 60;
|
||||||
|
|
||||||
|
public static final String PASSWORD_RESET_REQUEST_PAGE = "/resetRequest";
|
||||||
|
public static final String PASSWORD_RESET_PAGE = "/reset";
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class ControllersConfiguration {
|
||||||
|
@Bean
|
||||||
|
public CorsFilter corsFilter() {
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
|
config.setAllowCredentials(true);
|
||||||
|
config.addAllowedOrigin("*");
|
||||||
|
config.addAllowedHeader("*");
|
||||||
|
config.addAllowedMethod("GET");
|
||||||
|
config.addAllowedMethod("POST");
|
||||||
|
config.addAllowedMethod("DELETE");
|
||||||
|
source.registerCorsConfiguration("/**", config);
|
||||||
|
return new CorsFilter(source);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
|
||||||
|
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||||
|
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
|
||||||
|
import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class HttpListenerConfiguration implements EmbeddedServletContainerCustomizer {
|
||||||
|
@Value("${server.http.port}")
|
||||||
|
private int httpPort;
|
||||||
|
|
||||||
|
private void configureJetty(JettyEmbeddedServletContainerFactory jettyFactory) {
|
||||||
|
jettyFactory.addServerCustomizers((JettyServerCustomizer) server -> {
|
||||||
|
ServerConnector serverConnector = new ServerConnector(server);
|
||||||
|
serverConnector.setPort(httpPort);
|
||||||
|
server.addConnector(serverConnector);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(ConfigurableEmbeddedServletContainer container) {
|
||||||
|
if (container instanceof JettyEmbeddedServletContainerFactory) {
|
||||||
|
configureJetty((JettyEmbeddedServletContainerFactory) container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
|
||||||
|
import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class JacksonConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Hibernate5Module hibernate5Module() {
|
||||||
|
return new Hibernate5Module();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AfterburnerModule afterburnerModule() {
|
||||||
|
return new AfterburnerModule();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class MailTemplateConfiguration {
|
||||||
|
@Bean
|
||||||
|
public ClassLoaderTemplateResolver emailTemplateResolver() {
|
||||||
|
ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
|
||||||
|
emailTemplateResolver.setPrefix("mail_templates/");
|
||||||
|
emailTemplateResolver.setSuffix(".html");
|
||||||
|
emailTemplateResolver.setTemplateMode("HTML5");
|
||||||
|
emailTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||||
|
emailTemplateResolver.setOrder(1);
|
||||||
|
return emailTemplateResolver;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class MvcConfiguration extends WebMvcConfigurerAdapter {
|
||||||
|
@Override
|
||||||
|
public void addViewControllers(ViewControllerRegistry registry) {
|
||||||
|
registry.addViewController("/{articlename:\\w+}");
|
||||||
|
//registry.addViewController("/admin/{articlename:\\w+}");
|
||||||
|
registry.addRedirectViewController("/", "/index");
|
||||||
|
registry.addRedirectViewController("/default", "/index");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
|
registry
|
||||||
|
.addResourceHandler("/webjars/**")
|
||||||
|
.addResourceLocations("/webjars/");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class PasswordEncoderConfiguration {
|
||||||
|
@Bean
|
||||||
|
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.BeanInitializationException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
|
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||||
|
import ru.ulstu.user.controller.UserController;
|
||||||
|
import ru.ulstu.user.model.UserRoleConstants;
|
||||||
|
import ru.ulstu.user.service.UserService;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||||
|
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||||
|
|
||||||
|
@Value("${server.http.port}")
|
||||||
|
private int httpPort;
|
||||||
|
@Value("${server.port}")
|
||||||
|
private int httpsPort;
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
private final BCryptPasswordEncoder bCryptPasswordEncoder;
|
||||||
|
private final AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||||
|
private final LogoutSuccessHandler logoutSuccessHandler;
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
public SecurityConfiguration(UserService userService,
|
||||||
|
BCryptPasswordEncoder bCryptPasswordEncoder,
|
||||||
|
AuthenticationSuccessHandler authenticationSuccessHandler,
|
||||||
|
LogoutSuccessHandler logoutSuccessHandler,
|
||||||
|
ApplicationProperties applicationProperties) {
|
||||||
|
this.userService = userService;
|
||||||
|
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
|
||||||
|
this.authenticationSuccessHandler = authenticationSuccessHandler;
|
||||||
|
this.logoutSuccessHandler = logoutSuccessHandler;
|
||||||
|
this.applicationProperties = applicationProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http.csrf()
|
||||||
|
.disable();
|
||||||
|
if (applicationProperties.isDevMode()) {
|
||||||
|
log.debug("Security disabled");
|
||||||
|
http.authorizeRequests()
|
||||||
|
.anyRequest()
|
||||||
|
.permitAll();
|
||||||
|
http.anonymous()
|
||||||
|
.principal("developer")
|
||||||
|
.authorities(UserRoleConstants.ADMIN);
|
||||||
|
} else {
|
||||||
|
log.debug("Security enabled");
|
||||||
|
http.authorizeRequests()
|
||||||
|
.antMatchers(UserController.ACTIVATE_URL).permitAll()
|
||||||
|
.antMatchers(Constants.PASSWORD_RESET_REQUEST_PAGE).permitAll()
|
||||||
|
.antMatchers(Constants.PASSWORD_RESET_PAGE).permitAll()
|
||||||
|
.antMatchers(UserController.URL + UserController.REGISTER_URL).permitAll()
|
||||||
|
.antMatchers(UserController.URL + UserController.ACTIVATE_URL).permitAll()
|
||||||
|
.antMatchers(UserController.URL + UserController.PASSWORD_RESET_REQUEST_URL).permitAll()
|
||||||
|
.antMatchers(UserController.URL + UserController.PASSWORD_RESET_URL).permitAll()
|
||||||
|
.antMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN)
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.and()
|
||||||
|
.formLogin()
|
||||||
|
.loginPage("/login")
|
||||||
|
.successHandler(authenticationSuccessHandler)
|
||||||
|
.permitAll()
|
||||||
|
.and()
|
||||||
|
.logout()
|
||||||
|
.logoutSuccessHandler(logoutSuccessHandler)
|
||||||
|
.logoutSuccessUrl(Constants.LOGOUT_URL)
|
||||||
|
.invalidateHttpSession(false)
|
||||||
|
.clearAuthentication(true)
|
||||||
|
.deleteCookies(Constants.COOKIES_NAME)
|
||||||
|
.permitAll();
|
||||||
|
}
|
||||||
|
http.portMapper()
|
||||||
|
.http(httpPort)
|
||||||
|
.mapsTo(httpsPort)
|
||||||
|
.and()
|
||||||
|
.requiresChannel()
|
||||||
|
.anyRequest()
|
||||||
|
.requiresSecure();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(WebSecurity web) {
|
||||||
|
web.ignoring()
|
||||||
|
.antMatchers("/css/**")
|
||||||
|
.antMatchers("/js/**")
|
||||||
|
.antMatchers("/templates/**")
|
||||||
|
.antMatchers("/webjars/**");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||||
|
if (applicationProperties.isDevMode()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new BeanInitializationException("Security configuration failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicates;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import springfox.documentation.builders.PathSelectors;
|
||||||
|
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||||
|
import springfox.documentation.spi.DocumentationType;
|
||||||
|
import springfox.documentation.spring.web.plugins.Docket;
|
||||||
|
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableSwagger2
|
||||||
|
public class SwaggerConfiguration {
|
||||||
|
@Bean
|
||||||
|
public Docket swaggerApi() {
|
||||||
|
return new Docket(DocumentationType.SWAGGER_2)
|
||||||
|
.select()
|
||||||
|
.apis(RequestHandlerSelectors.any())
|
||||||
|
.paths(Predicates.not(PathSelectors.regex("/error")))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package ru.ulstu.core.controller;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.validation.FieldError;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import ru.ulstu.core.error.EntityIdIsNullException;
|
||||||
|
import ru.ulstu.core.model.ErrorConstants;
|
||||||
|
import ru.ulstu.core.model.response.Response;
|
||||||
|
import ru.ulstu.core.model.response.ResponseExtended;
|
||||||
|
import ru.ulstu.user.error.*;
|
||||||
|
import ru.ulstu.user.error.*;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@ControllerAdvice
|
||||||
|
public class AdviceController {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(AdviceController.class);
|
||||||
|
|
||||||
|
private Response<Void> handleException(ErrorConstants error) {
|
||||||
|
log.warn(error.toString());
|
||||||
|
return new Response<>(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <E> ResponseExtended<E> handleException(ErrorConstants error, E errorData) {
|
||||||
|
log.warn(error.toString());
|
||||||
|
return new ResponseExtended<>(error, errorData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(EntityIdIsNullException.class)
|
||||||
|
public Response<Void> handleEntityIdIsNullException(Throwable e) {
|
||||||
|
return handleException(ErrorConstants.ID_IS_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public ResponseExtended<Set<String>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
|
||||||
|
final Set<String> errors = e.getBindingResult().getAllErrors().stream()
|
||||||
|
.filter(error -> error instanceof FieldError)
|
||||||
|
.map(error -> ((FieldError) error).getField())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
return handleException(ErrorConstants.VALIDATION_ERROR, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(UserIdExistsException.class)
|
||||||
|
public Response<Void> handleUserIdExistsException(Throwable e) {
|
||||||
|
return handleException(ErrorConstants.USER_ID_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(UserActivationError.class)
|
||||||
|
public ResponseExtended<String> handleUserActivationError(Throwable e) {
|
||||||
|
return handleException(ErrorConstants.USER_ACTIVATION_ERROR, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(UserLoginExistsException.class)
|
||||||
|
public ResponseExtended<String> handleUserLoginExistsException(Throwable e) {
|
||||||
|
return handleException(ErrorConstants.USER_LOGIN_EXISTS, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(UserEmailExistsException.class)
|
||||||
|
public ResponseExtended<String> handleUserEmailExistsException(Throwable e) {
|
||||||
|
return handleException(ErrorConstants.USER_EMAIL_EXISTS, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(UserPasswordsNotValidOrNotMatchException.class)
|
||||||
|
public Response<Void> handleUserPasswordsNotValidOrNotMatchException(Throwable e) {
|
||||||
|
return handleException(ErrorConstants.USER_PASSWORDS_NOT_VALID_OR_NOT_MATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(UserNotFoundException.class)
|
||||||
|
public ResponseExtended<String> handleUserNotFoundException(Throwable e) {
|
||||||
|
return handleException(ErrorConstants.USER_NOT_FOUND, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(UserNotActivatedException.class)
|
||||||
|
public Response<Void> handleUserNotActivatedException(Throwable e) {
|
||||||
|
return handleException(ErrorConstants.USER_NOT_ACTIVATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(UserResetKeyError.class)
|
||||||
|
public ResponseExtended<String> handleUserResetKeyError(Throwable e) {
|
||||||
|
return handleException(ErrorConstants.USER_RESET_ERROR, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(UserIsUndeadException.class)
|
||||||
|
public ResponseExtended<String> handleUserIsUndeadException(Throwable e) {
|
||||||
|
return handleException(ErrorConstants.USER_UNDEAD_ERROR, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseExtended<String> handleUnknownException(Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return handleException(ErrorConstants.UNKNOWN, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package ru.ulstu.core.error;
|
||||||
|
|
||||||
|
public class EntityIdIsNullException extends RuntimeException {
|
||||||
|
public EntityIdIsNullException() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.core.error;
|
||||||
|
|
||||||
|
public class OdinException extends RuntimeException {
|
||||||
|
public OdinException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.core.error;
|
||||||
|
|
||||||
|
public class XlsLoadException extends Exception {
|
||||||
|
public XlsLoadException(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.core.error;
|
||||||
|
|
||||||
|
public class XlsParseException extends Exception {
|
||||||
|
public XlsParseException(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
package ru.ulstu.core.jpa;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class OffsetablePageRequest implements Pageable, Serializable {
|
||||||
|
private final int offset;
|
||||||
|
private final int count;
|
||||||
|
private final Sort sort;
|
||||||
|
|
||||||
|
public OffsetablePageRequest(int offset, int count) {
|
||||||
|
this(offset, count, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OffsetablePageRequest(int offset, int count, Sort.Direction direction, String... properties) {
|
||||||
|
this(offset, count, new Sort(direction, properties));
|
||||||
|
}
|
||||||
|
|
||||||
|
public OffsetablePageRequest(int offset, int count, Sort sort) {
|
||||||
|
if (offset < 0) {
|
||||||
|
throw new IllegalArgumentException("Offset value must not be less than zero!");
|
||||||
|
}
|
||||||
|
if (count < 1) {
|
||||||
|
throw new IllegalArgumentException("Count value must not be less than one!");
|
||||||
|
}
|
||||||
|
this.offset = offset;
|
||||||
|
this.count = count;
|
||||||
|
this.sort = sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Sort getSort() {
|
||||||
|
return sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPageSize() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPageNumber() {
|
||||||
|
return offset / count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOffset() {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPrevious() {
|
||||||
|
return offset > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pageable next() {
|
||||||
|
return new OffsetablePageRequest(getOffset() + getPageSize(), getPageSize(), getSort());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pageable previousOrFirst() {
|
||||||
|
return hasPrevious() ? previous() : first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pageable previous() {
|
||||||
|
return getOffset() == 0 ? this : new OffsetablePageRequest(getOffset() - getPageSize(), getPageSize(), getSort());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pageable first() {
|
||||||
|
return new OffsetablePageRequest(0, getPageSize(), getSort());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final OffsetablePageRequest other = (OffsetablePageRequest) obj;
|
||||||
|
return this.offset == other.offset && this.count == other.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + offset;
|
||||||
|
result = prime * result + count;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package ru.ulstu.core.model;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@MappedSuperclass
|
||||||
|
public abstract class BaseEntity implements Serializable, Comparable {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.TABLE)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private Integer version;
|
||||||
|
|
||||||
|
public BaseEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseEntity(Integer id, Integer version) {
|
||||||
|
this.id = id;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!getClass().isAssignableFrom(obj.getClass())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BaseEntity other = (BaseEntity) obj;
|
||||||
|
if (id == null) {
|
||||||
|
if (other.id != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!id.equals(other.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (id == null ? 0 : id.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "{" +
|
||||||
|
"id=" + id +
|
||||||
|
", version=" + version +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Object o) {
|
||||||
|
return id != null ? id.compareTo(((BaseEntity) o).getId()) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.id = null;
|
||||||
|
this.version = null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package ru.ulstu.core.model;
|
||||||
|
|
||||||
|
public enum ErrorConstants {
|
||||||
|
UNKNOWN(0, "Unknown error"),
|
||||||
|
ID_IS_NULL(1, "Id of entity has null value"),
|
||||||
|
VALIDATION_ERROR(2, "Validation error"),
|
||||||
|
USER_ID_EXISTS(100, "New user can't have id"),
|
||||||
|
USER_ACTIVATION_ERROR(101, "Invalid activation key"),
|
||||||
|
USER_EMAIL_EXISTS(102, "User with same email already exists"),
|
||||||
|
USER_LOGIN_EXISTS(103, "User with same login already exists"),
|
||||||
|
USER_PASSWORDS_NOT_VALID_OR_NOT_MATCH(104, "User passwords is not valid or not match"),
|
||||||
|
USER_NOT_FOUND(105, "User is not found"),
|
||||||
|
USER_NOT_ACTIVATED(106, "User is not activated"),
|
||||||
|
USER_RESET_ERROR(107, "Invalid reset key"),
|
||||||
|
USER_UNDEAD_ERROR(108, "Can't edit/delete that user"),
|
||||||
|
FILE_UPLOAD_ERROR(110, "File upload error");
|
||||||
|
|
||||||
|
private int code;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
ErrorConstants(int code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%d: %s", code, message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package ru.ulstu.core.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TreeDto {
|
||||||
|
private Integer id;
|
||||||
|
private String text;
|
||||||
|
private List<TreeDto> children = new ArrayList<>();
|
||||||
|
|
||||||
|
public TreeDto() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends TreeEntity> TreeDto(TreeEntity item) {
|
||||||
|
this.text = item.toString();
|
||||||
|
this.id = item.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TreeDto(String rootName) {
|
||||||
|
this.text = rootName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TreeDto> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package ru.ulstu.core.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface TreeEntity<T> {
|
||||||
|
|
||||||
|
Integer getId();
|
||||||
|
|
||||||
|
List<T> getChildren();
|
||||||
|
|
||||||
|
void setChildren(List<T> children);
|
||||||
|
|
||||||
|
T getParent();
|
||||||
|
|
||||||
|
void setParent(T parent);
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package ru.ulstu.core.model.response;
|
||||||
|
|
||||||
|
class ControllerResponse<D, E> {
|
||||||
|
private D data;
|
||||||
|
private ControllerResponseError<E> error;
|
||||||
|
|
||||||
|
ControllerResponse(D data) {
|
||||||
|
this.data = data;
|
||||||
|
this.error = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerResponse(ControllerResponseError<E> error) {
|
||||||
|
this.data = null;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public D getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ControllerResponseError<E> getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package ru.ulstu.core.model.response;
|
||||||
|
|
||||||
|
import ru.ulstu.core.model.ErrorConstants;
|
||||||
|
|
||||||
|
class ControllerResponseError<D> {
|
||||||
|
private ErrorConstants description;
|
||||||
|
private D data;
|
||||||
|
|
||||||
|
ControllerResponseError(ErrorConstants description, D data) {
|
||||||
|
this.description = description;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return description.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return description.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public D getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package ru.ulstu.core.model.response;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class PageableItems<T> {
|
||||||
|
private final long count;
|
||||||
|
private final Collection<T> items;
|
||||||
|
|
||||||
|
public PageableItems(long count, Collection<T> items) {
|
||||||
|
this.count = count;
|
||||||
|
this.items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<T> getItems() {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package ru.ulstu.core.model.response;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import ru.ulstu.core.model.ErrorConstants;
|
||||||
|
|
||||||
|
public class Response<D> extends ResponseEntity<Object> {
|
||||||
|
|
||||||
|
public Response(D data) {
|
||||||
|
super(new ControllerResponse<D, Void>(data), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response(ErrorConstants error) {
|
||||||
|
super(new ControllerResponse<Void, Void>(new ControllerResponseError<>(error, null)), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package ru.ulstu.core.model.response;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import ru.ulstu.core.model.ErrorConstants;
|
||||||
|
|
||||||
|
public class ResponseExtended<E> extends ResponseEntity<Object> {
|
||||||
|
|
||||||
|
public ResponseExtended(ErrorConstants error, E errorData) {
|
||||||
|
super(new ControllerResponse<Void, E>(new ControllerResponseError<E>(error, errorData)), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package ru.ulstu.core.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.repository.NoRepositoryBean;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@NoRepositoryBean
|
||||||
|
public interface JpaDetachableRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
|
||||||
|
void detach(T t);
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package ru.ulstu.core.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
|
||||||
|
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class JpaDetachableRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
|
||||||
|
implements JpaDetachableRepository<T, ID> {
|
||||||
|
private EntityManager entityManager;
|
||||||
|
|
||||||
|
public JpaDetachableRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
|
||||||
|
super(entityInformation, entityManager);
|
||||||
|
this.entityManager = entityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void detach(T t) {
|
||||||
|
entityManager.detach(t);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package ru.ulstu.core.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.core.model.TreeDto;
|
||||||
|
import ru.ulstu.core.model.TreeEntity;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class TreeService<T extends TreeEntity> {
|
||||||
|
public TreeDto getTree(String rootName, List<T> rootItems) {
|
||||||
|
return addChildNode(new TreeDto(rootName), rootItems, element -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TreeDto getTree(String rootName, List<T> rootItems, Predicate<T> filterPredicate) {
|
||||||
|
return addChildNode(new TreeDto(rootName), rootItems, filterPredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeDto addChildNode(TreeDto currentRoot, List<T> children, Predicate<T> filterPredicate) {
|
||||||
|
if (children != null) {
|
||||||
|
children.stream()
|
||||||
|
.filter(filterPredicate)
|
||||||
|
.forEach(item -> {
|
||||||
|
TreeDto newNode = new TreeDto(item);
|
||||||
|
currentRoot.getChildren().add(addChildNode(newNode, item.getChildren(), filterPredicate));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return currentRoot;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,207 @@
|
|||||||
|
package ru.ulstu.core.service;
|
||||||
|
|
||||||
|
import org.apache.poi.POIXMLDocument;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||||
|
import org.apache.poi.ss.usermodel.*;
|
||||||
|
import org.apache.poi.ss.util.CellRangeAddress;
|
||||||
|
import org.apache.poi.ss.util.RegionUtil;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
import ru.ulstu.core.error.XlsParseException;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class XlsDocumentBuilder {
|
||||||
|
private static final int DEFAULT_SHEET_NUM = 0;
|
||||||
|
private File documentFile;
|
||||||
|
private Workbook workBook;
|
||||||
|
private Sheet currentSheet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for reading and writing data from/to *.[xls|xlsx] document
|
||||||
|
*
|
||||||
|
* @param file contains existing document for reading or new document to save
|
||||||
|
*/
|
||||||
|
public XlsDocumentBuilder(File file) throws IOException, XlsParseException {
|
||||||
|
this.documentFile = file;
|
||||||
|
if (file.exists()) {
|
||||||
|
workBook = getWorkBook(file);
|
||||||
|
currentSheet = workBook.getSheetAt(DEFAULT_SHEET_NUM);
|
||||||
|
} else {
|
||||||
|
workBook = new XSSFWorkbook();
|
||||||
|
currentSheet = workBook.createSheet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Workbook getWorkBook(File file) throws XlsParseException, IOException {
|
||||||
|
InputStream inputStream = new PushbackInputStream(new FileInputStream(file), 4096);
|
||||||
|
if (isXlsx(inputStream)) {
|
||||||
|
return new XSSFWorkbook(inputStream);
|
||||||
|
} else if (isExcel(inputStream)) {
|
||||||
|
return new HSSFWorkbook(inputStream);
|
||||||
|
}
|
||||||
|
throw new XlsParseException("Wrong document format");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change active sheet to write or read data
|
||||||
|
*
|
||||||
|
* @param index index of sheet to activate
|
||||||
|
*/
|
||||||
|
public XlsDocumentBuilder setActiveSheet(int index) {
|
||||||
|
workBook.setActiveSheet(index);
|
||||||
|
currentSheet = workBook.getSheetAt(index);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new sheet in document and set it active
|
||||||
|
*
|
||||||
|
* @param sheetName
|
||||||
|
*/
|
||||||
|
public XlsDocumentBuilder insertNewSheet(String sheetName) {
|
||||||
|
currentSheet = workBook.createSheet(sheetName);
|
||||||
|
workBook.setActiveSheet(getSheetCount() - 1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XlsDocumentBuilder insertNewSheet(String sheetName, int order) {
|
||||||
|
insertNewSheet(sheetName);
|
||||||
|
workBook.setSheetOrder(sheetName, order);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns number of sheet in document
|
||||||
|
*
|
||||||
|
* @return sheets count
|
||||||
|
*/
|
||||||
|
public int getSheetCount() {
|
||||||
|
return workBook.getNumberOfSheets();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns number of rows in sheet
|
||||||
|
*
|
||||||
|
* @return rows count
|
||||||
|
*/
|
||||||
|
public int getRowCount() {
|
||||||
|
return currentSheet.getLastRowNum();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns number of columns in sheet
|
||||||
|
*
|
||||||
|
* @return columns count
|
||||||
|
*/
|
||||||
|
public int getColumnCount() {
|
||||||
|
Row row = currentSheet.getRow(getRowCount());
|
||||||
|
if (row == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return row.getLastCellNum() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns converted to string representation of cell value
|
||||||
|
*
|
||||||
|
* @param rowIndex row index of current sheet
|
||||||
|
* @param colIndex column index of current sheet
|
||||||
|
* @return string value of cell
|
||||||
|
*/
|
||||||
|
public String getCellAsString(int rowIndex, int colIndex) {
|
||||||
|
Cell cell = currentSheet.getRow(rowIndex).getCell(colIndex);
|
||||||
|
cell.setCellType(Cell.CELL_TYPE_STRING);
|
||||||
|
return cell.getStringCellValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets string cell value
|
||||||
|
*
|
||||||
|
* @param rowIndex row index of current sheet
|
||||||
|
* @param colIndex column index of current sheet
|
||||||
|
*/
|
||||||
|
public XlsDocumentBuilder setCellValue(int rowIndex, int colIndex, String value) {
|
||||||
|
if (currentSheet.getRow(rowIndex) == null) {
|
||||||
|
currentSheet.createRow(rowIndex);
|
||||||
|
}
|
||||||
|
if (currentSheet.getRow(rowIndex).getCell(colIndex) == null) {
|
||||||
|
currentSheet.getRow(rowIndex).createCell(colIndex);
|
||||||
|
}
|
||||||
|
Cell cell = currentSheet.getRow(rowIndex).getCell(colIndex);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
setColumnAutosize(colIndex, colIndex);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XlsDocumentBuilder setCellValue(int rowIndex, int colIndex, int value) {
|
||||||
|
return setCellValue(rowIndex, colIndex, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public XlsDocumentBuilder setCellValue(int rowIndex, int colIndex, long value) {
|
||||||
|
return setCellValue(rowIndex, colIndex, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save current file
|
||||||
|
*/
|
||||||
|
public XlsDocumentBuilder save() throws IOException {
|
||||||
|
OutputStream out = new FileOutputStream(documentFile);
|
||||||
|
workBook.write(out);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExcel(InputStream i) throws IOException {
|
||||||
|
return (POIFSFileSystem.hasPOIFSHeader(i) || POIXMLDocument.hasOOXMLHeader(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isXlsx(InputStream i) throws IOException {
|
||||||
|
return POIXMLDocument.hasOOXMLHeader(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActiveSheetIndex() {
|
||||||
|
return workBook.getActiveSheetIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public XlsDocumentBuilder mergeCells(int rowFrom, int rowTo, int colFrom, int colTo) {
|
||||||
|
currentSheet.addMergedRegion(new CellRangeAddress(rowFrom, rowTo, colFrom, colTo));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegionBorderWithMedium(int rowFrom, int rowTo, int colFrom, int colTo) {
|
||||||
|
for (int row = rowFrom; row < rowTo; row++) {
|
||||||
|
for (int col = colFrom; col <= colTo; col++) {
|
||||||
|
CellRangeAddress cellRangeAddress = new CellRangeAddress(row, row, col, col);
|
||||||
|
RegionUtil.setBorderBottom(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
||||||
|
RegionUtil.setBorderLeft(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
||||||
|
RegionUtil.setBorderRight(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
||||||
|
RegionUtil.setBorderTop(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public XlsDocumentBuilder setColumnAutosize(int from, int to) {
|
||||||
|
for (int col = from; col <= to; col++) {
|
||||||
|
currentSheet.autoSizeColumn(col, true);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XlsDocumentBuilder setRowAutosize(int from, int to) {
|
||||||
|
CellStyle style = workBook.createCellStyle();
|
||||||
|
style.setWrapText(true);
|
||||||
|
for (int row = from; row <= to; row++) {
|
||||||
|
for (int col = 0; col <= currentSheet.getRow(row).getLastCellNum(); col++) {
|
||||||
|
if (currentSheet.getRow(row).getCell(col) != null) {
|
||||||
|
currentSheet.getRow(row).getCell(col).setCellStyle(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XlsDocumentBuilder deleteSheet(int index) {
|
||||||
|
workBook.removeSheetAt(index);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package ru.ulstu.core.util;
|
||||||
|
|
||||||
|
import java.time.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DateUtils {
|
||||||
|
|
||||||
|
public static Date clearTime(Date date) {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.setTime(date);
|
||||||
|
calendar.set(Calendar.HOUR, 0);
|
||||||
|
calendar.set(Calendar.MINUTE, 0);
|
||||||
|
calendar.set(Calendar.SECOND, 0);
|
||||||
|
calendar.set(Calendar.MILLISECOND, 0);
|
||||||
|
return calendar.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Calendar getCalendar(Date date) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(date);
|
||||||
|
return cal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Month> getMonths () {
|
||||||
|
return Arrays.asList(Month.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date instantToDate(Instant instant) {
|
||||||
|
return Date.from(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date localDateToDate(LocalDate localDate) {
|
||||||
|
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date localDateTimeToDate(LocalDateTime localDate) {
|
||||||
|
return Date.from(localDate.atZone(ZoneId.systemDefault()).toInstant());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date localTimeToDate(LocalTime localTime) {
|
||||||
|
return Date.from(localTime.atDate(LocalDate.now()).atZone(ZoneId.systemDefault()).toInstant());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package ru.ulstu.core.util;
|
||||||
|
|
||||||
|
public class NumberUtils {
|
||||||
|
public static Double ceil(Double number) {
|
||||||
|
if (number == null) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
return Double.valueOf(Math.ceil(number));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Double round(Double number) {
|
||||||
|
if (number == null) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
return Double.valueOf(Math.ceil(number * 100)) / 100;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package ru.ulstu.core.util;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class StreamApiUtils {
|
||||||
|
|
||||||
|
public static <T, R> List<T> convert(List<R> entitites, Function<R, T> converter) {
|
||||||
|
return entitites.stream().map(e -> converter.apply(e)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package ru.ulstu.odin.controller;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import ru.ulstu.core.model.response.Response;
|
||||||
|
import ru.ulstu.odin.model.OdinDto;
|
||||||
|
import ru.ulstu.odin.model.OdinMetadata;
|
||||||
|
import ru.ulstu.odin.service.OdinService;
|
||||||
|
|
||||||
|
public abstract class OdinController<L, E extends OdinDto> {
|
||||||
|
public static final String META_LIST_URL = "/meta/list";
|
||||||
|
public static final String META_ELEMENT_URL = "/meta/element";
|
||||||
|
|
||||||
|
private Class<L> listDtoClass;
|
||||||
|
private Class<E> elementDtoClass;
|
||||||
|
@Autowired
|
||||||
|
private OdinService<L, E> odinService;
|
||||||
|
|
||||||
|
public OdinController(Class<L> listDtoClass) {
|
||||||
|
this(listDtoClass, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OdinController(Class<L> listDtoClass, Class<E> elementDtoClass) {
|
||||||
|
this.listDtoClass = listDtoClass;
|
||||||
|
this.elementDtoClass = elementDtoClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(META_LIST_URL)
|
||||||
|
public Response<OdinMetadata> getListModel() {
|
||||||
|
return new Response<>(odinService.getListModel(listDtoClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(META_ELEMENT_URL)
|
||||||
|
public Response<OdinMetadata> getElementModel() {
|
||||||
|
return new Response<>(odinService.getElementModel(elementDtoClass));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package ru.ulstu.odin.model;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class OdinBooleanField extends OdinField {
|
||||||
|
public OdinBooleanField(Field field) {
|
||||||
|
super(field, OdinFieldType.BOOLEAN);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package ru.ulstu.odin.model;
|
||||||
|
|
||||||
|
import ru.ulstu.core.error.OdinException;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
public class OdinCollectionField extends OdinField {
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
public OdinCollectionField(Field field) {
|
||||||
|
super(field, OdinFieldType.COLLECTION);
|
||||||
|
ParameterizedType genericType = (ParameterizedType) field.getGenericType();
|
||||||
|
Type fieldElementClass = genericType.getActualTypeArguments()[0];
|
||||||
|
try {
|
||||||
|
OdinDto someInstance = (OdinDto) ((Class) (fieldElementClass)).newInstance();
|
||||||
|
this.path = someInstance.getControllerPath();
|
||||||
|
} catch (IllegalAccessException | InstantiationException e) {
|
||||||
|
throw new OdinException(String.format("Can't create new instance, check default constructor of %s",
|
||||||
|
fieldElementClass.getTypeName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package ru.ulstu.odin.model;
|
||||||
|
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinDate;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class OdinDateField extends OdinField {
|
||||||
|
private final OdinDate.OdinDateType type;
|
||||||
|
|
||||||
|
public OdinDateField(Field field) {
|
||||||
|
super(field, OdinFieldType.DATE);
|
||||||
|
this.type = getValue(OdinDate.class, "type", OdinDate.OdinDateType.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package ru.ulstu.odin.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public interface OdinDto {
|
||||||
|
Object getId();
|
||||||
|
|
||||||
|
@JsonProperty("view")
|
||||||
|
String getViewValue();
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
String getControllerPath();
|
||||||
|
}
|
@ -0,0 +1,154 @@
|
|||||||
|
package ru.ulstu.odin.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import org.hibernate.validator.constraints.NotBlank;
|
||||||
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
|
import ru.ulstu.core.error.OdinException;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinReadOnly;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinVisible;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public abstract class OdinField implements Comparable {
|
||||||
|
public enum OdinFieldType {
|
||||||
|
BOOLEAN,
|
||||||
|
DATE,
|
||||||
|
NUMERIC,
|
||||||
|
STRING,
|
||||||
|
COLLECTION,
|
||||||
|
OBJECT,
|
||||||
|
UNKNOWN;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name().toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Field field;
|
||||||
|
protected final OdinFieldType fieldType;
|
||||||
|
protected final String fieldName;
|
||||||
|
protected final String caption;
|
||||||
|
protected final OdinVisible.OdinVisibleType visible;
|
||||||
|
protected final boolean readOnly;
|
||||||
|
protected final boolean notEmpty;
|
||||||
|
|
||||||
|
public OdinField(Field field, OdinFieldType fieldType) {
|
||||||
|
this.field = field;
|
||||||
|
this.fieldName = getFieldName(field);
|
||||||
|
this.caption = Optional.ofNullable(getValueFromAnnotation(OdinCaption.class, "value"))
|
||||||
|
.map(value -> cast(value, String.class))
|
||||||
|
.orElse(fieldName);
|
||||||
|
this.visible = getValue(OdinVisible.class, "type", OdinVisible.OdinVisibleType.class);
|
||||||
|
this.readOnly = field.isAnnotationPresent(OdinReadOnly.class);
|
||||||
|
this.notEmpty = field.isAnnotationPresent(NotBlank.class)
|
||||||
|
|| field.isAnnotationPresent(NotEmpty.class)
|
||||||
|
|| field.isAnnotationPresent(NotNull.class);
|
||||||
|
this.fieldType = fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFieldName(Field field) {
|
||||||
|
if (field.isAnnotationPresent(JsonProperty.class)) {
|
||||||
|
final String fieldName = cast(getValueFromAnnotation(JsonProperty.class, "value"), String.class);
|
||||||
|
return Optional.ofNullable(fieldName).filter(value -> !value.isEmpty()).orElse(field.getName());
|
||||||
|
}
|
||||||
|
return field.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getDefaultValueFromAnnotation(Class<? extends Annotation> annotationClass, String valueName) {
|
||||||
|
try {
|
||||||
|
final Method method = annotationClass.getDeclaredMethod(valueName);
|
||||||
|
return method.getDefaultValue();
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new OdinException(String.format("Default value %s is not found in annotation %s",
|
||||||
|
valueName, annotationClass.getSimpleName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getValueFromAnnotation(Class<? extends Annotation> annotationClass, String valueName) {
|
||||||
|
if (!field.isAnnotationPresent(annotationClass)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final Method method = annotationClass.getDeclaredMethod(valueName);
|
||||||
|
return method.invoke(field.getAnnotation(annotationClass));
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new OdinException(String.format("Value %s is not found in annotation %s",
|
||||||
|
valueName, annotationClass.getSimpleName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T cast(Object value, Class<T> clazz) {
|
||||||
|
try {
|
||||||
|
return clazz.cast(value);
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> T getValue(Class<? extends Annotation> annotationClass, String valueName, Class<T> clazz) {
|
||||||
|
if (field.isAnnotationPresent(annotationClass)) {
|
||||||
|
return cast(getValueFromAnnotation(annotationClass, valueName), clazz);
|
||||||
|
}
|
||||||
|
return cast(getDefaultValueFromAnnotation(annotationClass, valueName), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldType() {
|
||||||
|
return fieldType.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldName() {
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCaption() {
|
||||||
|
return caption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVisible() {
|
||||||
|
return visible.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReadOnly() {
|
||||||
|
return readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNotEmpty() {
|
||||||
|
return notEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
OdinField odinField = (OdinField) o;
|
||||||
|
return Objects.equals(fieldName, odinField.fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + " {" +
|
||||||
|
"fieldName='" + fieldName + '\'' +
|
||||||
|
", fieldType='" + fieldType + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Object o) {
|
||||||
|
final String thisName = Optional.ofNullable(fieldName).orElse("");
|
||||||
|
final String oName = Optional.ofNullable(o).map(obj -> ((OdinField) obj).fieldName).orElse("");
|
||||||
|
return thisName.compareTo(oName);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package ru.ulstu.odin.model;
|
||||||
|
|
||||||
|
import ru.ulstu.core.error.OdinException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class OdinMetadata {
|
||||||
|
private final boolean odinDto;
|
||||||
|
private final List<OdinField> fields;
|
||||||
|
|
||||||
|
public OdinMetadata(boolean odinDto, List<OdinField> fields) {
|
||||||
|
if (fields == null) {
|
||||||
|
throw new OdinException("Fields list can't be null");
|
||||||
|
}
|
||||||
|
this.odinDto = odinDto;
|
||||||
|
this.fields = fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOdinDto() {
|
||||||
|
return odinDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OdinField> getFields() {
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package ru.ulstu.odin.model;
|
||||||
|
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinNumeric;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class OdinNumericField extends OdinField {
|
||||||
|
private final boolean positiveOnly;
|
||||||
|
private final int precision;
|
||||||
|
private final int scale;
|
||||||
|
|
||||||
|
public OdinNumericField(Field field) {
|
||||||
|
super(field, OdinFieldType.NUMERIC);
|
||||||
|
this.positiveOnly = getValue(OdinNumeric.class, "positiveOnly", Boolean.class);
|
||||||
|
this.precision = getValue(OdinNumeric.class, "precision", Integer.class);
|
||||||
|
this.scale = getValue(OdinNumeric.class, "scale", Integer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPositiveOnly() {
|
||||||
|
return positiveOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPrecision() {
|
||||||
|
return precision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getScale() {
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package ru.ulstu.odin.model;
|
||||||
|
|
||||||
|
import ru.ulstu.core.error.OdinException;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
public class OdinObjectField extends OdinField {
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
public OdinObjectField(Field field) {
|
||||||
|
super(field, OdinFieldType.OBJECT);
|
||||||
|
Type fieldElementClass = field.getType();
|
||||||
|
try {
|
||||||
|
OdinDto someInstance = (OdinDto) ((Class) (fieldElementClass)).newInstance();
|
||||||
|
this.path = someInstance.getControllerPath();
|
||||||
|
} catch (IllegalAccessException | InstantiationException e) {
|
||||||
|
throw new OdinException(String.format("Can't create new instance, check default constructor of %s",
|
||||||
|
fieldElementClass.getTypeName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package ru.ulstu.odin.model;
|
||||||
|
|
||||||
|
import org.hibernate.validator.constraints.Email;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinString;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinString.OdinStringType;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
import static ru.ulstu.odin.model.annotation.OdinString.OdinStringType.EMAIL;
|
||||||
|
|
||||||
|
public class OdinStringField extends OdinField {
|
||||||
|
private final int minLength;
|
||||||
|
private final int maxLength;
|
||||||
|
private final OdinStringType type;
|
||||||
|
|
||||||
|
public OdinStringField(Field field) {
|
||||||
|
super(field, OdinFieldType.STRING);
|
||||||
|
this.minLength = getValue(Size.class, "min", Integer.class);
|
||||||
|
this.maxLength = getValue(Size.class, "max", Integer.class);
|
||||||
|
this.type = field.isAnnotationPresent(Email.class)
|
||||||
|
? EMAIL
|
||||||
|
: getValue(OdinString.class, "type", OdinStringType.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinLength() {
|
||||||
|
return minLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxLength() {
|
||||||
|
return maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package ru.ulstu.odin.model;
|
||||||
|
|
||||||
|
public class OdinVoid implements OdinDto {
|
||||||
|
@Override
|
||||||
|
public Object getId() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getViewValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getControllerPath() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package ru.ulstu.odin.model.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||||
|
public @interface OdinCaption {
|
||||||
|
String value();
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package ru.ulstu.odin.model.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||||
|
public @interface OdinDate {
|
||||||
|
enum OdinDateType {
|
||||||
|
DATETIME, DATE, TIME;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name().toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OdinDateType type() default OdinDateType.DATE;
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package ru.ulstu.odin.model.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||||
|
public @interface OdinNumeric {
|
||||||
|
boolean positiveOnly() default false;
|
||||||
|
|
||||||
|
int precision() default 10;
|
||||||
|
|
||||||
|
int scale() default 0;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package ru.ulstu.odin.model.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||||
|
public @interface OdinReadOnly {
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package ru.ulstu.odin.model.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||||
|
public @interface OdinString {
|
||||||
|
enum OdinStringType {
|
||||||
|
STRING, PASSWORD, TEXT, EMAIL, HREF;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name().toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OdinStringType type() default OdinStringType.STRING;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package ru.ulstu.odin.model.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||||
|
public @interface OdinVisible {
|
||||||
|
enum OdinVisibleType {
|
||||||
|
ALL, ON_CREATE, ON_UPDATE, NONE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name().toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OdinVisibleType type() default OdinVisibleType.ALL;
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package ru.ulstu.odin.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.core.error.OdinException;
|
||||||
|
import ru.ulstu.odin.model.*;
|
||||||
|
import ru.ulstu.odin.model.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class OdinService<L, E extends OdinDto> {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(OdinService.class);
|
||||||
|
|
||||||
|
private final Map<String, OdinField.OdinFieldType> aliases;
|
||||||
|
|
||||||
|
public OdinService() {
|
||||||
|
aliases = new HashMap<>();
|
||||||
|
aliases.put("boolean", OdinField.OdinFieldType.BOOLEAN);
|
||||||
|
aliases.put("date", OdinField.OdinFieldType.DATE);
|
||||||
|
aliases.put("instant", OdinField.OdinFieldType.DATE);
|
||||||
|
aliases.put("localdate", OdinField.OdinFieldType.DATE);
|
||||||
|
aliases.put("localtime", OdinField.OdinFieldType.DATE);
|
||||||
|
aliases.put("localdatetime", OdinField.OdinFieldType.DATE);
|
||||||
|
aliases.put("int", OdinField.OdinFieldType.NUMERIC);
|
||||||
|
aliases.put("integer", OdinField.OdinFieldType.NUMERIC);
|
||||||
|
aliases.put("double", OdinField.OdinFieldType.NUMERIC);
|
||||||
|
aliases.put("float", OdinField.OdinFieldType.NUMERIC);
|
||||||
|
aliases.put("long", OdinField.OdinFieldType.NUMERIC);
|
||||||
|
aliases.put("string", OdinField.OdinFieldType.STRING);
|
||||||
|
aliases.put("collection", OdinField.OdinFieldType.COLLECTION);
|
||||||
|
aliases.put("object", OdinField.OdinFieldType.OBJECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OdinField.OdinFieldType getFieldTypeByTypeAlias(String fieldType) {
|
||||||
|
return Optional.ofNullable(aliases.get(fieldType)).orElse(OdinField.OdinFieldType.UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTypeAlias(String fieldType) {
|
||||||
|
return fieldType.replaceAll(".*\\.", "").toLowerCase().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private OdinField.OdinFieldType getFieldType(Field field) {
|
||||||
|
final String typeAlias;
|
||||||
|
if (Collection.class.isAssignableFrom(field.getType())) {
|
||||||
|
typeAlias = "collection";
|
||||||
|
} else if (OdinDto.class.isAssignableFrom(field.getType())) {
|
||||||
|
typeAlias = "object";
|
||||||
|
} else {
|
||||||
|
typeAlias = getTypeAlias(field.getGenericType().getTypeName());
|
||||||
|
}
|
||||||
|
return getFieldTypeByTypeAlias(typeAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> List<OdinField> getDtoMetaModel(Class<T> dtoClass) {
|
||||||
|
return Arrays.stream(dtoClass.getDeclaredFields())
|
||||||
|
.filter(field -> !field.isAnnotationPresent(JsonIgnore.class)
|
||||||
|
&& !Modifier.isStatic(field.getModifiers()))
|
||||||
|
.map(field -> {
|
||||||
|
OdinField.OdinFieldType fieldType = getFieldType(field);
|
||||||
|
switch (fieldType) {
|
||||||
|
case BOOLEAN:
|
||||||
|
return new OdinBooleanField(field);
|
||||||
|
case DATE:
|
||||||
|
return new OdinDateField(field);
|
||||||
|
case NUMERIC:
|
||||||
|
return new OdinNumericField(field);
|
||||||
|
case STRING:
|
||||||
|
return new OdinStringField(field);
|
||||||
|
case COLLECTION:
|
||||||
|
return new OdinCollectionField(field);
|
||||||
|
case OBJECT:
|
||||||
|
return new OdinObjectField(field);
|
||||||
|
default:
|
||||||
|
log.debug("Unknown type {}. Skip field {} of DTO {}.",
|
||||||
|
field.getGenericType().getTypeName(), field.getName(), dtoClass.getSimpleName());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public OdinMetadata getListModel(Class<L> listDtoClass) {
|
||||||
|
if (listDtoClass == null) {
|
||||||
|
throw new OdinException("List DTO class is null");
|
||||||
|
}
|
||||||
|
return new OdinMetadata(OdinDto.class.isAssignableFrom(listDtoClass), getDtoMetaModel(listDtoClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
public OdinMetadata getElementModel(Class<E> elementDtoClass) {
|
||||||
|
return elementDtoClass == null
|
||||||
|
? null :
|
||||||
|
new OdinMetadata(true, getDtoMetaModel(elementDtoClass));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package ru.ulstu.odinexample.controller;
|
||||||
|
|
||||||
|
public class OdinExampleController {
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package ru.ulstu.odinexample.model;
|
||||||
|
|
||||||
|
public class OdinExampleDto {
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package ru.ulstu.odinexample.model;
|
||||||
|
|
||||||
|
import ru.ulstu.core.util.DateUtils;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinDate;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinNumeric;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class OdinExampleListDto {
|
||||||
|
@OdinCaption("instant")
|
||||||
|
@OdinDate(type = OdinDate.OdinDateType.DATETIME)
|
||||||
|
private Instant instant;
|
||||||
|
@OdinCaption("date")
|
||||||
|
private Date date;
|
||||||
|
@OdinCaption("localdate")
|
||||||
|
private LocalDate localDate;
|
||||||
|
@OdinCaption("localtime")
|
||||||
|
@OdinDate(type = OdinDate.OdinDateType.TIME)
|
||||||
|
private LocalTime localTime;
|
||||||
|
@OdinCaption("localdatetime")
|
||||||
|
@OdinDate(type = OdinDate.OdinDateType.DATETIME)
|
||||||
|
private LocalDateTime localDateTime;
|
||||||
|
@OdinCaption("int")
|
||||||
|
private int intval;
|
||||||
|
@OdinCaption("int+settings")
|
||||||
|
@OdinNumeric(precision = 5, scale = 2)
|
||||||
|
private int intvalset;
|
||||||
|
@OdinCaption("float")
|
||||||
|
private float floatval;
|
||||||
|
@OdinCaption("double")
|
||||||
|
private double aDouble;
|
||||||
|
@OdinCaption("double+set")
|
||||||
|
@OdinNumeric(precision = 5, scale = 3)
|
||||||
|
private double aDoubles;
|
||||||
|
@OdinCaption("int+positive")
|
||||||
|
@OdinNumeric(positiveOnly = true, scale = 2)
|
||||||
|
private int invalpos;
|
||||||
|
|
||||||
|
public OdinExampleListDto() {
|
||||||
|
this.instant = Instant.now();
|
||||||
|
this.date = new Date();
|
||||||
|
this.localDate = LocalDate.now();
|
||||||
|
this.localTime = LocalTime.now();
|
||||||
|
this.localDateTime = LocalDateTime.now();
|
||||||
|
intval = -134;
|
||||||
|
intvalset = 1343423232;
|
||||||
|
floatval = 2323.44F;
|
||||||
|
aDouble = -232323.43434;
|
||||||
|
aDoubles = 0.456456456;
|
||||||
|
invalpos = -23232323;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Date getInstant() {
|
||||||
|
return DateUtils.instantToDate(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLocalDate() {
|
||||||
|
return DateUtils.localDateToDate(localDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLocalTime() {
|
||||||
|
return DateUtils.localTimeToDate(localTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLocalDateTime() {
|
||||||
|
return DateUtils.localDateTimeToDate(localDateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIntval() {
|
||||||
|
return intval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIntvalset() {
|
||||||
|
return intvalset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getFloatval() {
|
||||||
|
return floatval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getaDouble() {
|
||||||
|
return aDouble;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getaDoubles() {
|
||||||
|
return aDoubles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInvalpos() {
|
||||||
|
return invalpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package ru.ulstu.odinexample.service;
|
||||||
|
|
||||||
|
public class OdinExampleService {
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package ru.ulstu.user.component;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
public final class IpAddressResolver {
|
||||||
|
private static final String CLIENT_IP_HEADER = "Client-IP";
|
||||||
|
private static final String FORWARDED_FOR_HEADER = "X-Forwarded-For";
|
||||||
|
|
||||||
|
public static String getRemoteAddr(HttpServletRequest request) {
|
||||||
|
String headerClientIp = request.getHeader("");
|
||||||
|
String headerXForwardedFor = request.getHeader(HttpServletRequest.FORM_AUTH);
|
||||||
|
if (StringUtils.isEmpty(request.getRemoteAddr()) && !StringUtils.isEmpty(headerClientIp)) {
|
||||||
|
return headerClientIp;
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(headerXForwardedFor)) {
|
||||||
|
return headerXForwardedFor;
|
||||||
|
}
|
||||||
|
return request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package ru.ulstu.user.component;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
|
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
import ru.ulstu.user.service.UserSessionService;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class UserSessionLoginHandler extends SavedRequestAwareAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserSessionLoginHandler.class);
|
||||||
|
private final UserSessionService userSessionService;
|
||||||
|
|
||||||
|
public UserSessionLoginHandler(UserSessionService userSessionService) {
|
||||||
|
super();
|
||||||
|
this.userSessionService = userSessionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSuccess(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
Authentication authentication) throws IOException, ServletException {
|
||||||
|
super.onAuthenticationSuccess(request, response, authentication);
|
||||||
|
final String login = authentication.getName();
|
||||||
|
final String ipAddress = IpAddressResolver.getRemoteAddr(request);
|
||||||
|
final String host = request.getRemoteHost();
|
||||||
|
log.debug("Authentication Success for {}@{} ({})", login, ipAddress, host);
|
||||||
|
HttpSession session = request.getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
final String sessionId = session.getId();
|
||||||
|
userSessionService.createUserSession(sessionId, login, ipAddress, host);
|
||||||
|
session.setAttribute(Constants.SESSION_ID_ATTR, sessionId);
|
||||||
|
session.setMaxInactiveInterval(Constants.SESSION_TIMEOUT_SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package ru.ulstu.user.component;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||||
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
import ru.ulstu.user.service.UserSessionService;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class UserSessionLogoutHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserSessionLogoutHandler.class);
|
||||||
|
private final UserSessionService userSessionService;
|
||||||
|
|
||||||
|
public UserSessionLogoutHandler(UserSessionService userSessionService) {
|
||||||
|
this.userSessionService = userSessionService;
|
||||||
|
setDefaultTargetUrl(Constants.LOGOUT_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLogoutSuccess(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
Authentication authentication) throws IOException, ServletException {
|
||||||
|
if (authentication == null) {
|
||||||
|
super.onLogoutSuccess(request, response, authentication);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String login = authentication.getName();
|
||||||
|
final String ipAddress = IpAddressResolver.getRemoteAddr(request);
|
||||||
|
final String host = request.getRemoteHost();
|
||||||
|
log.debug("Logout Success for {}@{} ({})", login, ipAddress, host);
|
||||||
|
HttpSession session = request.getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
final String sessionId = session.getAttribute(Constants.SESSION_ID_ATTR).toString();
|
||||||
|
userSessionService.closeUserSession(sessionId);
|
||||||
|
session.removeAttribute(Constants.SESSION_ID_ATTR);
|
||||||
|
session.invalidate();
|
||||||
|
}
|
||||||
|
super.onLogoutSuccess(request, response, authentication);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,158 @@
|
|||||||
|
package ru.ulstu.user.controller;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.access.annotation.Secured;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
import ru.ulstu.core.model.response.PageableItems;
|
||||||
|
import ru.ulstu.core.model.response.Response;
|
||||||
|
import ru.ulstu.odin.controller.OdinController;
|
||||||
|
import ru.ulstu.odin.model.OdinMetadata;
|
||||||
|
import ru.ulstu.odin.model.OdinVoid;
|
||||||
|
import ru.ulstu.odin.service.OdinService;
|
||||||
|
import ru.ulstu.user.model.*;
|
||||||
|
import ru.ulstu.user.service.UserService;
|
||||||
|
import ru.ulstu.user.service.UserSessionService;
|
||||||
|
import ru.ulstu.user.model.*;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import static ru.ulstu.user.controller.UserController.URL;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(URL)
|
||||||
|
public class UserController extends OdinController<UserListDto, UserDto> {
|
||||||
|
public static final String URL = Constants.API_1_0 + "users";
|
||||||
|
public static final String ROLES_URL = "/roles";
|
||||||
|
public static final String ROLES_META_URL = ROLES_URL + OdinController.META_LIST_URL;
|
||||||
|
public static final String SESSIONS_URL = "/sessions";
|
||||||
|
public static final String SESSIONS_META_URL = SESSIONS_URL + OdinController.META_LIST_URL;
|
||||||
|
public static final String REGISTER_URL = "/register";
|
||||||
|
public static final String ACTIVATE_URL = "/activate";
|
||||||
|
public static final String PASSWORD_RESET_REQUEST_URL = "/password-reset-request";
|
||||||
|
public static final String PASSWORD_RESET_URL = "/password-reset";
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserController.class);
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
private final UserSessionService userSessionService;
|
||||||
|
private final OdinService<UserRoleDto, UserRoleDto> odinRolesService;
|
||||||
|
private final OdinService<UserSessionListDto, OdinVoid> odinSessionsService;
|
||||||
|
|
||||||
|
public UserController(UserService userService,
|
||||||
|
UserSessionService userSessionService,
|
||||||
|
OdinService<UserRoleDto, UserRoleDto> odinRolesService,
|
||||||
|
OdinService<UserSessionListDto, OdinVoid> odinSessionsService) {
|
||||||
|
super(UserListDto.class, UserDto.class);
|
||||||
|
this.userService = userService;
|
||||||
|
this.userSessionService = userSessionService;
|
||||||
|
this.odinRolesService = odinRolesService;
|
||||||
|
this.odinSessionsService = odinSessionsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(ROLES_URL)
|
||||||
|
@Secured(UserRoleConstants.ADMIN)
|
||||||
|
public Response<PageableItems<UserRoleDto>> getUserRoles() {
|
||||||
|
log.debug("REST: UserController.getUserRoles()");
|
||||||
|
return new Response<>(userService.getUserRoles());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(ROLES_META_URL)
|
||||||
|
@Secured(UserRoleConstants.ADMIN)
|
||||||
|
public Response<OdinMetadata> getUserRolesMetaData() {
|
||||||
|
log.debug("REST: UserController.getUserRolesMetaData()");
|
||||||
|
return new Response<>(odinRolesService.getListModel(UserRoleDto.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(SESSIONS_URL)
|
||||||
|
@Secured(UserRoleConstants.ADMIN)
|
||||||
|
public Response<PageableItems<UserSessionListDto>> getUserSessions(@RequestParam(value = "offset", defaultValue = "0") int offset,
|
||||||
|
@RequestParam(value = "count", defaultValue = "10") int count) {
|
||||||
|
log.debug("REST: UserController.getUserSessions()");
|
||||||
|
return new Response<>(userSessionService.getSessions(offset, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(SESSIONS_META_URL)
|
||||||
|
@Secured(UserRoleConstants.ADMIN)
|
||||||
|
public Response<OdinMetadata> getUserSessionsMetaData() {
|
||||||
|
log.debug("REST: UserController.getUserSessionsMetaData()");
|
||||||
|
return new Response<>(odinSessionsService.getListModel(UserSessionListDto.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("")
|
||||||
|
@Secured(UserRoleConstants.ADMIN)
|
||||||
|
public Response<PageableItems<UserListDto>> getAllUsers(@RequestParam(value = "offset", defaultValue = "0") int offset,
|
||||||
|
@RequestParam(value = "count", defaultValue = "10") int count) {
|
||||||
|
log.debug("REST: UserController.getAllUsers( {}, {} )", offset, count);
|
||||||
|
return new Response<>(userService.getAllUsers(offset, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{userId}")
|
||||||
|
@Secured(UserRoleConstants.ADMIN)
|
||||||
|
public Response<UserDto> getUser(@PathVariable Integer userId) {
|
||||||
|
log.debug("REST: UserController.getUser( {} )", userId);
|
||||||
|
return new Response<>(userService.getUserWithRolesById(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("")
|
||||||
|
@Secured(UserRoleConstants.ADMIN)
|
||||||
|
public Response<UserDto> createUser(@Valid @RequestBody UserDto userDto) {
|
||||||
|
log.debug("REST: UserController.createUser( {} )", userDto.getLogin());
|
||||||
|
return new Response<>(userService.createUser(userDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("")
|
||||||
|
@Secured(UserRoleConstants.ADMIN)
|
||||||
|
public Response<UserDto> updateUser(@Valid @RequestBody UserDto userDto) {
|
||||||
|
log.debug("REST: UserController.updateUser( {} )", userDto.getLogin());
|
||||||
|
return new Response<>(userService.updateUser(userDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{userId}")
|
||||||
|
@Secured(UserRoleConstants.ADMIN)
|
||||||
|
public Response<UserDto> deleteUser(@PathVariable Integer userId) {
|
||||||
|
log.debug("REST: UserController.deleteUser( {} )", userId);
|
||||||
|
return new Response<>(userService.deleteUser(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(REGISTER_URL)
|
||||||
|
public Response<UserDto> registerUser(@Valid @RequestBody UserDto userDto) {
|
||||||
|
log.debug("REST: UserController.registerUser( {} )", userDto.getLogin());
|
||||||
|
return new Response<>(userService.createUser(userDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(ACTIVATE_URL)
|
||||||
|
public Response<UserDto> activateUser(@RequestParam("key") String activationKey) {
|
||||||
|
log.debug("REST: UserController.activateUser( {} )", activationKey);
|
||||||
|
return new Response<>(userService.activateUser(activationKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add page for user edit (user-profile)
|
||||||
|
@PostMapping("/change-information")
|
||||||
|
public Response<UserDto> changeInformation(@Valid @RequestBody UserDto userDto) {
|
||||||
|
log.debug("REST: UserController.changeInformation( {} )", userDto.getLogin());
|
||||||
|
return new Response<>(userService.updateUserInformation(userDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add page for user password change (user-profile)
|
||||||
|
@PostMapping("/change-password")
|
||||||
|
public Response<UserDto> changePassword(@Valid @RequestBody UserDto userDto) {
|
||||||
|
log.debug("REST: UserController.changePassword( {} )", userDto.getLogin());
|
||||||
|
return new Response<>(userService.changeUserPassword(userDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(PASSWORD_RESET_REQUEST_URL)
|
||||||
|
public Response<Boolean> requestPasswordReset(@RequestParam("email") String email) {
|
||||||
|
log.debug("REST: UserController.requestPasswordReset( {} )", email);
|
||||||
|
return new Response<>(userService.requestUserPasswordReset(email));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(PASSWORD_RESET_URL)
|
||||||
|
public Response<Boolean> finishPasswordReset(@RequestParam("key") String key,
|
||||||
|
@RequestBody UserResetPasswordDto userResetPasswordDto) {
|
||||||
|
log.debug("REST: UserController.requestPasswordReset( {} )", key);
|
||||||
|
return new Response<>(userService.completeUserPasswordReset(key, userResetPasswordDto));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.user.error;
|
||||||
|
|
||||||
|
public class UserActivationError extends RuntimeException {
|
||||||
|
public UserActivationError(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.user.error;
|
||||||
|
|
||||||
|
public class UserEmailExistsException extends RuntimeException {
|
||||||
|
public UserEmailExistsException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package ru.ulstu.user.error;
|
||||||
|
|
||||||
|
public class UserIdExistsException extends RuntimeException {
|
||||||
|
public UserIdExistsException() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.user.error;
|
||||||
|
|
||||||
|
public class UserIsUndeadException extends RuntimeException {
|
||||||
|
public UserIsUndeadException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.user.error;
|
||||||
|
|
||||||
|
public class UserLoginExistsException extends RuntimeException {
|
||||||
|
public UserLoginExistsException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package ru.ulstu.user.error;
|
||||||
|
|
||||||
|
public class UserNotActivatedException extends RuntimeException {
|
||||||
|
public UserNotActivatedException() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.user.error;
|
||||||
|
|
||||||
|
public class UserNotFoundException extends RuntimeException {
|
||||||
|
public UserNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package ru.ulstu.user.error;
|
||||||
|
|
||||||
|
public class UserPasswordsNotValidOrNotMatchException extends RuntimeException {
|
||||||
|
public UserPasswordsNotValidOrNotMatchException() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.user.error;
|
||||||
|
|
||||||
|
public class UserResetKeyError extends RuntimeException {
|
||||||
|
public UserResetKeyError(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,170 @@
|
|||||||
|
package ru.ulstu.user.model;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.BatchSize;
|
||||||
|
import org.hibernate.validator.constraints.Email;
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
import ru.ulstu.core.model.BaseEntity;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "users")
|
||||||
|
public class User extends BaseEntity {
|
||||||
|
@NotNull
|
||||||
|
@Pattern(regexp = Constants.LOGIN_REGEX)
|
||||||
|
@Size(min = 1, max = 50)
|
||||||
|
@Column(length = 50, unique = true, nullable = false)
|
||||||
|
private String login;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Size(min = 60, max = 60)
|
||||||
|
@Column(name = "password_hash", length = 60, nullable = false)
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Size(max = 50)
|
||||||
|
@Column(name = "first_name", length = 50, nullable = false)
|
||||||
|
private String firstName;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Size(max = 50)
|
||||||
|
@Column(name = "last_name", length = 50, nullable = false)
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Email
|
||||||
|
@Size(min = 5, max = 100)
|
||||||
|
@Column(length = 100, nullable = false, unique = true)
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private boolean activated;
|
||||||
|
|
||||||
|
@Size(max = 20)
|
||||||
|
@Column(name = "activation_key", length = 20)
|
||||||
|
private String activationKey;
|
||||||
|
|
||||||
|
@Column(name = "activation_date")
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
private Date activationDate;
|
||||||
|
|
||||||
|
@Size(max = 20)
|
||||||
|
@Column(name = "reset_key", length = 20)
|
||||||
|
private String resetKey;
|
||||||
|
|
||||||
|
@Column(name = "reset_date")
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
private Date resetDate;
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(
|
||||||
|
name = "user_role",
|
||||||
|
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
|
||||||
|
inverseJoinColumns = {@JoinColumn(name = "user_role_name", referencedColumnName = "name")})
|
||||||
|
@BatchSize(size = 20)
|
||||||
|
private Set<UserRole> roles;
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
roles = new HashSet<>();
|
||||||
|
activated = false;
|
||||||
|
activationDate = new Date();
|
||||||
|
resetDate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getActivated() {
|
||||||
|
return activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivated(boolean activated) {
|
||||||
|
this.activated = activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getActivationKey() {
|
||||||
|
return activationKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivationKey(String activationKey) {
|
||||||
|
this.activationKey = activationKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getActivationDate() {
|
||||||
|
return activationDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivationDate(Date activationDate) {
|
||||||
|
this.activationDate = activationDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResetKey() {
|
||||||
|
return resetKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResetKey(String resetKey) {
|
||||||
|
this.resetKey = resetKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getResetDate() {
|
||||||
|
return resetDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResetDate(Date resetDate) {
|
||||||
|
this.resetDate = resetDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<UserRole> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoles(Collection<UserRole> roles) {
|
||||||
|
this.roles.clear();
|
||||||
|
this.roles.addAll(roles);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
package ru.ulstu.user.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import org.hibernate.validator.constraints.Email;
|
||||||
|
import org.hibernate.validator.constraints.NotBlank;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
import ru.ulstu.odin.model.OdinDto;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinReadOnly;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinString;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinVisible;
|
||||||
|
import ru.ulstu.user.controller.UserController;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ru.ulstu.odin.model.annotation.OdinString.OdinStringType.PASSWORD;
|
||||||
|
|
||||||
|
public class UserDto implements OdinDto {
|
||||||
|
@OdinReadOnly
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@Pattern(regexp = Constants.LOGIN_REGEX)
|
||||||
|
@Size(min = 4, max = 50)
|
||||||
|
@OdinCaption("Логин")
|
||||||
|
private String login;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@Size(min = 2, max = 50)
|
||||||
|
@OdinCaption("Имя")
|
||||||
|
private String firstName;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@Size(min = 2, max = 50)
|
||||||
|
@OdinCaption("Фамилия")
|
||||||
|
private String lastName;
|
||||||
|
|
||||||
|
@Email
|
||||||
|
@NotBlank
|
||||||
|
@Size(min = 5, max = 100)
|
||||||
|
@OdinCaption("E-Mail")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@OdinCaption("Аккаунт активен")
|
||||||
|
private boolean activated;
|
||||||
|
|
||||||
|
@OdinCaption("Роли")
|
||||||
|
private LinkedHashSet<UserRoleDto> roles;
|
||||||
|
|
||||||
|
@OdinString(type = PASSWORD)
|
||||||
|
@OdinVisible(type = OdinVisible.OdinVisibleType.ON_UPDATE)
|
||||||
|
@OdinCaption("Текущий пароль")
|
||||||
|
@Size(max = 50)
|
||||||
|
private String oldPassword;
|
||||||
|
|
||||||
|
@OdinString(type = PASSWORD)
|
||||||
|
@OdinCaption("Пароль")
|
||||||
|
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@OdinString(type = PASSWORD)
|
||||||
|
@OdinCaption("Пароль (подтверждение)")
|
||||||
|
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||||
|
private String passwordConfirm;
|
||||||
|
|
||||||
|
public UserDto() {
|
||||||
|
activated = false;
|
||||||
|
roles = new LinkedHashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDto(User user) {
|
||||||
|
this();
|
||||||
|
this.id = user.getId();
|
||||||
|
this.login = user.getLogin();
|
||||||
|
this.firstName = user.getFirstName();
|
||||||
|
this.lastName = user.getLastName();
|
||||||
|
this.email = user.getEmail();
|
||||||
|
this.activated = user.getActivated();
|
||||||
|
this.roles.addAll(user.getRoles().stream()
|
||||||
|
.map(UserRoleDto::new)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getViewValue() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getControllerPath() {
|
||||||
|
return UserController.URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActivated() {
|
||||||
|
return activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivated(boolean activated) {
|
||||||
|
this.activated = activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<UserRoleDto> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoles(Collection<UserRoleDto> roles) {
|
||||||
|
this.roles.clear();
|
||||||
|
this.roles.addAll(roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOldPassword() {
|
||||||
|
return oldPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPasswordConfirm() {
|
||||||
|
return passwordConfirm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean isPasswordsValid() {
|
||||||
|
if (StringUtils.isEmpty(password) || StringUtils.isEmpty(passwordConfirm)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Objects.equals(password, passwordConfirm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean isOldPasswordValid() {
|
||||||
|
return !StringUtils.isEmpty(oldPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + " {" +
|
||||||
|
"id=" + id +
|
||||||
|
", login='" + login + '\'' +
|
||||||
|
", firstName='" + firstName + '\'' +
|
||||||
|
", lastName='" + lastName + '\'' +
|
||||||
|
", email='" + email + '\'' +
|
||||||
|
", activated=" + activated +
|
||||||
|
", roles=" + roles +
|
||||||
|
", password='" + password + '\'' +
|
||||||
|
", passwordConfirm='" + passwordConfirm + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
package ru.ulstu.user.model;
|
||||||
|
|
||||||
|
import ru.ulstu.odin.model.OdinDto;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinVisible;
|
||||||
|
import ru.ulstu.user.controller.UserController;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class UserListDto implements OdinDto {
|
||||||
|
@OdinVisible(type = OdinVisible.OdinVisibleType.NONE)
|
||||||
|
private int id;
|
||||||
|
@OdinCaption("Логин")
|
||||||
|
private String login;
|
||||||
|
@OdinCaption("Имя")
|
||||||
|
private String firstName;
|
||||||
|
@OdinCaption("Фамилия")
|
||||||
|
private String lastName;
|
||||||
|
@OdinCaption("E-Mail")
|
||||||
|
private String email;
|
||||||
|
@OdinCaption("Аккаунт активен")
|
||||||
|
private boolean activated;
|
||||||
|
@OdinCaption("Права администратора")
|
||||||
|
private boolean admin;
|
||||||
|
|
||||||
|
public UserListDto(User user) {
|
||||||
|
this.id = user.getId();
|
||||||
|
this.login = user.getLogin();
|
||||||
|
this.firstName = user.getFirstName();
|
||||||
|
this.lastName = user.getLastName();
|
||||||
|
this.email = user.getEmail();
|
||||||
|
this.activated = user.getActivated();
|
||||||
|
this.admin = Optional.ofNullable(user.getRoles()).orElse(Collections.emptySet()).stream()
|
||||||
|
.anyMatch(role -> Objects.equals(UserRoleConstants.ADMIN, role.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getViewValue() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getControllerPath() {
|
||||||
|
return UserController.URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActivated() {
|
||||||
|
return activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAdmin() {
|
||||||
|
return admin;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package ru.ulstu.user.model;
|
||||||
|
|
||||||
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class UserResetPasswordDto {
|
||||||
|
@NotEmpty
|
||||||
|
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||||
|
private String password;
|
||||||
|
@NotEmpty
|
||||||
|
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||||
|
private String passwordConfirm;
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPasswordConfirm() {
|
||||||
|
return passwordConfirm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPasswordsValid() {
|
||||||
|
return Objects.equals(password, passwordConfirm);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package ru.ulstu.user.model;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "user_roles")
|
||||||
|
public class UserRole {
|
||||||
|
@Id
|
||||||
|
@NotNull
|
||||||
|
@Size(max = 50)
|
||||||
|
@Column(length = 50, nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public UserRole() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRole(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UserRole role = (UserRole) o;
|
||||||
|
return !(name != null ? !name.equals(role.name) : role.name != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return name != null ? name.hashCode() : 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package ru.ulstu.user.model;
|
||||||
|
|
||||||
|
public class UserRoleConstants {
|
||||||
|
public static final String ADMIN = "ROLE_ADMIN";
|
||||||
|
public static final String USER = "ROLE_USER";
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package ru.ulstu.user.model;
|
||||||
|
|
||||||
|
import ru.ulstu.odin.model.OdinDto;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||||
|
import ru.ulstu.user.controller.UserController;
|
||||||
|
|
||||||
|
public class UserRoleDto implements OdinDto {
|
||||||
|
@OdinCaption("Роль")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
public UserRoleDto() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRoleDto(UserRole role) {
|
||||||
|
this.id = role.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRoleDto(String name) {
|
||||||
|
this.id = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getViewValue() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getControllerPath() {
|
||||||
|
return UserController.URL + UserController.ROLES_URL;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package ru.ulstu.user.model;
|
||||||
|
|
||||||
|
import ru.ulstu.core.model.BaseEntity;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "user_sessions")
|
||||||
|
public class UserSession extends BaseEntity {
|
||||||
|
@NotNull
|
||||||
|
@Column(name = "session_id", nullable = false, unique = true)
|
||||||
|
private String sessionId;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(name = "ip_address", nullable = false)
|
||||||
|
private String ipAddress;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(name = "login_time", nullable = false)
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
private Date loginTime;
|
||||||
|
|
||||||
|
@Column(name = "logout_time")
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
private Date logoutTime;
|
||||||
|
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
@JoinColumn(name = "user_id")
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
public UserSession() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserSession(String sessionId, String ipAddress, String host, User user) {
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
this.ipAddress = ipAddress;
|
||||||
|
this.host = host;
|
||||||
|
this.loginTime = new Date();
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIpAddress() {
|
||||||
|
return ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLoginTime() {
|
||||||
|
return loginTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLogoutTime() {
|
||||||
|
return logoutTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
this.logoutTime = new Date();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package ru.ulstu.user.model;
|
||||||
|
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||||
|
import ru.ulstu.odin.model.annotation.OdinDate;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class UserSessionListDto {
|
||||||
|
@OdinCaption("Сессия")
|
||||||
|
private String sessionId;
|
||||||
|
@OdinCaption("Пользователь")
|
||||||
|
private String login;
|
||||||
|
@OdinCaption("IP адрес")
|
||||||
|
private String ipAddress;
|
||||||
|
@OdinCaption("Хост")
|
||||||
|
private String host;
|
||||||
|
@OdinCaption("Вход")
|
||||||
|
@OdinDate(type = OdinDate.OdinDateType.DATETIME)
|
||||||
|
private Date loginTime;
|
||||||
|
@OdinCaption("Выход")
|
||||||
|
@OdinDate(type = OdinDate.OdinDateType.DATETIME)
|
||||||
|
private Date logoutTime;
|
||||||
|
|
||||||
|
public UserSessionListDto(UserSession userSession) {
|
||||||
|
this.sessionId = userSession.getSessionId();
|
||||||
|
this.login = userSession.getUser().getLogin();
|
||||||
|
this.ipAddress = userSession.getIpAddress();
|
||||||
|
this.host = userSession.getHost();
|
||||||
|
this.loginTime = userSession.getLoginTime();
|
||||||
|
this.logoutTime = userSession.getLogoutTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIpAddress() {
|
||||||
|
return ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLoginTime() {
|
||||||
|
return loginTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLogoutTime() {
|
||||||
|
return logoutTime;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package ru.ulstu.user.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.EntityGraph;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import ru.ulstu.user.model.User;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface UserRepository extends JpaRepository<User, Integer> {
|
||||||
|
User findOneByActivationKey(String activationKey);
|
||||||
|
|
||||||
|
List<User> findAllByActivatedIsFalseAndActivationDateBefore(Date date);
|
||||||
|
|
||||||
|
User findOneByResetKey(String resetKey);
|
||||||
|
|
||||||
|
List<User> findAllByResetKeyNotNullAndResetDateBefore(Date date);
|
||||||
|
|
||||||
|
User findOneByEmailIgnoreCase(String email);
|
||||||
|
|
||||||
|
User findOneByLoginIgnoreCase(String login);
|
||||||
|
|
||||||
|
@EntityGraph(attributePaths = "roles")
|
||||||
|
User findOneWithRolesById(int id);
|
||||||
|
|
||||||
|
@EntityGraph(attributePaths = "roles")
|
||||||
|
User findOneWithRolesByLogin(String login);
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.user.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import ru.ulstu.user.model.UserRole;
|
||||||
|
|
||||||
|
public interface UserRoleRepository extends JpaRepository<UserRole, String> {
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package ru.ulstu.user.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import ru.ulstu.user.model.UserSession;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface UserSessionRepository extends JpaRepository<UserSession, Integer> {
|
||||||
|
UserSession findOneBySessionId(String sessionId);
|
||||||
|
|
||||||
|
List<UserSession> findAllByLogoutTimeIsNullAndLoginTimeBefore(Date date);
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package ru.ulstu.user.scheduler;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.core.util.DateUtils;
|
||||||
|
import ru.ulstu.user.model.User;
|
||||||
|
import ru.ulstu.user.repository.UserRepository;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserScheduler {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserScheduler.class);
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
public UserScheduler(UserRepository userRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 0 1 * * ?")
|
||||||
|
public void removeNotActivatedUsers() {
|
||||||
|
log.debug("UserScheduler.removeNotActivatedUsers started");
|
||||||
|
List<User> users = userRepository.findAllByActivatedIsFalseAndActivationDateBefore(
|
||||||
|
DateUtils.instantToDate(Instant.now().minus(3, ChronoUnit.DAYS)));
|
||||||
|
users.forEach(user -> {
|
||||||
|
log.debug("Deleting not activated user {}", user.getLogin());
|
||||||
|
userRepository.delete(user);
|
||||||
|
});
|
||||||
|
log.debug("UserScheduler.removeNotActivatedUsers finished");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 0 1 * * ?")
|
||||||
|
public void removeUnusedRestKeyRequestsOfUsers() {
|
||||||
|
log.debug("UserScheduler.removeUnusedRestKeyRequestsOfUsers started");
|
||||||
|
List<User> users = userRepository.findAllByResetKeyNotNullAndResetDateBefore(
|
||||||
|
DateUtils.instantToDate(Instant.now().minus(7, ChronoUnit.DAYS)));
|
||||||
|
users.forEach(user -> {
|
||||||
|
log.debug("Deleting old reset key request of user {}", user.getLogin());
|
||||||
|
user.setResetKey(null);
|
||||||
|
user.setResetDate(null);
|
||||||
|
userRepository.save(user);
|
||||||
|
});
|
||||||
|
log.debug("UserScheduler.removeUnusedRestKeyRequestsOfUsers finished");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package ru.ulstu.user.scheduler;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.core.util.DateUtils;
|
||||||
|
import ru.ulstu.user.model.UserSession;
|
||||||
|
import ru.ulstu.user.repository.UserSessionRepository;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserSessionScheduler {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserSessionScheduler.class);
|
||||||
|
|
||||||
|
private final UserSessionRepository userSessionRepository;
|
||||||
|
|
||||||
|
public UserSessionScheduler(UserSessionRepository userSessionRepository) {
|
||||||
|
this.userSessionRepository = userSessionRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 0 1 * * ?")
|
||||||
|
public void closeOldSessions() {
|
||||||
|
log.debug("UserSessionScheduler.closeOldSessions started");
|
||||||
|
final List<UserSession> sessions = userSessionRepository.findAllByLogoutTimeIsNullAndLoginTimeBefore(
|
||||||
|
DateUtils.instantToDate(Instant.now().minus(1, ChronoUnit.DAYS)));
|
||||||
|
sessions.forEach(session -> {
|
||||||
|
log.debug("Close session {}", session.getSessionId());
|
||||||
|
session.close();
|
||||||
|
userSessionRepository.save(session);
|
||||||
|
});
|
||||||
|
log.debug("UserSessionScheduler.closeOldSessions finished");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package ru.ulstu.user.service;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.mail.MailProperties;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.thymeleaf.context.Context;
|
||||||
|
import org.thymeleaf.spring4.SpringTemplateEngine;
|
||||||
|
import ru.ulstu.configuration.ApplicationProperties;
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
import ru.ulstu.user.model.User;
|
||||||
|
|
||||||
|
import javax.mail.internet.MimeMessage;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MailService {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(MailService.class);
|
||||||
|
|
||||||
|
private static final String USER = "user";
|
||||||
|
private static final String BASE_URL = "baseUrl";
|
||||||
|
|
||||||
|
private final JavaMailSender javaMailSender;
|
||||||
|
private final SpringTemplateEngine templateEngine;
|
||||||
|
private final MailProperties mailProperties;
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
public MailService(JavaMailSender javaMailSender, SpringTemplateEngine templateEngine,
|
||||||
|
MailProperties mailProperties, ApplicationProperties applicationProperties) {
|
||||||
|
this.javaMailSender = javaMailSender;
|
||||||
|
this.templateEngine = templateEngine;
|
||||||
|
this.mailProperties = mailProperties;
|
||||||
|
this.applicationProperties = applicationProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void sendEmail(String to, String subject, String content) {
|
||||||
|
log.debug("Send email to '{}' with subject '{}'", to, subject);
|
||||||
|
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
|
||||||
|
try {
|
||||||
|
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, false, StandardCharsets.UTF_8.name());
|
||||||
|
message.setTo(to);
|
||||||
|
message.setFrom(mailProperties.getUsername());
|
||||||
|
message.setSubject(subject);
|
||||||
|
message.setText(content, true);
|
||||||
|
javaMailSender.send(mimeMessage);
|
||||||
|
log.debug("Sent email to User '{}'", to);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.warn("Email could not be sent to user '{}'", to, e);
|
||||||
|
} else {
|
||||||
|
log.warn("Email could not be sent to user '{}': {}", to, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void sendEmailFromTemplate(User user, String templateName, String subject) {
|
||||||
|
Context context = new Context();
|
||||||
|
context.setVariable(USER, user);
|
||||||
|
context.setVariable(BASE_URL, applicationProperties.getBaseUrl());
|
||||||
|
String content = templateEngine.process(templateName, context);
|
||||||
|
sendEmail(user.getEmail(), subject, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void sendActivationEmail(User user) {
|
||||||
|
sendEmailFromTemplate(user, "activationEmail", Constants.MAIL_ACTIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public void sendPasswordResetMail(User user) {
|
||||||
|
sendEmailFromTemplate(user, "passwordResetEmail", Constants.MAIL_RESET);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package ru.ulstu.user.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.ulstu.user.model.*;
|
||||||
|
import ru.ulstu.user.model.*;
|
||||||
|
import ru.ulstu.user.repository.UserRoleRepository;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserMapper {
|
||||||
|
private final UserRoleRepository userRoleRepository;
|
||||||
|
|
||||||
|
public UserMapper(UserRoleRepository userRoleRepository) {
|
||||||
|
this.userRoleRepository = userRoleRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<UserRole> rolesFromDto(Set<UserRoleDto> strings) {
|
||||||
|
return Optional.ofNullable(strings).orElse(Collections.emptySet()).stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(role -> userRoleRepository.findOne(role.getId().toString()))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDto userEntityToUserDto(User userEntity) {
|
||||||
|
if (userEntity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new UserDto(userEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserListDto userEntityToUserListDto(User userEntity) {
|
||||||
|
if (userEntity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new UserListDto(userEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UserListDto> userEntitiesToUserListDtos(List<User> userEntities) {
|
||||||
|
return userEntities.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(this::userEntityToUserListDto)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public User userDtoToUserEntity(UserDto userDto) {
|
||||||
|
if (userDto == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final User user = new User();
|
||||||
|
user.setId(userDto.getId());
|
||||||
|
user.setLogin(userDto.getLogin());
|
||||||
|
user.setFirstName(userDto.getFirstName());
|
||||||
|
user.setLastName(userDto.getLastName());
|
||||||
|
user.setEmail(userDto.getEmail());
|
||||||
|
user.setActivated(userDto.isActivated());
|
||||||
|
final Set<UserRole> roles = this.rolesFromDto(userDto.getRoles());
|
||||||
|
if (!roles.isEmpty()) {
|
||||||
|
user.setRoles(roles);
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,285 @@
|
|||||||
|
package ru.ulstu.user.service;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import ru.ulstu.configuration.ApplicationProperties;
|
||||||
|
import ru.ulstu.core.error.EntityIdIsNullException;
|
||||||
|
import ru.ulstu.core.jpa.OffsetablePageRequest;
|
||||||
|
import ru.ulstu.core.model.BaseEntity;
|
||||||
|
import ru.ulstu.core.model.response.PageableItems;
|
||||||
|
import ru.ulstu.user.error.*;
|
||||||
|
import ru.ulstu.user.model.*;
|
||||||
|
import ru.ulstu.user.repository.UserRepository;
|
||||||
|
import ru.ulstu.user.repository.UserRoleRepository;
|
||||||
|
import ru.ulstu.user.util.UserUtils;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class UserService implements UserDetailsService {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserService.class);
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
private final UserRoleRepository userRoleRepository;
|
||||||
|
private final UserMapper userMapper;
|
||||||
|
private final MailService mailService;
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
public UserService(UserRepository userRepository,
|
||||||
|
PasswordEncoder passwordEncoder,
|
||||||
|
UserRoleRepository userRoleRepository,
|
||||||
|
UserMapper userMapper,
|
||||||
|
MailService mailService,
|
||||||
|
ApplicationProperties applicationProperties) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.passwordEncoder = passwordEncoder;
|
||||||
|
this.userRoleRepository = userRoleRepository;
|
||||||
|
this.userMapper = userMapper;
|
||||||
|
this.mailService = mailService;
|
||||||
|
this.applicationProperties = applicationProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
private User getUserByEmail(String email) {
|
||||||
|
return userRepository.findOneByEmailIgnoreCase(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
private User getUserByActivationKey(String activationKey) {
|
||||||
|
return userRepository.findOneByActivationKey(activationKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUserByLogin(String login) {
|
||||||
|
return userRepository.findOneByLoginIgnoreCase(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public UserDto getUserWithRolesById(Integer userId) {
|
||||||
|
final User userEntity = userRepository.findOneWithRolesById(userId);
|
||||||
|
if (userEntity == null) {
|
||||||
|
throw new UserNotFoundException(userId.toString());
|
||||||
|
}
|
||||||
|
return userMapper.userEntityToUserDto(userEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public PageableItems<UserListDto> getAllUsers(int offset, int count) {
|
||||||
|
final Page<User> page = userRepository.findAll(new OffsetablePageRequest(offset, count, new Sort("id")));
|
||||||
|
return new PageableItems<>(page.getTotalElements(), userMapper.userEntitiesToUserListDtos(page.getContent()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public PageableItems<UserRoleDto> getUserRoles() {
|
||||||
|
final List<UserRoleDto> roles = userRoleRepository.findAll().stream()
|
||||||
|
.map(UserRoleDto::new)
|
||||||
|
.sorted(Comparator.comparing(UserRoleDto::getViewValue))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return new PageableItems<>(roles.size(), roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDto createUser(UserDto userDto) {
|
||||||
|
if (userDto.getId() != null) {
|
||||||
|
throw new UserIdExistsException();
|
||||||
|
}
|
||||||
|
if (getUserByLogin(userDto.getLogin()) != null) {
|
||||||
|
throw new UserLoginExistsException(userDto.getLogin());
|
||||||
|
}
|
||||||
|
if (getUserByEmail(userDto.getEmail()) != null) {
|
||||||
|
throw new UserEmailExistsException(userDto.getEmail());
|
||||||
|
}
|
||||||
|
if (!userDto.isPasswordsValid()) {
|
||||||
|
throw new UserPasswordsNotValidOrNotMatchException();
|
||||||
|
}
|
||||||
|
User user = userMapper.userDtoToUserEntity(userDto);
|
||||||
|
user.setActivated(false);
|
||||||
|
user.setActivationKey(UserUtils.generateActivationKey());
|
||||||
|
user.setRoles(Collections.singleton(new UserRole(UserRoleConstants.USER)));
|
||||||
|
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||||
|
user = userRepository.save(user);
|
||||||
|
mailService.sendActivationEmail(user);
|
||||||
|
log.debug("Created Information for User: {}", user.getLogin());
|
||||||
|
return userMapper.userEntityToUserDto(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDto activateUser(String activationKey) {
|
||||||
|
final User user = getUserByActivationKey(activationKey);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserActivationError(activationKey);
|
||||||
|
}
|
||||||
|
user.setActivated(true);
|
||||||
|
user.setActivationKey(null);
|
||||||
|
user.setActivationDate(null);
|
||||||
|
log.debug("Activated user: {}", user.getLogin());
|
||||||
|
return userMapper.userEntityToUserDto(userRepository.save(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDto updateUser(UserDto userDto) {
|
||||||
|
if (userDto.getId() == null) {
|
||||||
|
throw new EntityIdIsNullException();
|
||||||
|
}
|
||||||
|
if (!Objects.equals(
|
||||||
|
Optional.ofNullable(getUserByEmail(userDto.getEmail()))
|
||||||
|
.map(BaseEntity::getId).orElse(userDto.getId()),
|
||||||
|
userDto.getId())) {
|
||||||
|
throw new UserEmailExistsException(userDto.getEmail());
|
||||||
|
}
|
||||||
|
if (!Objects.equals(
|
||||||
|
Optional.ofNullable(getUserByLogin(userDto.getLogin()))
|
||||||
|
.map(BaseEntity::getId).orElse(userDto.getId()),
|
||||||
|
userDto.getId())) {
|
||||||
|
throw new UserLoginExistsException(userDto.getLogin());
|
||||||
|
}
|
||||||
|
User user = userRepository.findOne(userDto.getId());
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserNotFoundException(userDto.getId().toString());
|
||||||
|
}
|
||||||
|
if (applicationProperties.getUndeadUserLogin().equalsIgnoreCase(user.getLogin())) {
|
||||||
|
userDto.setLogin(applicationProperties.getUndeadUserLogin());
|
||||||
|
userDto.setActivated(true);
|
||||||
|
userDto.setRoles(Collections.singletonList(new UserRoleDto(UserRoleConstants.ADMIN)));
|
||||||
|
}
|
||||||
|
user.setLogin(userDto.getLogin());
|
||||||
|
user.setFirstName(userDto.getFirstName());
|
||||||
|
user.setLastName(userDto.getLastName());
|
||||||
|
user.setEmail(userDto.getEmail());
|
||||||
|
if (userDto.isActivated() != user.getActivated()) {
|
||||||
|
if (userDto.isActivated()) {
|
||||||
|
user.setActivationKey(null);
|
||||||
|
user.setActivationDate(null);
|
||||||
|
} else {
|
||||||
|
user.setActivationKey(UserUtils.generateActivationKey());
|
||||||
|
user.setActivationDate(new Date());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user.setActivated(userDto.isActivated());
|
||||||
|
final Set<UserRole> roles = userMapper.rolesFromDto(userDto.getRoles());
|
||||||
|
user.setRoles(roles.isEmpty()
|
||||||
|
? Collections.singleton(new UserRole(UserRoleConstants.USER))
|
||||||
|
: roles);
|
||||||
|
if (!StringUtils.isEmpty(userDto.getOldPassword())) {
|
||||||
|
if (!userDto.isPasswordsValid() || !userDto.isOldPasswordValid()) {
|
||||||
|
throw new UserPasswordsNotValidOrNotMatchException();
|
||||||
|
}
|
||||||
|
if (!passwordEncoder.matches(userDto.getOldPassword(), user.getPassword())) {
|
||||||
|
throw new UserPasswordsNotValidOrNotMatchException();
|
||||||
|
}
|
||||||
|
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||||
|
log.debug("Changed password for User: {}", user.getLogin());
|
||||||
|
}
|
||||||
|
user = userRepository.save(user);
|
||||||
|
log.debug("Changed Information for User: {}", user.getLogin());
|
||||||
|
return userMapper.userEntityToUserDto(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDto updateUserInformation(UserDto userDto) {
|
||||||
|
if (userDto.getId() == null) {
|
||||||
|
throw new EntityIdIsNullException();
|
||||||
|
}
|
||||||
|
if (!Objects.equals(
|
||||||
|
Optional.ofNullable(getUserByEmail(userDto.getEmail()))
|
||||||
|
.map(BaseEntity::getId).orElse(userDto.getId()),
|
||||||
|
userDto.getId())) {
|
||||||
|
throw new UserEmailExistsException(userDto.getEmail());
|
||||||
|
}
|
||||||
|
User user = userRepository.findOne(userDto.getId());
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserNotFoundException(userDto.getId().toString());
|
||||||
|
}
|
||||||
|
user.setFirstName(userDto.getFirstName());
|
||||||
|
user.setLastName(userDto.getLastName());
|
||||||
|
user.setEmail(userDto.getEmail());
|
||||||
|
user = userRepository.save(user);
|
||||||
|
log.debug("Updated Information for User: {}", user.getLogin());
|
||||||
|
return userMapper.userEntityToUserDto(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDto changeUserPassword(UserDto userDto) {
|
||||||
|
if (userDto.getId() == null) {
|
||||||
|
throw new EntityIdIsNullException();
|
||||||
|
}
|
||||||
|
if (!userDto.isPasswordsValid() || !userDto.isOldPasswordValid()) {
|
||||||
|
throw new UserPasswordsNotValidOrNotMatchException();
|
||||||
|
}
|
||||||
|
final String login = UserUtils.getCurrentUserLogin();
|
||||||
|
final User user = userRepository.findOneByLoginIgnoreCase(login);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserNotFoundException(login);
|
||||||
|
}
|
||||||
|
if (!passwordEncoder.matches(userDto.getOldPassword(), user.getPassword())) {
|
||||||
|
throw new UserPasswordsNotValidOrNotMatchException();
|
||||||
|
}
|
||||||
|
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||||
|
log.debug("Changed password for User: {}", user.getLogin());
|
||||||
|
return userMapper.userEntityToUserDto(userRepository.save(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean requestUserPasswordReset(String email) {
|
||||||
|
User user = userRepository.findOneByEmailIgnoreCase(email);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserNotFoundException(email);
|
||||||
|
}
|
||||||
|
if (!user.getActivated()) {
|
||||||
|
throw new UserNotActivatedException();
|
||||||
|
}
|
||||||
|
user.setResetKey(UserUtils.generateResetKey());
|
||||||
|
user.setResetDate(new Date());
|
||||||
|
user = userRepository.save(user);
|
||||||
|
mailService.sendPasswordResetMail(user);
|
||||||
|
log.debug("Created Reset Password Request for User: {}", user.getLogin());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean completeUserPasswordReset(String key, UserResetPasswordDto userResetPasswordDto) {
|
||||||
|
if (!userResetPasswordDto.isPasswordsValid()) {
|
||||||
|
throw new UserPasswordsNotValidOrNotMatchException();
|
||||||
|
}
|
||||||
|
User user = userRepository.findOneByResetKey(key);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserResetKeyError(key);
|
||||||
|
}
|
||||||
|
user.setPassword(passwordEncoder.encode(userResetPasswordDto.getPassword()));
|
||||||
|
user.setResetKey(null);
|
||||||
|
user.setResetDate(null);
|
||||||
|
user = userRepository.save(user);
|
||||||
|
log.debug("Reset Password for User: {}", user.getLogin());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDto deleteUser(Integer userId) {
|
||||||
|
final User user = userRepository.findOne(userId);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserNotFoundException(userId.toString());
|
||||||
|
}
|
||||||
|
if (applicationProperties.getUndeadUserLogin().equalsIgnoreCase(user.getLogin())) {
|
||||||
|
throw new UserIsUndeadException(user.getLogin());
|
||||||
|
}
|
||||||
|
userRepository.delete(user);
|
||||||
|
log.debug("Deleted User: {}", user.getLogin());
|
||||||
|
return userMapper.userEntityToUserDto(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) {
|
||||||
|
final User user = userRepository.findOneByLoginIgnoreCase(username);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserNotFoundException(username);
|
||||||
|
}
|
||||||
|
if (!user.getActivated()) {
|
||||||
|
throw new UserNotActivatedException();
|
||||||
|
}
|
||||||
|
return new org.springframework.security.core.userdetails.User(user.getLogin(),
|
||||||
|
user.getPassword(),
|
||||||
|
Optional.ofNullable(user.getRoles()).orElse(Collections.emptySet()).stream()
|
||||||
|
.map(role -> new SimpleGrantedAuthority(role.getName()))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package ru.ulstu.user.service;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.ulstu.core.model.response.PageableItems;
|
||||||
|
import ru.ulstu.core.jpa.OffsetablePageRequest;
|
||||||
|
import ru.ulstu.user.error.UserNotFoundException;
|
||||||
|
import ru.ulstu.user.model.User;
|
||||||
|
import ru.ulstu.user.model.UserSession;
|
||||||
|
import ru.ulstu.user.model.UserSessionListDto;
|
||||||
|
import ru.ulstu.user.repository.UserSessionRepository;
|
||||||
|
|
||||||
|
import static ru.ulstu.core.util.StreamApiUtils.convert;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class UserSessionService {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserSessionService.class);
|
||||||
|
private final UserSessionRepository userSessionRepository;
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
public UserSessionService(UserSessionRepository userSessionRepository, UserService userService) {
|
||||||
|
this.userSessionRepository = userSessionRepository;
|
||||||
|
this.userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public PageableItems<UserSessionListDto> getSessions(int offset, int count) {
|
||||||
|
final Page<UserSession> page = userSessionRepository.findAll(
|
||||||
|
new OffsetablePageRequest(offset, count, new Sort(Sort.Direction.DESC, "loginTime")));
|
||||||
|
return new PageableItems<>(page.getTotalElements(),
|
||||||
|
convert(page.getContent(), UserSessionListDto::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createUserSession(String sessionId, String login, String ipAddress, String host) {
|
||||||
|
final User user = userService.getUserByLogin(login);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserNotFoundException(login);
|
||||||
|
}
|
||||||
|
userSessionRepository.save(new UserSession(sessionId, ipAddress, host, user));
|
||||||
|
log.debug("User session {} created for user {}@{} ({})", sessionId, login, ipAddress, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeUserSession(String sessionId) {
|
||||||
|
final UserSession userSession = userSessionRepository.findOneBySessionId(sessionId);
|
||||||
|
if (userSession == null) {
|
||||||
|
throw new IllegalArgumentException(String.format("User session %s not found", sessionId));
|
||||||
|
}
|
||||||
|
userSession.close();
|
||||||
|
userSessionRepository.save(userSession);
|
||||||
|
log.debug("User session {} closed", sessionId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package ru.ulstu.user.util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
public class UserUtils {
|
||||||
|
private static final int DEF_COUNT = 20;
|
||||||
|
|
||||||
|
public static String generateActivationKey() {
|
||||||
|
return RandomStringUtils.randomNumeric(DEF_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateResetKey() {
|
||||||
|
return RandomStringUtils.randomNumeric(DEF_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCurrentUserLogin() {
|
||||||
|
final SecurityContext securityContext = SecurityContextHolder.getContext();
|
||||||
|
if (securityContext == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Authentication authentication = securityContext.getAuthentication();
|
||||||
|
if (authentication.getPrincipal() instanceof UserDetails) {
|
||||||
|
final UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
|
||||||
|
return springSecurityUser.getUsername();
|
||||||
|
}
|
||||||
|
if (authentication.getPrincipal() instanceof String) {
|
||||||
|
return (String) authentication.getPrincipal();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
# Server Settings
|
||||||
|
spring.main.banner-mode=off
|
||||||
|
server.port=8443
|
||||||
|
server.http.port=8080
|
||||||
|
spring.http.multipart.maxFileSize=20MB
|
||||||
|
spring.http.multipart.maxRequestSize=20MB
|
||||||
|
# Thymeleaf Settings
|
||||||
|
spring.thymeleaf.cache=false
|
||||||
|
# SSL Settings
|
||||||
|
security.require-ssl=true
|
||||||
|
server.ssl.key-store=classpath:sample.jks
|
||||||
|
server.ssl.key-store-password=secret
|
||||||
|
server.ssl.key-password=password
|
||||||
|
# Log settings (TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF)
|
||||||
|
logging.level.ru.ulstu=DEBUG
|
||||||
|
# Mail Settings
|
||||||
|
spring.mail.host=smtp.yandex.ru
|
||||||
|
spring.mail.port=465
|
||||||
|
spring.mail.username=balance@soft.kitchen
|
||||||
|
spring.mail.password=fkvfpbalance
|
||||||
|
spring.mail.properties.mail.smtp.auth=true
|
||||||
|
spring.mail.properties.mail.smtp.ssl.enable=true
|
||||||
|
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
|
||||||
|
# JPA Settings
|
||||||
|
spring.datasource.url=jdbc:postgresql://localhost:5432/ng-tracker
|
||||||
|
spring.datasource.username=postgres
|
||||||
|
spring.datasource.password=postgres
|
||||||
|
spring.datasource.driverclassName=org.postgresql.Driver
|
||||||
|
spring.jpa.hibernate.ddl-auto=validate
|
||||||
|
# Liquibase Settings
|
||||||
|
liquibase.drop-first=false
|
||||||
|
liquibase.enabled=true
|
||||||
|
liquibase.change-log=classpath:db/changelog-master.xml
|
||||||
|
# Application Settings
|
||||||
|
ng-tracker.base-url=https://127.0.0.1:8443
|
||||||
|
ng-tracker.undead-user-login=admin
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue