diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..5ff6309b7199129c1afe4f4ec1906e640bec48c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..13566b81b018ad684f3a35fee301741b2734c8f4 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000000000000000000000000000000000000..aa00ffab7828f4818589659c804ec2cfd99baed3 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Encoding"> + <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" /> + <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" /> + </component> +</project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000000000000000000000000000000000..82dbec8ad28463aed32007a93ffc07865ae98968 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ExternalStorageConfigurationManager" enabled="true" /> + <component name="MavenProjectsManager"> + <option name="originalFiles"> + <list> + <option value="$PROJECT_DIR$/pom.xml" /> + </list> + </option> + </component> + <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK"> + <output url="file://$PROJECT_DIR$/out" /> + </component> +</project> \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000000000000000000000000000000000000..2b63946d5b31084bbb7dda418ceb3d75eb686373 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Palette2"> + <group name="Swing"> + <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> + </item> + <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true"> + <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> + <initial-values> + <property name="text" value="Button" /> + </initial-values> + </item> + <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="RadioButton" /> + </initial-values> + </item> + <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="CheckBox" /> + </initial-values> + </item> + <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="Label" /> + </initial-values> + </item> + <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> + <preferred-size width="-1" height="20" /> + </default-constraints> + </item> + <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> + </item> + </group> + </component> +</project> \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..94a25f7f4cb416c083d265558da75d457237d671 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="$PROJECT_DIR$" vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..6ea07f5c8a2b8519951d36b1269ba35ab5da5137 --- /dev/null +++ b/pom.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-parent</artifactId> + <version>3.2.2</version> + <relativePath/> <!-- lookup parent from repository --> + </parent> + + <groupId>org.local</groupId> + <artifactId>calculator-endpoint</artifactId> + <version>1.0-SNAPSHOT</version> + + <properties> + <maven.compiler.source>17</maven.compiler.source> + <maven.compiler.target>17</maven.compiler.target> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-jpa</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.springdoc</groupId> + <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> + <version>2.3.0</version> + </dependency> + + <dependency> + <groupId>com.fathzer</groupId> + <artifactId>javaluator</artifactId> + <version>3.0.3</version> + </dependency> + + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>1.18.30</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <scope>runtime</scope> + </dependency> + + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt-api</artifactId> + <version>0.11.5</version> + </dependency> + + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt-impl</artifactId> + <version>0.11.5</version> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt-jackson</artifactId> + <version>0.11.5</version> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>com.auth0</groupId> + <artifactId>java-jwt</artifactId> + <version>4.3.0</version> + </dependency> + + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + </plugin> + </plugins> + </build> + +</project> \ No newline at end of file diff --git a/src/main/java/org/local/Main.java b/src/main/java/org/local/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..d0400fedb693793cb1c16b66514188accf8664bc --- /dev/null +++ b/src/main/java/org/local/Main.java @@ -0,0 +1,13 @@ +package org.local; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + + +@SpringBootApplication(scanBasePackages={"org.local"}) +public class Main { + public static void main(String[] args) { + SpringApplication.run(Main.class,args); + } +} diff --git a/src/main/java/org/local/config/OpenApiConfiguration.java b/src/main/java/org/local/config/OpenApiConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..cdbfde85969b274cd04a861382baf3e62e63edca --- /dev/null +++ b/src/main/java/org/local/config/OpenApiConfiguration.java @@ -0,0 +1,39 @@ +package org.local.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OpenApiConfiguration { + + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI().info(apiInfo()); + } + + private Info apiInfo() { + return new Info() + .title("Calculator API") + .description("API for calculating formulas and storing results for users") + .version("1.0") + .contact(apiContact()) + .license(apiLicence()); + } + + private License apiLicence() { + return new License() + .name("MIT Licence") + .url("https://opensource.org/licenses/mit-license.php"); + } + + private Contact apiContact() { + return new Contact() + .name("Erwan LE TUTOUR") + .email("erwanletutour.elt@gmail.com") + .url("https://github.com/ErwanLT"); + } +} diff --git a/src/main/java/org/local/config/ServerConfig.java b/src/main/java/org/local/config/ServerConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..1dd9aa033f0a4910d601492eb13ba2723fabb9c0 --- /dev/null +++ b/src/main/java/org/local/config/ServerConfig.java @@ -0,0 +1,22 @@ +package org.local.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.stereotype.Component; + +import java.net.InetAddress; + +@Component +public class ServerConfig implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> { + Logger logger = LoggerFactory.getLogger(ServerConfig.class); + @Override + public void customize(ConfigurableServletWebServerFactory factory) { + try { + factory.setAddress(InetAddress.getByName("0.0.0.0")); + }catch (Exception e){ + logger.error(e.getMessage()); + } + } +} diff --git a/src/main/java/org/local/controllers/MainController.java b/src/main/java/org/local/controllers/MainController.java new file mode 100644 index 0000000000000000000000000000000000000000..45736be82301dda618fb4250db6e07b13333de15 --- /dev/null +++ b/src/main/java/org/local/controllers/MainController.java @@ -0,0 +1,29 @@ +package org.local.controllers; + + +import org.local.model.Expression; +import org.local.security.UserDetailsImpl; +import org.local.service.CalculatorService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@RestController +@CrossOrigin(origins = "http://localhost:5173") +public class MainController { + @Autowired + CalculatorService calculatorService; + + @PostMapping( "/calculator") + public Expression saveExpression(@RequestBody Expression expression, + @AuthenticationPrincipal UserDetailsImpl user) { + return calculatorService.CalculateExpression(expression, user.getId()); + } + + @GetMapping( "/calculator") + public Page<Expression> getExpression(@AuthenticationPrincipal UserDetailsImpl user, + @RequestParam int pageNumber){ + return calculatorService.getExpressionsFromUser(user.getId(), pageNumber); + } +} diff --git a/src/main/java/org/local/controllers/UserController.java b/src/main/java/org/local/controllers/UserController.java new file mode 100644 index 0000000000000000000000000000000000000000..b2f151607efd1bde2ad5f88d2ed57f73e76328e2 --- /dev/null +++ b/src/main/java/org/local/controllers/UserController.java @@ -0,0 +1,36 @@ +package org.local.controllers; + +import org.local.model.SignInRequest; +import org.local.security.TokenService; +import org.local.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/users") +@CrossOrigin(origins = "http://localhost:5173") +public class UserController { + + @Autowired + UserService userService; + @Autowired + TokenService tokenService; + + @PostMapping( "/verify") + public Map<String,Object> verifyUser(@RequestBody SignInRequest user){ + Map<String,Object> map = new HashMap<>(); + map.put("token",userService.checkUser(user.getUserName(),user.getPassword())); + return map; + } + + @PostMapping( "/create") + public Map<String,Object> createUser(@RequestBody SignInRequest user){ + Map<String,Object> map = new HashMap<>(); + map.put("token",userService.createUser(user.getUserName(),user.getPassword())); + return map; + } + +} diff --git a/src/main/java/org/local/dao/ExpressionDao.java b/src/main/java/org/local/dao/ExpressionDao.java new file mode 100644 index 0000000000000000000000000000000000000000..a3817f6654ba078a32a06c683205f080f96a612f --- /dev/null +++ b/src/main/java/org/local/dao/ExpressionDao.java @@ -0,0 +1,14 @@ +package org.local.dao; + +import org.local.model.Expression; +import org.local.model.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ExpressionDao extends JpaRepository<Expression,Long> { + Page<Expression> findByUser(User user, Pageable pageable); + +} diff --git a/src/main/java/org/local/dao/UserDao.java b/src/main/java/org/local/dao/UserDao.java new file mode 100644 index 0000000000000000000000000000000000000000..07a15866d79003385b4a1c5a82c608896ebaf828 --- /dev/null +++ b/src/main/java/org/local/dao/UserDao.java @@ -0,0 +1,11 @@ +package org.local.dao; + +import org.local.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface UserDao extends JpaRepository<User,String> { + Optional<User> findByNameAndPassword(String name, String password); + Optional<User> findByName(String name); +} diff --git a/src/main/java/org/local/model/Expression.java b/src/main/java/org/local/model/Expression.java new file mode 100644 index 0000000000000000000000000000000000000000..bccc26cd5a0acac84f67cceef746dd9b506e778c --- /dev/null +++ b/src/main/java/org/local/model/Expression.java @@ -0,0 +1,28 @@ +package org.local.model; + + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.*; +import lombok.Data; + +@Data +@Entity +@Table(name = "Expressions") +public class Expression { + + @Id + @GeneratedValue + @Column(name="EXPRESSION_ID") + private Long id; + + @Column(name="EXPRESSION") + String expression; + + @Column(name="ANSWER") + String answer; + + @JsonBackReference + @ManyToOne + @JoinColumn(name="USER_ID") + User user; +} diff --git a/src/main/java/org/local/model/SignInRequest.java b/src/main/java/org/local/model/SignInRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..c052c58e946441bd488f93502bb86a2e8ee02c7b --- /dev/null +++ b/src/main/java/org/local/model/SignInRequest.java @@ -0,0 +1,9 @@ +package org.local.model; + +import lombok.Data; + +@Data +public class SignInRequest { + private String userName; + private String password; +} diff --git a/src/main/java/org/local/model/User.java b/src/main/java/org/local/model/User.java new file mode 100644 index 0000000000000000000000000000000000000000..8325334e24b177064398cf2b33bbad289039b602 --- /dev/null +++ b/src/main/java/org/local/model/User.java @@ -0,0 +1,40 @@ +package org.local.model; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.persistence.*; +import lombok.Data; +import org.hibernate.annotations.GenericGenerator; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Data +@Entity +@Table(name = "Users") +public class User { + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + @Column(name="USER_ID") + private String id; + + @Column(name="NAME") + private String name; + + @Column(name="IS_ADMIN") + private boolean isAdmin; + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @Column(name="PASSWORD") + private String password; + + @JsonManagedReference + @OneToMany(mappedBy="user") + private List<Expression> expressions = new ArrayList<>(); + + +} diff --git a/src/main/java/org/local/security/AuthTokenFilter.java b/src/main/java/org/local/security/AuthTokenFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..ac834dc20d8bb91c5eaca6545fc4e606cafe6fd6 --- /dev/null +++ b/src/main/java/org/local/security/AuthTokenFilter.java @@ -0,0 +1,78 @@ +package org.local.security; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.local.dao.UserDao; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.web.filter.OncePerRequestFilter; +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.http.HttpHeaders; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.io.IOException; +public class AuthTokenFilter extends OncePerRequestFilter { + + @Value("${keyStr}") + private String keyStr; + + @Autowired + private UserDetailsService userDetailsService; + @Autowired + UserDao userDao; + + private static final Logger logger = LogManager.getLogger(AuthTokenFilter.class); + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Credentials", "true"); + response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); + response.setHeader("Access-Control-Max-Age", "3600"); + response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me"); + + final String header = request.getHeader(HttpHeaders.AUTHORIZATION); + + if (header != null && header.startsWith("Bearer ")) { + String token = header.substring(7); + final String userId = validateTokenAndGetUserId(token); + UserDetails userDetails = UserDetailsImpl.build(userDao.findById(userId).get()); + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + filterChain.doFilter(request, response); + + } + public String validateTokenAndGetUserId(final String token) { + try { + final Algorithm hmac512 = Algorithm.HMAC512(keyStr); + final JWTVerifier verifier = JWT.require(hmac512).build(); + return verifier.verify(token).getSubject(); + } catch (final JWTVerificationException verificationEx) { + logger.warn("token is invalid: {}", verificationEx.getMessage()); + return null; + } + } + +} diff --git a/src/main/java/org/local/security/TokenService.java b/src/main/java/org/local/security/TokenService.java new file mode 100644 index 0000000000000000000000000000000000000000..ab9b469140b9b78e19395f5d5d18206d8ff74237 --- /dev/null +++ b/src/main/java/org/local/security/TokenService.java @@ -0,0 +1,31 @@ +package org.local.security; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.time.Instant; + +@Service +public class TokenService { + @Value("${keyStr}") + private String keyStr; + + private static final Duration JWT_TOKEN_VALIDITY = Duration.ofMinutes(5); + + public String generateToken(final String userId) { + final Instant now = Instant.now(); + final Algorithm hmac512 = Algorithm.HMAC512(keyStr);; + final JWTVerifier verifier = JWT.require(hmac512).build(); + return JWT.create() + .withSubject(userId) + .withIssuer("idatt2105_token_issuer_app") + .withIssuedAt(now) + .withExpiresAt(now.plusMillis(JWT_TOKEN_VALIDITY.toMillis())) + .sign(hmac512); + } + +} diff --git a/src/main/java/org/local/security/UserDetailsImpl.java b/src/main/java/org/local/security/UserDetailsImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..8cddacfdcd0c7bde902eafbfbbe0bdec2fb92128 --- /dev/null +++ b/src/main/java/org/local/security/UserDetailsImpl.java @@ -0,0 +1,69 @@ +package org.local.security; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import lombok.Getter; +import org.local.model.User; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +@Getter +public class UserDetailsImpl implements UserDetails{ + private static final long serialVersionUID = 1L; + + private String id; + + private String username; + + @JsonIgnore + private String password; + + private Collection<? extends GrantedAuthority> authorities; + + public UserDetailsImpl(String id, String username,String password, + Collection<? extends GrantedAuthority> authorities) { + this.id = id; + this.username = username; + this.password = password; + this.authorities = authorities; + } + + public static UserDetailsImpl build(User user) { + List<GrantedAuthority> authorities = new ArrayList<>(); + if(user.isAdmin()){ + authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); + } + return new UserDetailsImpl( + user.getId(), + user.getName(), + user.getPassword(), + authorities); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + +} diff --git a/src/main/java/org/local/security/WebSecurityConfig.java b/src/main/java/org/local/security/WebSecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..32182812e263467d7784b25925e3db9a031d63a0 --- /dev/null +++ b/src/main/java/org/local/security/WebSecurityConfig.java @@ -0,0 +1,74 @@ +package org.local.security; + +import jakarta.transaction.Transactional; +import org.local.dao.UserDao; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +public class WebSecurityConfig { + + @Autowired + UserDao userDao; + + @Bean + public DaoAuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + + authProvider.setUserDetailsService(userDetailsService()); + authProvider.setPasswordEncoder(passwordEncoder()); + return authProvider; + } + + @Bean + public UserDetailsService userDetailsService() { + return username -> UserDetailsImpl.build(userDao.findByName(username).get()); + } + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { + return authConfig.getAuthenticationManager(); + } + + @Bean + public AuthTokenFilter authenticationJwtTokenFilter() { + return new AuthTokenFilter(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + + http.csrf(AbstractHttpConfigurer::disable) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> + auth.requestMatchers("/users/**", + "/swagger-ui/**", + "/v3/api-docs/**") + .permitAll() + .anyRequest().authenticated() + ) + .authenticationProvider(authenticationProvider()) + .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + +} diff --git a/src/main/java/org/local/service/CalculatorService.java b/src/main/java/org/local/service/CalculatorService.java new file mode 100644 index 0000000000000000000000000000000000000000..1d9375cee43e5da44752485d69b7b25d911c0ec9 --- /dev/null +++ b/src/main/java/org/local/service/CalculatorService.java @@ -0,0 +1,45 @@ +package org.local.service; + +import com.fathzer.soft.javaluator.DoubleEvaluator; +import org.local.dao.ExpressionDao; +import org.local.dao.UserDao; +import org.local.model.Expression; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; + + +@Service +public class CalculatorService { + @Autowired + ExpressionDao expressionDao; + @Autowired + UserDao userDao; + Logger logger = LoggerFactory.getLogger(CalculatorService.class); + + public Expression CalculateExpression(Expression expression,String UserId){ + DoubleEvaluator evaluator = new DoubleEvaluator(); + String result = "nan"; + + try { + result = String.valueOf(evaluator.evaluate(expression.getExpression())); + } catch (IllegalArgumentException e){ + logger.error(e.getMessage()); + } + if(result.equals("Infinity")) result = "nan"; + + expression.setAnswer(result); + if(!result.equals("nan")){ + expression.setUser(userDao.findById(UserId).get()); + expressionDao.save(expression); + } + return expression; + } + public Page<Expression> getExpressionsFromUser(String id,int pageNumber){ + return expressionDao.findByUser(userDao.findById(id).get(), PageRequest.of(pageNumber,10)); + } + +} diff --git a/src/main/java/org/local/service/UserService.java b/src/main/java/org/local/service/UserService.java new file mode 100644 index 0000000000000000000000000000000000000000..0d2abc8389de16f0307edf26de984f5d98f23657 --- /dev/null +++ b/src/main/java/org/local/service/UserService.java @@ -0,0 +1,57 @@ +package org.local.service; + +import org.local.dao.UserDao; +import org.local.model.User; +import org.local.security.UserDetailsImpl; +import org.local.security.TokenService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +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; + +@Service +public class UserService { + @Autowired + UserDao userDao; + + @Autowired + AuthenticationManager authenticationManager; + + @Autowired + private UserDetailsService userDetailsService; + + @Autowired + PasswordEncoder encoder; + + @Autowired + TokenService tokenService; + + Logger logger = LoggerFactory.getLogger(UserService.class); + + public String checkUser(String name, String password){ + Authentication authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(name, password)); + UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); + + return tokenService.generateToken(userDetails.getId()); + } + public String createUser(String name, String password){ + + User user = new User(); + user.setName(name); + user.setPassword(encoder.encode(password)); + + User userResult = userDao.save(user); + + return tokenService.generateToken(userResult.getId()); + } + + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000000000000000000000000000000000000..ade1b60ca02eef37cd386e303ce05c65ed56d61c --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,18 @@ +keyStr = testsecrettestsecrettestsecrettestsecrettestsecret +logging.level.org.springframework.web: TRACE + + +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= + +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto= update + +spring.h2.console.enabled=true +# default path: h2-console +spring.h2.console.path=/h2-ui + +