Rewrite lesson to be self-contained and not depend on the core of WebGoat for fetching users
Split the assignment into 2 assignments
This commit is contained in:
parent
9e6ed11aa7
commit
3ad51e6d6b
@ -34,15 +34,14 @@ public class WebGoatUser implements UserDetails {
|
||||
}
|
||||
|
||||
public WebGoatUser(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
createUser();
|
||||
this(username, password, ROLE_USER);
|
||||
}
|
||||
|
||||
public WebGoatUser(String username, String password, String role) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.role = role;
|
||||
createUser();
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,54 +1,87 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import lombok.Data;
|
||||
|
||||
public class AccessControlTest extends IntegrationTest {
|
||||
|
||||
@Test
|
||||
|
||||
@Test
|
||||
public void testLesson() {
|
||||
startLesson("MissingFunctionAC");
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.clear();
|
||||
params.put("hiddenMenu1", "Users");
|
||||
params.put("hiddenMenu2", "Config");
|
||||
|
||||
|
||||
checkAssignment(url("/WebGoat/access-control/hidden-menu"), params, true);
|
||||
String userHash =
|
||||
startLesson("MissingFunctionAC");
|
||||
assignment1();
|
||||
assignment2();
|
||||
assignment3();
|
||||
|
||||
checkResults("/access-control");
|
||||
}
|
||||
|
||||
private void assignment3() {
|
||||
//direct call should fail if user has not been created
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.contentType(ContentType.JSON)
|
||||
.get(url("/WebGoat/access-control/users-admin-fix"))
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_FORBIDDEN);
|
||||
|
||||
//create user
|
||||
var userTemplate = """
|
||||
{"username":"%s","password":"%s","admin": "true"}
|
||||
""";
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.contentType(ContentType.JSON)
|
||||
.body(String.format(userTemplate, getWebgoatUser(), getWebgoatUser()))
|
||||
.post(url("/WebGoat/access-control/users"))
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
|
||||
//get the users
|
||||
var userHash =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.contentType(ContentType.JSON)
|
||||
.get(url("/WebGoat/users"))
|
||||
.contentType(ContentType.JSON)
|
||||
.get(url("/WebGoat/access-control/users-admin-fix"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath()
|
||||
.get("find { it.username == \"" + getWebgoatUser() + "\" }.userHash");
|
||||
|
||||
params.clear();
|
||||
params.put("userHash", userHash);
|
||||
checkAssignment(url("/WebGoat/access-control/user-hash"), params, true);
|
||||
|
||||
|
||||
checkResults("/access-control");
|
||||
.get("find { it.username == \"Jerry\" }.userHash");
|
||||
|
||||
checkAssignment(url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true);
|
||||
}
|
||||
|
||||
@Data
|
||||
public class Item {
|
||||
private String username;
|
||||
private boolean admin;
|
||||
private String userHash;
|
||||
|
||||
private void assignment2() {
|
||||
var userHash =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.contentType(ContentType.JSON)
|
||||
.get(url("/WebGoat/access-control/users"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath()
|
||||
.get("find { it.username == \"Jerry\" }.userHash");
|
||||
|
||||
checkAssignment(url("/WebGoat/access-control/user-hash"), Map.of("userHash", userHash), true);
|
||||
}
|
||||
|
||||
private void assignment1() {
|
||||
var params = Map.of("hiddenMenu1", "Users", "hiddenMenu2", "Config");
|
||||
checkAssignment(url("/WebGoat/access-control/hidden-menu"), params, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.owasp.webgoat.missing_ac;
|
||||
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Base64;
|
||||
|
||||
@ -31,55 +31,32 @@ import java.util.Base64;
|
||||
* projects.
|
||||
* <p>
|
||||
*/
|
||||
|
||||
@Getter
|
||||
public class DisplayUser {
|
||||
//intended to provide a display version of WebGoatUser for admins to view user attributes
|
||||
|
||||
|
||||
private String username;
|
||||
private boolean admin;
|
||||
private String userHash;
|
||||
|
||||
public DisplayUser(WebGoatUser user) {
|
||||
public DisplayUser(User user, String passwordSalt) {
|
||||
this.username = user.getUsername();
|
||||
this.admin = false;
|
||||
this.admin = user.isAdmin();
|
||||
|
||||
for (GrantedAuthority authority : user.getAuthorities()) {
|
||||
this.admin = (authority.getAuthority().contains("WEBGOAT_ADMIN")) ? true : false;
|
||||
}
|
||||
|
||||
// create userHash on the fly
|
||||
//TODO: persist userHash
|
||||
try {
|
||||
this.userHash = genUserHash(user.getUsername(), user.getPassword());
|
||||
this.userHash = genUserHash(user.getUsername(), user.getPassword(), passwordSalt);
|
||||
} catch (Exception ex) {
|
||||
//TODO: implement better fallback operation
|
||||
this.userHash = "Error generating user hash";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected String genUserHash(String username, String password) throws Exception {
|
||||
protected String genUserHash(String username, String password, String passwordSalt) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
// salting is good, but static & too predictable ... short too for a salt
|
||||
String salted = password + "DeliberatelyInsecure1234" + username;
|
||||
String salted = password + passwordSalt + username;
|
||||
//md.update(salted.getBytes("UTF-8")); // Change this to "UTF-16" if needed
|
||||
byte[] hash = md.digest(salted.getBytes("UTF-8"));
|
||||
String encoded = Base64.getEncoder().encodeToString(hash);
|
||||
return encoded;
|
||||
}
|
||||
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public boolean isAdmin() {
|
||||
return admin;
|
||||
}
|
||||
|
||||
public String getUserHash() {
|
||||
return userHash;
|
||||
byte[] hash = md.digest(salted.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.getEncoder().encodeToString(hash);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
package org.owasp.webgoat.missing_ac;
|
||||
|
||||
import org.owasp.webgoat.LessonDataSource;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class MissingAccessControlUserRepository {
|
||||
|
||||
private final NamedParameterJdbcTemplate jdbcTemplate;
|
||||
private final RowMapper<User> mapper = (rs, rowNum) -> new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("admin"));
|
||||
|
||||
public MissingAccessControlUserRepository(LessonDataSource lessonDataSource) {
|
||||
this.jdbcTemplate = new NamedParameterJdbcTemplate(lessonDataSource);
|
||||
}
|
||||
|
||||
public List<User> findAllUsers() {
|
||||
return jdbcTemplate.query("select username, password, admin from access_control_users", mapper);
|
||||
}
|
||||
|
||||
public User findByUsername(String username) {
|
||||
var users = jdbcTemplate.query("select username, password, admin from access_control_users where username=:username",
|
||||
new MapSqlParameterSource().addValue("username", username),
|
||||
mapper);
|
||||
if (CollectionUtils.isEmpty(users)) {
|
||||
return null;
|
||||
}
|
||||
return users.get(0);
|
||||
}
|
||||
|
||||
public User save(User user) {
|
||||
jdbcTemplate.update("INSERT INTO access_control_users(username, password, admin) VALUES(:username,:password,:admin)",
|
||||
new MapSqlParameterSource()
|
||||
.addValue("username", user.getUsername())
|
||||
.addValue("password", user.getPassword())
|
||||
.addValue("admin", user.isAdmin()));
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
@ -27,7 +27,10 @@ import org.owasp.webgoat.lessons.Lesson;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class MissingFunctionAC extends Lesson {
|
||||
public class MissingFunctionAC extends Lesson {
|
||||
|
||||
public static final String PASSWORD_SALT_SIMPLE = "DeliberatelyInsecure1234";
|
||||
public static final String PASSWORD_SALT_ADMIN = "DeliberatelyInsecure1235";
|
||||
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
|
@ -25,8 +25,6 @@ package org.owasp.webgoat.missing_ac;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.session.UserSessionData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@ -37,10 +35,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
@AssignmentHints({"access-control.hidden-menus.hint1","access-control.hidden-menus.hint2","access-control.hidden-menus.hint3"})
|
||||
public class MissingFunctionACHiddenMenus extends AssignmentEndpoint {
|
||||
//UserSessionData is bound to session and can be used to persist data across multiple assignments
|
||||
@Autowired
|
||||
UserSessionData userSessionData;
|
||||
|
||||
|
||||
@PostMapping(path = "/access-control/hidden-menu", produces = {"application/json"})
|
||||
@ResponseBody
|
||||
|
@ -22,79 +22,82 @@
|
||||
|
||||
package org.owasp.webgoat.missing_ac;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.users.UserService;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.owasp.webgoat.session.WebSession;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_ADMIN;
|
||||
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
|
||||
|
||||
/**
|
||||
* Created by jason on 1/5/17.
|
||||
*/
|
||||
|
||||
@Controller
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class MissingFunctionACUsers {
|
||||
|
||||
// this will actually put controllers on the /WebGoat/* path ... the jsp for list_users restricts what can be seen, but the add_user is not controlled carefully
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
private final MissingAccessControlUserRepository userRepository;
|
||||
private final WebSession webSession;
|
||||
|
||||
@RequestMapping(path = {"users"}, method = RequestMethod.GET)
|
||||
public ModelAndView listUsers(HttpServletRequest request) {
|
||||
@GetMapping(path = {"access-control/users"})
|
||||
public ModelAndView listUsers() {
|
||||
|
||||
ModelAndView model = new ModelAndView();
|
||||
model.setViewName("list_users");
|
||||
List<WebGoatUser> allUsers = userService.getAllUsers();
|
||||
model.addObject("numUsers",allUsers.size());
|
||||
List<User> allUsers = userRepository.findAllUsers();
|
||||
model.addObject("numUsers", allUsers.size());
|
||||
//add display user objects in place of direct users
|
||||
List<DisplayUser> displayUsers = new ArrayList<>();
|
||||
for (WebGoatUser user : allUsers) {
|
||||
displayUsers.add(new DisplayUser(user));
|
||||
for (User user : allUsers) {
|
||||
displayUsers.add(new DisplayUser(user, PASSWORD_SALT_SIMPLE));
|
||||
}
|
||||
model.addObject("allUsers",displayUsers);
|
||||
model.addObject("allUsers", displayUsers);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
@RequestMapping(path = {"users", "/"}, method = RequestMethod.GET,consumes = "application/json")
|
||||
@GetMapping(path = {"access-control/users"}, consumes = "application/json")
|
||||
@ResponseBody
|
||||
public List<DisplayUser> usersService(HttpServletRequest request) {
|
||||
|
||||
List<WebGoatUser> allUsers = userService.getAllUsers();
|
||||
List<DisplayUser> displayUsers = new ArrayList<>();
|
||||
for (WebGoatUser user : allUsers) {
|
||||
displayUsers.add(new DisplayUser(user));
|
||||
}
|
||||
return displayUsers;
|
||||
public ResponseEntity<List<DisplayUser>> usersService() {
|
||||
return ResponseEntity.ok(userRepository.findAllUsers().stream().map(user -> new DisplayUser(user, PASSWORD_SALT_SIMPLE)).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@RequestMapping(path = {"users","/"}, method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
|
||||
@GetMapping(path = {"access-control/users-admin-fix"}, consumes = "application/json")
|
||||
@ResponseBody
|
||||
//@PreAuthorize()
|
||||
public WebGoatUser addUser(@RequestBody WebGoatUser newUser) {
|
||||
public ResponseEntity<List<DisplayUser>> usersFixed() {
|
||||
var currentUser = userRepository.findByUsername(webSession.getUserName());
|
||||
if (currentUser != null && currentUser.isAdmin()) {
|
||||
return ResponseEntity.ok(userRepository.findAllUsers().stream().map(user -> new DisplayUser(user, PASSWORD_SALT_ADMIN)).collect(Collectors.toList()));
|
||||
}
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
|
||||
@PostMapping(path = {"access-control/users", "access-control/users-admin-fix"}, consumes = "application/json", produces = "application/json")
|
||||
@ResponseBody
|
||||
public User addUser(@RequestBody User newUser) {
|
||||
try {
|
||||
userService.addUser(newUser.getUsername(),newUser.getPassword());
|
||||
return userService.loadUserByUsername(newUser.getUsername());
|
||||
userRepository.save(newUser);
|
||||
return newUser;
|
||||
} catch (Exception ex) {
|
||||
log.error("Error creating new User", ex);
|
||||
//TODO: implement error handling ...
|
||||
} finally {
|
||||
// no streams or other resources opened ... nothing to do, right?
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
|
||||
//@RequestMapping(path = {"user/{username}","/"}, method = RequestMethod.DELETE, consumes = "application/json", produces = "application/json")
|
||||
//TODO implement delete method with id param and authorization
|
||||
|
||||
}
|
||||
|
||||
//@RequestMapping(path = {"user/{username}","/"}, method = RequestMethod.DELETE, consumes = "application/json", produces = "application/json")
|
||||
//TODO implement delete method with id param and authorization
|
||||
|
||||
}
|
||||
|
@ -22,35 +22,33 @@
|
||||
|
||||
package org.owasp.webgoat.missing_ac;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.owasp.webgoat.users.UserService;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
|
||||
|
||||
@RestController
|
||||
@AssignmentHints({"access-control.hash.hint1","access-control.hash.hint2","access-control.hash.hint3",
|
||||
"access-control.hash.hint4","access-control.hash.hint5","access-control.hash.hint6","access-control.hash.hint7",
|
||||
"access-control.hash.hint8","access-control.hash.hint9","access-control.hash.hint10","access-control.hash.hint11","access-control.hash.hint12"})
|
||||
@AssignmentHints({"access-control.hash.hint1", "access-control.hash.hint2", "access-control.hash.hint3", "access-control.hash.hint4", "access-control.hash.hint5"})
|
||||
@RequiredArgsConstructor
|
||||
public class MissingFunctionACYourHash extends AssignmentEndpoint {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
private final MissingAccessControlUserRepository userRepository;
|
||||
|
||||
|
||||
@PostMapping(path = "/access-control/user-hash", produces = {"application/json"})
|
||||
@ResponseBody
|
||||
public AttackResult completed(String userHash) {
|
||||
String currentUser = getWebSession().getUserName();
|
||||
WebGoatUser user = userService.loadUserByUsername(currentUser);
|
||||
DisplayUser displayUser = new DisplayUser(user);
|
||||
public AttackResult simple(String userHash) {
|
||||
User user = userRepository.findByUsername("Jerry");
|
||||
DisplayUser displayUser = new DisplayUser(user, PASSWORD_SALT_SIMPLE);
|
||||
if (userHash.equals(displayUser.getUserHash())) {
|
||||
return success(this).feedback("access-control.hash.success").build();
|
||||
} else {
|
||||
return failed(this).feedback("access-control.hash.close").build();
|
||||
return failed(this).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
|
||||
*
|
||||
* Copyright (c) 2002 - 2019 Bruce Mayhew
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program; if
|
||||
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Getting Source ==============
|
||||
*
|
||||
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
|
||||
*/
|
||||
|
||||
package org.owasp.webgoat.missing_ac;
|
||||
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_ADMIN;
|
||||
|
||||
@RestController
|
||||
@AssignmentHints({"access-control.hash.hint6", "access-control.hash.hint7",
|
||||
"access-control.hash.hint8", "access-control.hash.hint9", "access-control.hash.hint10", "access-control.hash.hint11", "access-control.hash.hint12"})
|
||||
public class MissingFunctionACYourHashAdmin extends AssignmentEndpoint {
|
||||
|
||||
private final MissingAccessControlUserRepository userRepository;
|
||||
|
||||
public MissingFunctionACYourHashAdmin(MissingAccessControlUserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@PostMapping(path = "/access-control/user-hash-fix", produces = {"application/json"})
|
||||
@ResponseBody
|
||||
public AttackResult admin(String userHash) {
|
||||
//current user should be in the DB
|
||||
//if not admin then return 403
|
||||
|
||||
var user = userRepository.findByUsername("Jerry");
|
||||
var displayUser = new DisplayUser(user, PASSWORD_SALT_ADMIN);
|
||||
if (userHash.equals(displayUser.getUserHash())) {
|
||||
return success(this).feedback("access-control.hash.success").build();
|
||||
} else {
|
||||
return failed(this).feedback("access-control.hash.close").build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package org.owasp.webgoat.missing_ac;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class User {
|
||||
|
||||
private String username;
|
||||
private String password;
|
||||
private boolean admin;
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
/*
|
||||
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
|
||||
*
|
||||
* Copyright (c) 2002 - 2019 Bruce Mayhew
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program; if
|
||||
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Getting Source ==============
|
||||
*
|
||||
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
|
||||
*/
|
||||
|
||||
package org.owasp.webgoat.missing_ac;
|
||||
|
||||
import org.owasp.webgoat.LessonDataSource;
|
||||
import org.owasp.webgoat.session.UserSessionData;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Users {
|
||||
|
||||
private UserSessionData userSessionData;
|
||||
private LessonDataSource dataSource;
|
||||
|
||||
public Users(UserSessionData userSessionData, LessonDataSource dataSource) {
|
||||
this.userSessionData = userSessionData;
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@GetMapping(produces = {"application/json"})
|
||||
@ResponseBody
|
||||
protected HashMap<Integer, HashMap> getUsers() {
|
||||
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
String query = "SELECT * FROM user_data";
|
||||
|
||||
try {
|
||||
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
|
||||
ResultSet.CONCUR_READ_ONLY);
|
||||
ResultSet results = statement.executeQuery(query);
|
||||
HashMap<Integer, HashMap> allUsersMap = new HashMap();
|
||||
|
||||
if ((results != null) && (results.first() == true)) {
|
||||
while (results.next()) {
|
||||
HashMap<String, String> userMap = new HashMap<>();
|
||||
userMap.put("first", results.getString(1));
|
||||
userMap.put("last", results.getString(2));
|
||||
userMap.put("cc", results.getString(3));
|
||||
userMap.put("ccType", results.getString(4));
|
||||
userMap.put("cookie", results.getString(5));
|
||||
userMap.put("loginCount", Integer.toString(results.getInt(6)));
|
||||
allUsersMap.put(results.getInt(0), userMap);
|
||||
}
|
||||
userSessionData.setValue("allUsers", allUsersMap);
|
||||
return allUsersMap;
|
||||
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
HashMap<String, String> errMap = new HashMap() {{
|
||||
put("err", sqle.getErrorCode() + "::" + sqle.getMessage());
|
||||
}};
|
||||
|
||||
return new HashMap<Integer, HashMap>() {{
|
||||
put(0, errMap);
|
||||
}};
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
HashMap<String, String> errMap = new HashMap() {{
|
||||
put("err", e.getMessage() + "::" + e.getCause());
|
||||
}};
|
||||
e.printStackTrace();
|
||||
return new HashMap<Integer, HashMap>() {{
|
||||
put(0, errMap);
|
||||
}};
|
||||
|
||||
|
||||
} finally {
|
||||
try {
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
HashMap<String, String> errMap = new HashMap() {{
|
||||
put("err", e.getMessage() + "::" + e.getCause());
|
||||
}};
|
||||
e.printStackTrace();
|
||||
return new HashMap<>() {{
|
||||
put(0, errMap);
|
||||
}};
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
.hidden-menu-item {
|
||||
display:none;
|
||||
visibility:hidden;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
CREATE TABLE access_control_users(
|
||||
username varchar(40),
|
||||
password varchar(40),
|
||||
admin boolean
|
||||
);
|
||||
|
||||
INSERT INTO access_control_users VALUES ('Tom', 'qwertyqwerty1234', false);
|
||||
INSERT INTO access_control_users VALUES ('Jerry', 'doesnotreallymatter', true);
|
||||
INSERT INTO access_control_users VALUES ('Sylvester', 'testtesttest', false);
|
@ -5,6 +5,7 @@
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/ac.css}"/>
|
||||
<div class="adoc-content" th:replace="doc:missing-function-ac-02-client-controls.adoc"></div>
|
||||
|
||||
<div class="attack-container">
|
||||
@ -17,7 +18,6 @@
|
||||
|
||||
<div class="collapse navbar-collapse" id="alignment-example">
|
||||
|
||||
<!-- Links -->
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Account<span class="caret"></span></a>
|
||||
@ -37,8 +37,9 @@
|
||||
<li class="hidden-menu-item dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Admin<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" aria-labelledby="admin">
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/config">Config</a></li>
|
||||
<li><a href="/access-control/users">Users</a></li>
|
||||
<li><a href="/access-control/users-admin-fix">Users</a></li>
|
||||
<li><a href="/access-control/config">Config</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@ -89,4 +90,26 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
|
||||
<div class="adoc-content" th:replace="doc:missing-function-ac-04-users-fixed.adoc"></div>
|
||||
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST" name="form"
|
||||
action="/WebGoat/access-control/user-hash-fix">
|
||||
|
||||
<p>Your Hash: <input name="userHash" value="" type="TEXT"/></p>
|
||||
<br/>
|
||||
<input name="submit" value="Submit" type="SUBMIT"/>
|
||||
|
||||
</form>
|
||||
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</html>
|
||||
|
@ -11,15 +11,15 @@ access-control.hidden-menus.hint3=Look for something a super-user or administato
|
||||
access-control.hash.success=Congrats! You really succeeded when you added the user.
|
||||
access-control.hash.close=Keep trying, this one may take several attempts & steps to achieve. See the hints for help.
|
||||
|
||||
access-control.hash.hint1=There is an easier way and a 'harder' way to achieve this, the easier way involves one simple change in a GET request.
|
||||
access-control.hash.hint2= If you haven't found the hidden menus from the earlier exercise, go do that first.
|
||||
access-control.hash.hint3=When you look at the users page, there is a hint that more info is viewable by a given role.
|
||||
access-control.hash.hint4=For the easy way, have you tried tampering the GET request? Different content-types?
|
||||
access-control.hash.hint5=For the 'easy' way, modify the GET request to /users to include 'Content-Type: application/json'
|
||||
access-control.hash.hint1=This assignment involves one simple change in a GET request.
|
||||
access-control.hash.hint2=If you haven't found the hidden menus from the earlier exercise, go do that first.
|
||||
access-control.hash.hint3=When you look at the users page, there is a hint that more info is viewable by a given role.
|
||||
access-control.hash.hint4=Have you tried tampering the GET request? Different content-types?
|
||||
access-control.hash.hint5=Modify the GET request to `/access-control/users` to include 'Content-Type: application/json'
|
||||
access-control.hash.hint6=Now for the harder way ... it builds on the easier way
|
||||
access-control.hash.hint7=If the request to view users, were a 'service' or 'RESTful' endpoint, what would be different about it?
|
||||
access-control.hash.hint8=If you're still looking for hints ... try changing the Content-type header as in the GET request.
|
||||
access-control.hash.hint9=You also need to deliver a proper payload for the request (look at how registration works). This should be formatted in line with the content-type you just defined.
|
||||
access-control.hash.hint10=You will want to add WEBGOAT_ADMIN for the user's role. Yes, you'd have to guess/fuzz this in a real-world setting.
|
||||
access-control.hash.hint11=OK, here it is. First, create an admin user ... Change the method to POST, change the content-type to "application/json". And your payload should look something like: {"username":"newUser2","password":"newUser12","matchingPassword":"newUser12","role":"WEBGOAT_ADMIN"}
|
||||
access-control.hash.hint10=You will want to add your own username with an admin role. Yes, you'd have to guess/fuzz this in a real-world setting.
|
||||
access-control.hash.hint11=OK, here it is. First, create an admin user ... Change the method to POST, change the content-type to "application/json". And your payload should look something like: {"username":"newUser2","password":"newUser12","admin": "true"}
|
||||
access-control.hash.hint12=Now log in as that user and bring up WebGoat/users. Copy your hash and log back in to your original account and input it there to get credit.
|
||||
|
@ -12,4 +12,4 @@ It will likely take multiple steps and multiple attempts to get this one:
|
||||
- You'll need to do some guessing too.
|
||||
- You may need to use another browser/account along the way.
|
||||
|
||||
Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'Hash' for your user account.
|
||||
Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'hash' for Jerry's account.
|
@ -0,0 +1,5 @@
|
||||
== The company fixed the problem, right?
|
||||
|
||||
The company found out the endpoint was a bit too open, they made an emergency fixed and not only admin users can list all users.
|
||||
|
||||
Start with the information you already gathered (hidden menu items) to see if you can pull the list of users and then provide the 'hash' for Jerry's account.
|
@ -22,23 +22,22 @@
|
||||
|
||||
package org.owasp.webgoat.missing_ac;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class DisplayUserTest {
|
||||
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
|
||||
|
||||
class DisplayUserTest {
|
||||
|
||||
@Test
|
||||
public void TestDisplayUserCreation() {
|
||||
DisplayUser displayUser = new DisplayUser(new WebGoatUser("user1","password1"));
|
||||
assert(!displayUser.isAdmin());
|
||||
void testDisplayUserCreation() {
|
||||
DisplayUser displayUser = new DisplayUser(new User("user1", "password1", true), PASSWORD_SALT_SIMPLE);
|
||||
Assertions.assertThat(displayUser.isAdmin()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TesDisplayUserHash() {
|
||||
DisplayUser displayUser = new DisplayUser(new WebGoatUser("user1","password1"));
|
||||
assert(displayUser.getUserHash().equals("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc="));
|
||||
void testDisplayUserHash() {
|
||||
DisplayUser displayUser = new DisplayUser(new User("user1", "password1", false), PASSWORD_SALT_SIMPLE);
|
||||
Assertions.assertThat(displayUser.getUserHash()).isEqualTo("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc=");
|
||||
}
|
||||
}
|
||||
|
@ -28,53 +28,53 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.owasp.webgoat.users.UserService;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.owasp.webgoat.plugins.LessonTest;
|
||||
import org.owasp.webgoat.session.WebSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class MissingFunctionACUsersTest {
|
||||
private MockMvc mockMvc;
|
||||
@Mock
|
||||
private UserService userService;
|
||||
class MissingFunctionACUsersTest extends LessonTest {
|
||||
|
||||
@Autowired
|
||||
private MissingFunctionAC ac;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
MissingFunctionACUsers usersController = new MissingFunctionACUsers();
|
||||
this.mockMvc = standaloneSetup(usersController).build();
|
||||
ReflectionTestUtils.setField(usersController,"userService",userService);
|
||||
when(userService.getAllUsers()).thenReturn(getUsersList());
|
||||
void setup() {
|
||||
when(webSession.getCurrentLesson()).thenReturn(ac);
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestContentTypeApplicationJSON () throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/users")
|
||||
.header("Content-type","application/json"))
|
||||
void getUsers() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/access-control/users")
|
||||
.header("Content-type", "application/json"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$[0].username", CoreMatchers.is("user1")))
|
||||
.andExpect(jsonPath("$[0].userHash",CoreMatchers.is("cplTjehjI/e5ajqTxWaXhU5NW9UotJfXj+gcbPvfWWc=")))
|
||||
.andExpect(jsonPath("$[1].admin",CoreMatchers.is(true)));
|
||||
|
||||
.andExpect(jsonPath("$[0].username", CoreMatchers.is("Tom")))
|
||||
.andExpect(jsonPath("$[0].userHash", CoreMatchers.is("Mydnhcy00j2b0m6SjmPz6PUxF9WIeO7tzm665GiZWCo=")))
|
||||
.andExpect(jsonPath("$[0].admin", CoreMatchers.is(false)));
|
||||
}
|
||||
|
||||
private List<WebGoatUser> getUsersList() {
|
||||
List <WebGoatUser> tempUsers = new ArrayList<>();
|
||||
tempUsers.add(new WebGoatUser("user1","password1"));
|
||||
tempUsers.add(new WebGoatUser("user2","password2","WEBGOAT_ADMIN"));
|
||||
tempUsers.add(new WebGoatUser("user3","password3", "WEBGOAT_USER"));
|
||||
return tempUsers;
|
||||
@Test
|
||||
void addUser() throws Exception {
|
||||
var user = """
|
||||
{"username":"newUser","password":"newUser12","admin": "true"}
|
||||
""";
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/users")
|
||||
.header("Content-type", "application/json").content(user))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/access-control/users")
|
||||
.header("Content-type", "application/json"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.size()", is(4)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package org.owasp.webgoat.missing_ac;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.owasp.webgoat.plugins.LessonTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_ADMIN;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
class MissingFunctionACYourHashAdminTest extends LessonTest {
|
||||
|
||||
@Autowired
|
||||
private MissingFunctionAC ac;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
when(webSession.getCurrentLesson()).thenReturn(ac);
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void solve() throws Exception {
|
||||
var userHash = new DisplayUser(new User("Jerry", "doesnotreallymatter", true), PASSWORD_SALT_ADMIN).getUserHash();
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash-fix")
|
||||
.param("userHash", userHash))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Congrats! You really succeeded when you added the user.")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void wrongUserHash() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash-fix")
|
||||
.param("userHash", "wrong"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||
}
|
||||
}
|
@ -25,56 +25,40 @@ package org.owasp.webgoat.missing_ac;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpointTest;
|
||||
import org.owasp.webgoat.users.UserService;
|
||||
import org.owasp.webgoat.plugins.LessonTest;
|
||||
import org.owasp.webgoat.users.WebGoatUser;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.owasp.webgoat.missing_ac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class MissingFunctionYourHashTest extends AssignmentEndpointTest {
|
||||
private MockMvc mockMvc;
|
||||
private DisplayUser mockDisplayUser;
|
||||
|
||||
@Mock
|
||||
protected UserService userService;
|
||||
class MissingFunctionYourHashTest extends LessonTest {
|
||||
@Autowired
|
||||
private MissingFunctionAC ac;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
MissingFunctionACYourHash yourHashTest = new MissingFunctionACYourHash();
|
||||
init(yourHashTest);
|
||||
this.mockMvc = standaloneSetup(yourHashTest).build();
|
||||
this.mockDisplayUser = new DisplayUser(new WebGoatUser("user", "userPass"));
|
||||
ReflectionTestUtils.setField(yourHashTest, "userService", userService);
|
||||
when(userService.loadUserByUsername(any())).thenReturn(new WebGoatUser("user", "userPass"));
|
||||
public void setup() {
|
||||
when(webSession.getCurrentLesson()).thenReturn(ac);
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void HashDoesNotMatch() throws Exception {
|
||||
void hashDoesNotMatch() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
||||
.param("userHash", "42"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashMatches() throws Exception {
|
||||
void hashMatches() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/access-control/user-hash")
|
||||
.param("userHash", "2340928sadfajsdalsNfwrBla="))
|
||||
.param("userHash", "SVtOlaa+ER+w2eoIIVE5/77umvhcsh5V8UyDLUa1Itg="))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.containsString("Keep trying, this one may take several attempts")))
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false)));
|
||||
.andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true)));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user