diff --git a/homework-g597-spirin/pom.xml b/homework-g597-spirin/pom.xml index 8e3251357..a8217888b 100644 --- a/homework-g597-spirin/pom.xml +++ b/homework-g597-spirin/pom.xml @@ -11,6 +11,10 @@ homework-g597-spirin + + 1.4.2.RELEASE + + ru.mipt.java2016 @@ -30,6 +34,40 @@ guava 20.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 + 1.0.0 + test + + \ No newline at end of file diff --git a/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/Application.java b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/Application.java new file mode 100644 index 000000000..75dc2ed38 --- /dev/null +++ b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/Application.java @@ -0,0 +1,39 @@ +package ru.mipt.java2016.homework.g597.spirin.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; + +import ru.mipt.java2016.homework.base.task1.Calculator; + +/** + * Created by whoami on 12/13/16. + */ + +@EnableAutoConfiguration +@Configuration +@ComponentScan(basePackageClasses = Application.class) +public class Application { + + @Bean + public Calculator calculator() { + return new Evaluator(); + } + + @Bean + public EmbeddedServletContainerCustomizer customizer( + @Value("${ru.mipt.java2016.homework.g597.spirin.task4.httpPort:9001}") int port) { + return container -> container.setPort(port); + } + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(Application.class); + application.setBannerMode(Banner.Mode.OFF); + application.run(args); + } +} \ No newline at end of file diff --git a/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/BillingDao.java b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/BillingDao.java new file mode 100644 index 000000000..c0dcac9f8 --- /dev/null +++ b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/BillingDao.java @@ -0,0 +1,163 @@ +package ru.mipt.java2016.homework.g597.spirin.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; +import java.util.List; + +/** + * Created by whoami on 12/13/16. + */ + +@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.info("Initializing schema"); + jdbcTemplate.execute("CREATE SCHEMA IF NOT EXISTS billing"); + + // User table + jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS billing.users " + + "(username VARCHAR PRIMARY KEY, password VARCHAR)"); + + // Update initial (username, password) + jdbcTemplate.execute("DELETE FROM billing.users WHERE username = 'username'"); + jdbcTemplate.execute("INSERT INTO billing.users VALUES ('username', 'password')"); + + // Variable table + jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS billing.variables " + + "(username VARCHAR, variable VARCHAR, value FLOAT)"); + + // Function table + jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS billing.functions " + + "(username VARCHAR, function VARCHAR, arity INTEGER, body VARCHAR)"); + } + + // Load user from user table + public BillingUser loadUser(String username) throws EmptyResultDataAccessException { + LOG.info("Querying for user " + username); + return jdbcTemplate.queryForObject( + "SELECT username, password 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") + ); + } + } + ); + } + + // Put user into user table + public void putUser(String username, String password) { + LOG.info("Putting user (username, password) into table"); + jdbcTemplate.execute("INSERT INTO billing.users VALUES ('" + + username + "', '" + password + "')"); + } + + // Get value of variable of particular user + public Double getVariable(String username, String variable) throws EmptyResultDataAccessException { + LOG.info("Querying for variable " + variable + " of user " + username); + return jdbcTemplate.queryForObject( + "SELECT value FROM billing.variables WHERE username = '" + username + + "' AND variable = '" + variable + "'", + new Double[]{}, + new RowMapper() { + @Override + public Double mapRow(ResultSet rs, int rowNum) throws SQLException { + return new Double( + rs.getString("value").toString() + ); + } + } + ); + } + + // Put variable of particular user + public void putVariable(String username, String variable, Double value) { + LOG.info("Putting variable " + variable + " of user " + username); + deleteVariable(username, variable); + jdbcTemplate.execute("INSERT INTO billing.variables VALUES ('" + + username + "', '" + variable + "', " + value.toString() + ")"); + } + + // Delete variable of particular user + public void deleteVariable(String username, String variable) { + LOG.info("Deleting variable " + variable + " of user " + username); + jdbcTemplate.execute("DELETE FROM billing.variables WHERE username = '" + + username + "' AND variable = '" + variable + "'"); + } + + // Get all variables from the service of particular user + public String[] getAllVariables(String username) { + LOG.info("Querying for all variables of user " + username); + + List queryResult = jdbcTemplate.queryForList( + "SELECT variable FROM billing.variables WHERE username = '" + + username + "'"); + + String[] variables = new String[queryResult.size()]; + + for (int i = 0; i < queryResult.size(); ++i) { + variables[i] = queryResult.get(i).toString(); + } + + return variables; + } + + // Get function of particular user + public String getFunction(String username, String function) { + LOG.info("Querying for function " + function + " of user " + username); + return jdbcTemplate.queryForObject( + "SELECT body FROM billing.functions WHERE username = '" + username + + "' AND function = '" + function + "'", + new String[]{}, + new RowMapper() { + @Override + public String mapRow(ResultSet rs, int rowNum) throws SQLException { + return rs.getString("body"); + } + } + ); + } + + // Put function of particular user + public void putFunction(String username, String function, Integer arity, String body) { + LOG.info("Putting function " + function + " of user " + username); + deleteFunction(username, function); + jdbcTemplate.execute("INSERT INTO billing.functions VALUES ('" + + username + "', '" + function + "', " + arity.toString() + ", '" + + body + "')"); + } + + // Delete function of particular user + public void deleteFunction(String username, String function) { + LOG.info("Deleting function " + function + " of user " + username); + jdbcTemplate.execute("DELETE FROM billing.functions WHERE username = '" + + username + "' AND function = '" + function + "'"); + } +} diff --git a/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/BillingDatabaseConfiguration.java b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/BillingDatabaseConfiguration.java new file mode 100644 index 000000000..8528d1052 --- /dev/null +++ b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/BillingDatabaseConfiguration.java @@ -0,0 +1,30 @@ +package ru.mipt.java2016.homework.g597.spirin.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; + +/** + * Created by whoami on 12/13/16. + */ + +@Configuration +public class BillingDatabaseConfiguration { + @Bean + public DataSource billingDataSource( + @Value("jdbc:h2:mem:testdb") String jdbcUrl, + @Value("${ru.mipt.java2016.homework.g597.spirin.task4.username:}") String username, + @Value("${ru.mipt.java2016.homework.g597.spirin.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); + } +} diff --git a/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/BillingUser.java b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/BillingUser.java new file mode 100644 index 000000000..4de2d352a --- /dev/null +++ b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/BillingUser.java @@ -0,0 +1,63 @@ +package ru.mipt.java2016.homework.g597.spirin.task4; + +/** + * Created by whoami on 12/13/16. + */ + +public class BillingUser { + private final String username; + private final String password; + + public BillingUser(String username, String password) { + 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; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + @Override + public String toString() { + return "BillingUser{" + + "username='" + username + '\'' + + ", password='" + password + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BillingUser that = (BillingUser) o; + + 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(); + return result; + } +} + diff --git a/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/CalculatorController.java b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/CalculatorController.java new file mode 100644 index 000000000..db55ba517 --- /dev/null +++ b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/CalculatorController.java @@ -0,0 +1,115 @@ +package ru.mipt.java2016.homework.g597.spirin.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; + +/** + * Created by whoami on 12/13/16. + */ + +@RestController +public class CalculatorController { + private static final Logger LOG = LoggerFactory.getLogger(CalculatorController.class); + @Autowired + private Calculator 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 "" + + "mountain-viewer app" + + "

