diff --git a/homework-g597-sigareva/pom.xml b/homework-g597-sigareva/pom.xml index 0817be603..0ddfd9962 100644 --- a/homework-g597-sigareva/pom.xml +++ b/homework-g597-sigareva/pom.xml @@ -12,12 +12,41 @@ homework-g597-sigareva + + 1.4.2.RELEASE + + ru.mipt.java2016 homework-base 1.0.0 + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-jdbc + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-security + ${spring.boot.version} + + + com.zaxxer + HikariCP + 2.5.1 + + + com.h2database + h2 + 1.4.193 + ru.mipt.java2016 homework-tests diff --git a/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/BillingDao.java b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/BillingDao.java new file mode 100644 index 000000000..95d454dac --- /dev/null +++ b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/BillingDao.java @@ -0,0 +1,93 @@ +package ru.mipt.java2016.homework.g597.sigareva.task4; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Repository; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Repository +public class BillingDao { + private static final Logger LOG = LoggerFactory.getLogger(BillingDao.class); + + @Autowired + private DataSource dataSource; + + private JdbcTemplate jdbcTemplate; + + @PostConstruct + public void postConstruct() { + jdbcTemplate = new JdbcTemplate(dataSource, false); + initSchema(); + } + + public void initSchema() { + LOG.trace("Initializing schema"); + jdbcTemplate.execute("CREATE SCHEMA IF NOT EXISTS billing"); + jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS billing.users " + + "(username VARCHAR PRIMARY KEY, password VARCHAR, enabled BOOLEAN)"); + jdbcTemplate.execute("DELETE FROM billing.users WHERE username = 'username'"); + jdbcTemplate.update("INSERT INTO billing.users VALUES ('username', 'password', TRUE)"); + } + + public BillingUser loadUser(String username) throws EmptyResultDataAccessException { + LOG.trace("Querying for user " + username); + return jdbcTemplate.queryForObject( + "SELECT username, password, enabled FROM billing.users WHERE username = ?", + new Object[]{username}, + new RowMapper() { + @Override + public BillingUser mapRow(ResultSet rs, int rowNum) throws SQLException { + return new BillingUser( + rs.getString("username"), + rs.getString("password"), + rs.getBoolean("enabled") + ); + } + } + ); + } + + public Double getVariable(String username, String variableName) throws EmptyResultDataAccessException { + LOG.trace("Querying for user " + username); + return jdbcTemplate.queryForObject( + "SELECT username, variableName, value FROM billing.usersVariables WHERE username = '" + username + "' " + + "AND variableName = '" + variableName + "'", + new Object[]{}, + new RowMapper() { + @Override + public Double mapRow(ResultSet rs, int rowNum) throws SQLException { + return new Double( + rs.getString("value") + ); + } + } + ); + } + + public void registerNewUser(String user, String password) { + jdbcTemplate.update("INSERT INTO billing.users VALUES ('" + user + "', '" + password + "', TRUE)"); + } + + public void addValue(String user, String variableName, String value) { + System.out.println(user); + System.out.println(variableName); + jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS billing.usersVariables " + + "(username VARCHAR, variableName VARCHAR PRIMARY KEY, value FLOAT)"); + jdbcTemplate.execute("DELETE FROM billing.usersVariables WHERE variableName = '" + variableName + "' " + + "AND username = '" + user + "'"); + jdbcTemplate.update("INSERT INTO billing.usersVariables VALUES ('" + user + "', '" + variableName + "', '" + value + "')"); + } + + public void deleteVariable(String user, String variableName) { + jdbcTemplate.execute("DELETE FROM billing.usersVariables WHERE variableName = '" + variableName + "' " + + "AND username = '" + user + "'"); + } +} \ No newline at end of file diff --git a/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/BillingDatabaseConfiguration.java b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/BillingDatabaseConfiguration.java new file mode 100644 index 000000000..ba1ff210e --- /dev/null +++ b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/BillingDatabaseConfiguration.java @@ -0,0 +1,26 @@ +package ru.mipt.java2016.homework.g597.sigareva.task4; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.sql.DataSource; + +@Configuration +public class BillingDatabaseConfiguration { + @Bean + public DataSource billingDataSource( + @Value("${ru.mipt.java2016.homework.g597.sigareva.task4.jdbcUrl}") String jdbcUrl, + @Value("${ru.mipt.java2016.homework.g597.sigareva.task4.username:}") String username, + @Value("${ru.mipt.java2016.homework.g597.sigareva.task4.password:}") String password + ) { + HikariConfig config = new HikariConfig(); + config.setDriverClassName(org.h2.Driver.class.getName()); + config.setJdbcUrl(jdbcUrl); + config.setUsername(username); + config.setPassword(password); + return new HikariDataSource(config); + } +} \ No newline at end of file diff --git a/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/BillingUser.java b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/BillingUser.java new file mode 100644 index 000000000..4ee119f56 --- /dev/null +++ b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/BillingUser.java @@ -0,0 +1,69 @@ +package ru.mipt.java2016.homework.g597.sigareva.task4; + +public class BillingUser { + private final String username; + private final String password; + private final boolean enabled; + + public BillingUser(String username, String password, boolean enabled) { + if (username == null) { + throw new IllegalArgumentException("Null username is not allowed"); + } + if (password == null) { + throw new IllegalArgumentException("Null password is not allowed"); + } + this.username = username; + this.password = password; + this.enabled = enabled; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public boolean isEnabled() { + return enabled; + } + + @Override + public String toString() { + return "BillingUser{" + + "username='" + username + '\'' + + ", password='" + password + '\'' + + ", enabled=" + enabled + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BillingUser that = (BillingUser) o; + + if (enabled != that.enabled) { + return false; + } + if (!username.equals(that.username)) { + return false; + } + return password.equals(that.password); + + } + + @Override + public int hashCode() { + int result = username.hashCode(); + result = 31 * result + password.hashCode(); + result = 31 * result + (enabled ? 1 : 0); + return result; + } +} diff --git a/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/CalculatorController.java b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/CalculatorController.java new file mode 100644 index 000000000..4ae2aea17 --- /dev/null +++ b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/CalculatorController.java @@ -0,0 +1,75 @@ +package ru.mipt.java2016.homework.g597.sigareva.task4; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.*; +import ru.mipt.java2016.homework.base.task1.Calculator; +import ru.mipt.java2016.homework.base.task1.ParsingException; + +import java.io.IOException; +import java.util.Vector; + +@RestController +public class CalculatorController { + private static final Logger LOG = LoggerFactory.getLogger(CalculatorController.class); + @Autowired + private MyCalculator calculator; + + @Autowired + private BillingDao billingDao; + + @RequestMapping(path = "/ping", method = RequestMethod.GET, produces = "text/plain") + public String echo() { + return "OK\n"; + } + + @RequestMapping(path = "/", method = RequestMethod.GET, produces = "text/html") + public String main(@RequestParam(required = false) String name) { + if (name == null) { + name = "world"; + } + return "" + + "FediqApp" + + "

Hello, " + name + "!

" + + ""; + } + + @RequestMapping(path = "/eval", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") + public String eval(Authentication userName, @RequestBody String expression) throws ParsingException { // + AUTH + LOG.debug("Evaluation request: [" + expression + "]"); + double result = calculator.calculate(expression, userName.getName()); // username + LOG.trace("Result: " + result); + return Double.toString(result) + "\n"; + } + + @RequestMapping(path = "/registration", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") + public void registration(@RequestParam(value = "arguments") Vector arguments) throws IOException { + try { + if (arguments.size() != 2) { + throw new IOException("Mistake"); + } else { + billingDao.registerNewUser(arguments.firstElement(), arguments.lastElement()); + } + } catch (IOException e) { + throw new IllegalStateException("Can't understand"); + } + } + + @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.PUT, consumes = "text/plain", produces = "text/plain") + public void addingVariable(Authentication userName, @PathVariable String variableName, @RequestBody String value) throws IOException { + billingDao.addValue(userName.getName(), variableName, value); + } + + @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.DELETE, consumes = "text/plain", produces = "text/plain") + public void deleteVariable(Authentication userName, @PathVariable String variableName) throws IOException { + billingDao.deleteVariable(userName.getName(), variableName); + } + + @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.GET/*, consumes = "text/plain", produces = "text/plain"*/) + public String getVariable(Authentication userName, @PathVariable String variableName) throws IOException { + System.out.println("ALIVE"); + return billingDao.getVariable(userName.getName(), variableName).toString(); + } +} \ No newline at end of file diff --git a/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/MyApplication.java b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/MyApplication.java new file mode 100644 index 000000000..fe7550d6d --- /dev/null +++ b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/MyApplication.java @@ -0,0 +1,41 @@ +package ru.mipt.java2016.homework.g597.sigareva.task4; + + import org.springframework.beans.factory.annotation.Value; + import org.springframework.boot.Banner; + import org.springframework.boot.SpringApplication; + import org.springframework.boot.autoconfigure.EnableAutoConfiguration; + import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; + import org.springframework.context.annotation.Bean; + import org.springframework.context.annotation.ComponentScan; + import org.springframework.context.annotation.Configuration; + +/** + * curl http://localhost:9001/eval \ + * -X POST \ + * -H "Content-Type: text/plain" \ + * -H "Authorization: Basic $(echo -n "username:password" | base64)" \ + * --data-raw "44*3+2" + */ + +@EnableAutoConfiguration +@Configuration +@ComponentScan(basePackageClasses = MyApplication.class) +public class MyApplication { + + @Bean + public MyCalculator calculator() { + return new MyCalculator(); + } + + @Bean + public EmbeddedServletContainerCustomizer customizer( + @Value("${ru.mipt.java2016.homework.g597.sigareva.task4.httpPort:9001}") int port) { + return container -> container.setPort(port); + } + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(MyApplication.class); + application.setBannerMode(Banner.Mode.OFF); + application.run(args); + } +} diff --git a/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/MyCalculator.java b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/MyCalculator.java new file mode 100644 index 000000000..243e752c1 --- /dev/null +++ b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/MyCalculator.java @@ -0,0 +1,215 @@ +package ru.mipt.java2016.homework.g597.sigareva.task4; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; +import java.util.Vector; + +public class MyCalculator { + Map Variables = new HashMap<>(); + + static final String operations = "()+-/*_,"; + static final String expression = "2 * (3 - 7)"; + + static Stack operationsStack; + static Stack resultStack; + + @Autowired + private BillingDao billingDao; + + StringBuilder newExpression = new StringBuilder(expression); + + public boolean checkSymbols(StringBuilder newExpression) { + boolean rightSymbolFlag = false; + + for (int i = 0; i < newExpression.length(); ++i) { + rightSymbolFlag = false; + + if (Character.isDigit(newExpression.charAt(i))) { + rightSymbolFlag = true; + } + + for (char j = 'a'; j <= 'z'; ++j) { + if (newExpression.charAt(i) == j) { + rightSymbolFlag = true; + } + } + + for (int j = 0; j < operations.length(); ++j) { + if (operations.charAt(j) == newExpression.charAt(i)) { + rightSymbolFlag = true; + } + } + + if (!rightSymbolFlag) { + break; + } + } + return rightSymbolFlag; + } + + + public double calculate(String expression, String username) { // username + String newExpression = new String(); + + newExpression = expression.replaceAll(" ", ""); + if (newExpression.charAt(0) == '('){ + return calculate(expression.substring(1, expression.length() - 1), username); + } + StringBuilder resultExpression = new StringBuilder(); + if (newExpression.charAt(0) == '-') { + resultExpression.append("0"); + } + resultExpression.append(newExpression.charAt(0)); + + for (int i = 1; i < newExpression.length(); ++i) { + if (newExpression.charAt(i - 1) == '(' && newExpression.charAt(i) == '-') { + resultExpression.append("0"); + } + resultExpression.append(newExpression.charAt(i)); + } + + StringBuilder name = new StringBuilder(); + StringBuilder newResult = new StringBuilder(); + + for (int i = 0; i < resultExpression.length(); ++i){ + boolean operatorFlag = false; + for (int j = 0; j < operations.length(); ++j) { + if (resultExpression.charAt(i) == operations.charAt(j)) { + operatorFlag = true; + } + } + if(operatorFlag) { + if (name.length() > 0) { + try { + Double value = billingDao.getVariable(username, name.toString()); + newResult.append(value.toString()); + } catch (EmptyResultDataAccessException e) { + newResult.append(name); + } + name = new StringBuilder(); + } + newResult.append(resultExpression.charAt(i)); + } else { + name.append(resultExpression.charAt(i)); + } + } + + if (name.length() > 0) { + try { + Double value = billingDao.getVariable(username, name.toString()); + newResult.append(value.toString()); + } catch (EmptyResultDataAccessException e) { + newResult.append(name); + } + name = new StringBuilder(); + } + + resultExpression = newResult; + System.out.println(resultExpression); + + Integer balance = 0; + for (int i = 0; i < resultExpression.length(); ++i) { + if (resultExpression.charAt(i) == '(') { + ++balance; + } + if (newExpression.charAt(i) == ')') { + --balance; + } + if (resultExpression.charAt(i) == '+' && balance == 0) { + String firstPartOfExpression = resultExpression.substring(0, i); + String secondPartOfString = resultExpression.substring(i + 1, resultExpression.length()); + return calculate(firstPartOfExpression, username) + calculate(secondPartOfString, username); + } + if (resultExpression.charAt(i) == '-' && balance == 0) { + String firstPartOfExpression = resultExpression.substring(0, i); + String secondPartOfString = resultExpression.substring(i + 1, resultExpression.length()); + return calculate(firstPartOfExpression, username) - calculate(secondPartOfString, username); + } + } + balance = 0; + for (int i = 0; i < resultExpression.length(); ++i) { + if (resultExpression.charAt(i) == '(') { + ++balance; + } + if (newExpression.charAt(i) == ')') { + --balance; + } + if (resultExpression.charAt(i) == '*' && balance == 0) { + String firstPartOfExpression = resultExpression.substring(0, i); + String secondPartOfString = resultExpression.substring(i + 1, resultExpression.length()); + return calculate(firstPartOfExpression, username) * calculate(secondPartOfString, username); + } + if (resultExpression.charAt(i) == '/' && balance == 0) { + String firstPartOfExpression = resultExpression.substring(0, i); + String secondPartOfString = resultExpression.substring(i + 1, resultExpression.length()); + return calculate(firstPartOfExpression, username) / calculate(secondPartOfString, username); + } + } + /*if (resultExpression.charAt(0) == '(') { + return calculate(resultExpression.substring(1, resultExpression.length() - 1)); + } else {*/ + if (Character.isDigit((resultExpression.charAt(0)))) { + return Double.parseDouble(resultExpression.toString()); + } else { + String functionName = resultExpression.substring(0, resultExpression.indexOf("(")); + String arguments = resultExpression.substring(resultExpression.indexOf("(") + 1, resultExpression.length() - 1); + Vector functionArguments = new Vector<>(); + Integer argumentBalance = 0; + Integer start = 0; + for (int i = 0; i < arguments.length(); ++i) { + if (arguments.charAt(i) == '(') { + ++argumentBalance; + } + if (arguments.charAt(i) == ')') { + --argumentBalance; + } + if (arguments.charAt(i) == ',' && argumentBalance == 0) { + functionArguments.add(calculate(arguments.substring(start, i), username)); + start = i + 1; + } + } + functionArguments.add(calculate(arguments.substring(start, arguments.length()), username)); + if (functionName.equals("sin")) { + return Math.sin(functionArguments.lastElement()); + } + if (functionName.equals("cos")) { + return Math.cos(functionArguments.lastElement()); + } + if (functionName.equals("tg")) { + return Math.tan(functionArguments.lastElement()); + } + if (functionName.equals("sqrt")) { + return Math.sqrt(functionArguments.lastElement()); + } + if (functionName.equals("pow")) { + return Math.pow(functionArguments.firstElement(), functionArguments.lastElement()); + } + if (functionName.equals("abs")) { + return Math.abs(functionArguments.lastElement()); + } + if (functionName.equals("sign")) { + return Math.signum(functionArguments.lastElement()); + } + if (functionName.equals("log")) { + return Math.log(functionArguments.firstElement()) / Math.log(functionArguments.lastElement()); + } + if (functionName.equals("log2")) { + return Math.log(functionArguments.lastElement()) / Math.log(2); + } + if (functionName.equals("rnd")) { + return Math.random(); + } + if (functionName.equals("max")) { + return Math.max(functionArguments.firstElement(), functionArguments.lastElement()); + } + if (functionName.equals("min")) { + return Math.min(functionArguments.firstElement(), functionArguments.lastElement()); + } + return 0; + } + } +} diff --git a/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/SecurityServiceConfiguration.java b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/SecurityServiceConfiguration.java new file mode 100644 index 000000000..d51ddd873 --- /dev/null +++ b/homework-g597-sigareva/src/main/java/ru/mipt/java2016/homework/g597/sigareva/task4/SecurityServiceConfiguration.java @@ -0,0 +1,55 @@ +package ru.mipt.java2016.homework.g597.sigareva.task4; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import java.util.Collections; + +@Configuration +@EnableWebSecurity +public class SecurityServiceConfiguration extends WebSecurityConfigurerAdapter { + private static final Logger LOG = LoggerFactory.getLogger(SecurityServiceConfiguration.class); + + @Autowired + private BillingDao billingDao; + + @Override + protected void configure(HttpSecurity http) throws Exception { + LOG.info("Configuring security"); + http + .httpBasic().realmName("Calculator").and() + .formLogin().disable() + .logout().disable() + .csrf().disable() + .authorizeRequests() + .antMatchers("/eval/**").authenticated() + .anyRequest().permitAll(); + } + + @Autowired + public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception { + LOG.info("Registering global user details service"); + auth.userDetailsService(username -> { + try { + BillingUser user = billingDao.loadUser(username); + return new User( + user.getUsername(), + user.getPassword(), + Collections.singletonList(() -> "AUTH") + ); + } catch (EmptyResultDataAccessException e) { + LOG.warn("No such user: " + username); + throw new UsernameNotFoundException(username); + } + }); + } +} \ No newline at end of file