Hello, " + name + "!

" + + ""; + } + + @RequestMapping(path = "/eval", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") + public String eval(Authentication authentication, @RequestBody String expression) throws ParsingException { + LOG.info("Evaluation request: [" + expression + "]"); + + String username = authentication.getName(); + double result = calculator.calculate(expression); + + LOG.info("Result: " + result); + + return Double.toString(result) + "\n"; + } + + @RequestMapping(path = "/signup", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") + public void signUp(@RequestParam(value = "args") String[] arguments) throws ParsingException { + String username = arguments[0]; + String password = arguments[1]; + + LOG.info(username); + LOG.info(password); + + billingDao.putUser(username, password); + } + + @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.PUT, + consumes = "text/plain", produces = "text/plain") + public void putVariable(Authentication authentication, + @PathVariable String variableName, @RequestParam(value = "value") Double value) { + String username = authentication.getName(); + billingDao.putVariable(username, variableName, value); + } + + @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.GET, + consumes = "text/plain", produces = "text/plain") + public String getVariable(Authentication authentication, @PathVariable String variableName) { + String username = authentication.getName(); + Double res = billingDao.getVariable(username, variableName); + return res.toString() + "\n"; + } + + @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.DELETE, + consumes = "text/plain", produces = "text/plain") + public void deleteVariable(Authentication authentication, @PathVariable String variableName) { + String username = authentication.getName(); + billingDao.deleteVariable(username, variableName); + } + + @RequestMapping(path = "/variable", method = RequestMethod.GET, consumes = "text/plain", produces = "text/plain") + public String getAllVariables(Authentication authentication) { + String username = authentication.getName(); + String[] res = billingDao.getAllVariables(username); + return res.toString() + "\n"; + } + + @RequestMapping(path = "/function/{functionName}", method = RequestMethod.PUT, + consumes = "text/plain", produces = "text/plain") + public void putFunction(Authentication authentication, @PathVariable String functionName, + @RequestParam(value = "arity") Integer arity, @RequestBody String body) { + String username = authentication.getName(); + LOG.trace(username); + billingDao.putFunction(username, functionName, arity, body); + } + + @RequestMapping(path = "/function/{functionName}", method = RequestMethod.DELETE, + consumes = "text/plain", produces = "text/plain") + public void deleteFunction(Authentication authentication, @PathVariable String functionName) { + String username = authentication.getName(); + billingDao.deleteFunction(username, functionName); + } + + @RequestMapping(path = "/function/{functionName}", method = RequestMethod.GET, + consumes = "text/plain", produces = "text/plain") + public String getFunction(Authentication authentication, @PathVariable String functionName) { + String username = authentication.getName(); + String res = billingDao.getFunction(username, functionName); + return res + "\n"; + } +} \ No newline at end of file diff --git a/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/Evaluator.java b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/Evaluator.java new file mode 100644 index 000000000..b3f328ed6 --- /dev/null +++ b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/Evaluator.java @@ -0,0 +1,43 @@ +package ru.mipt.java2016.homework.g597.spirin.task4; + +import ru.mipt.java2016.homework.base.task1.Calculator; +import ru.mipt.java2016.homework.base.task1.ParsingException; + +/** + * Created by whoami on 12/13/16. + */ + +public class Evaluator implements Calculator { + @Override + public double calculate(String expression) throws ParsingException { + if (expression == null) { + throw new ParsingException("Null can't be interpreted as an expression"); + } + + if (!checkIfBalanceCorrect(expression)) { + throw new ParsingException("Incorrect bracket sequence"); + } + + return new EvaluatorHelper(expression).evaluate(); + } + + // Check correctness of bracket sequence + private boolean checkIfBalanceCorrect(String expression) { + int balance = 0; + + for (Character ch : expression.toCharArray()) { + if (balance < 0) { + return false; + } + if (ch == '(') { + balance++; + } else if (ch == ')') { + balance--; + } + + } + + return balance == 0; + } +} + diff --git a/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/EvaluatorHelper.java b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/EvaluatorHelper.java new file mode 100644 index 000000000..97a9317f3 --- /dev/null +++ b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/EvaluatorHelper.java @@ -0,0 +1,197 @@ +package ru.mipt.java2016.homework.g597.spirin.task4; + +import ru.mipt.java2016.homework.base.task1.ParsingException; +import java.util.Stack; + +/** + * Created by whoami on 12/13/16. + */ + +// Algorithm based on grammar expressions + +// Grammar: +// expression = term | expression `+` term | expression `-` term | expression, expression +// term = factor | term `*` factor | term `/` factor +// factor = `+` factor | `-` factor | `(` expression `)` +// | number | functionName factor + +class EvaluatorHelper { + + // Class data members + private int pos; + private char ch; + private final String expression; + + private final Stack functions = new Stack<>(); + + // Constructor + EvaluatorHelper(String expression) { + this.expression = expression; + pos = -1; + } + + // Access to next character + private void getNextChar() throws ParsingException { + if (++pos < expression.length()) { + ch = expression.charAt(pos); + } else { + // Just to stop execution + ch = '&'; + } + } + + // Wait for expected character + private boolean tryCaptureChar(char charToBeCaptured) throws ParsingException { + // Skip all whitespace characters + while (Character.isWhitespace(ch)) { + getNextChar(); + } + + if (ch == charToBeCaptured) { + getNextChar(); + return true; + } + return false; + } + + // Main logic function + double evaluate() throws ParsingException { + getNextChar(); + + double result = processExpression(); + if (pos < expression.length()) { + throw new ParsingException("Unexpected appearance of: " + ch); + } + + return result; + } + + // Calculate the value of call function(a, b) + private double performBinaryFunction(String function, double a, double b) throws ParsingException { + if (function.equals("max")) { + return Math.max(a, b); + } else if (function.equals("min")) { + return Math.min(a, b); + } else if (function.equals("pow")) { + return Math.pow(a, b); + } else if (function.equals("log")) { + return Math.log(a) / Math.log(b); + } else { + throw new ParsingException("Unknown function call: " + function); + } + } + + // Evaluation the whole expression + private double processExpression() throws ParsingException { + double result = processTerm(); + + while (true) { + if (tryCaptureChar('+')) { + result += processTerm(); + } else if (tryCaptureChar('-')) { + result -= processTerm(); + } else if (tryCaptureChar(',')) { + double pendingArgument = processExpression(); + result = performBinaryFunction(functions.pop(), result, pendingArgument); + } else { + break; + } + } + return result; + } + + // Evaluation subterm of the expression + private double processTerm() throws ParsingException { + double result = processFactor(); + + while (true) { + if (tryCaptureChar('*')) { + result *= processFactor(); + } else if (tryCaptureChar('/')) { + result /= processFactor(); + } else { + break; + } + } + + return result; + } + + // Check if function is binary + private boolean isBinaryFunction(String func) { + return func.equals("pow") || func.equals("log") || func.equals("min") || func.equals("max"); + } + + // Evaluation factors of the expression + private double processFactor() throws ParsingException { + if (tryCaptureChar('-')) { + return -processFactor(); + } + + double result = 0; + int startPos = this.pos; + + if (tryCaptureChar('(')) { + result = processExpression(); + tryCaptureChar(')'); + } else if (Character.isDigit(ch) || ch == '.') { + + while (Character.isDigit(ch) || ch == '.') { + getNextChar(); + } + + // Number of points in a string + // Without a hack :( + int countPoints = 0; + for (int i = startPos; i < this.pos; ++i) { + countPoints += expression.charAt(i) == '.' ? 1 : 0; + } + + if (countPoints > 1) { + throw new ParsingException("Number with many points found."); + } + + result = Double.parseDouble(expression.substring(startPos, this.pos)); + } else if (Character.isAlphabetic(ch)) { + while (Character.isAlphabetic(ch)) { + getNextChar(); + } + + String func = expression.substring(startPos, this.pos); + + if (isBinaryFunction(func)) { + functions.push(func); + } + + if (func.equals("rnd")) { + result = Math.random(); + getNextChar(); + getNextChar(); + } else { + result = processFactor(); + if (func.equals("sqrt")) { + result = Math.sqrt(result); + } else if (func.equals("sin")) { + result = Math.sin(Math.toRadians(result)); + } else if (func.equals("cos")) { + result = Math.cos(Math.toRadians(result)); + } else if (func.equals("tg")) { + result = Math.tan(Math.toRadians(result)); + } else if (func.equals("abs")) { + result = Math.abs(result); + } else if (func.equals("sign")) { + result = Math.signum(result); + } else if (func.equals("log2")) { + result = Math.log(result) / Math.log(2); + } else if (!isBinaryFunction(func)) { + throw new ParsingException("Unknown function call: " + func); + } + } + } else { + throw new ParsingException("Unexpected appearance of: " + ch); + } + + return result; + } + +} diff --git a/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/README.md b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/README.md new file mode 100644 index 000000000..eaab7410c --- /dev/null +++ b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/README.md @@ -0,0 +1,29 @@ +# Java REST Calculator (in progress) + +# Usage + +First run application, then follow commands below: + +To authorize run in shell: + +`curl http://localhost:9001/signup?args="mountain-viewer,1234567" -X POST -H "Content-Type: text/plain"` + +To put variable/function run in shell: + +`curl http://localhost:9001/variable/x?value="19" -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)"` + +`curl http://localhost:9001/function/sum?arity="2" -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)" --data "x+y"` + + +To get value of a variable/body of function run in shell: + +`curl http://localhost:9001/variable/x -X GET -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)"` + +`curl http://localhost:9001/function/sum -X GET -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)"` + + +To evaluate expressions run in shell: + +`curl http://localhost:9001/eval -X POST -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)" --data "pow(sin(3 + pow(2, 4)), tg(32)) + 44*3+2"` + +`curl http://localhost:9001/eval -X POST -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "mountain-viewer:1234567" | base64)" --data "sum(sum(3, 4), 4)"` diff --git a/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/SecurityServiceConfiguration.java b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/SecurityServiceConfiguration.java new file mode 100644 index 000000000..49a476f27 --- /dev/null +++ b/homework-g597-spirin/src/main/java/ru/mipt/java2016/homework/g597/spirin/task4/SecurityServiceConfiguration.java @@ -0,0 +1,58 @@ +package ru.mipt.java2016.homework.g597.spirin.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); + } + }); + } +} +