Skip to content

Commit

Permalink
Merge pull request #83 from AO-StreetArt/expandUserSupport
Browse files Browse the repository at this point in the history
Expand user support
  • Loading branch information
AO-StreetArt committed Dec 20, 2018
2 parents 5450068 + 9d21de9 commit ecf5c38
Show file tree
Hide file tree
Showing 12 changed files with 613 additions and 106 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ install:
- sleep 480
script:
- cd $TRAVIS_BUILD_DIR/src/test/json && newman run Adrestia.postman_collection.json -e AdrestiaTest.postman_environment.json
- cd $TRAVIS_BUILD_DIR/src/test/json && newman run AdrestiaUserApi.postman_collection.json -e AdrestiaTest.postman_environment.json
- docker stop $(docker ps -aq)
after_success:
- cd $TRAVIS_BUILD_DIR && scripts/linux/push_docker.sh
Expand Down
14 changes: 7 additions & 7 deletions src/main/java/com/ao/adrestia/AdrestiaApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
import com.ao.adrestia.filters.RoutingFilter;
import com.ao.adrestia.model.ApplicationUser;
import com.ao.adrestia.repo.ApplicationUserRepository;
import com.ao.adrestia.security.JWTAuthorizationFilter;
import com.ao.adrestia.security.JWTAuthenticationFilter;
import com.ao.adrestia.security.DbUserDetails;
import com.ao.adrestia.security.JwtAuthenticationFilter;
import com.ao.adrestia.security.JwtAuthorizationFilter;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
Expand All @@ -45,8 +45,8 @@
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
Expand Down Expand Up @@ -98,8 +98,8 @@ public BCryptPasswordEncoder bCryptPasswordEncoder() {
// Enable loading custom CSS and PNG Files
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/images/**").addResourceLocations("/images/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/images/**").addResourceLocations("/images/");
}

// Configure Web Security
Expand All @@ -116,8 +116,8 @@ protected void configure(HttpSecurity http) throws Exception {
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers("/**").authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager(), jwtSecret))
.addFilter(new JWTAuthorizationFilter(authenticationManager(), applicationUserRepository, jwtSecret))
.addFilter(new JwtAuthenticationFilter(authenticationManager(), jwtSecret))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), applicationUserRepository, jwtSecret))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
} else {
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/ao/adrestia/controller/UiController.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
* Controller for serving the Web Interface.
*/
@SuppressWarnings("unused")
@Controller
public class UiController {
Expand Down
125 changes: 117 additions & 8 deletions src/main/java/com/ao/adrestia/controller/UsersController.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import com.ao.adrestia.model.ApplicationUser;
import com.ao.adrestia.repo.ApplicationUserRepository;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -30,11 +32,20 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
* Rest Controller which provides CRUD operations for Users.
*/
@RestController
@RequestMapping("users")
public class UsersController {
Expand All @@ -46,24 +57,122 @@ public class UsersController {
@Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;

/**
* Sign-up a new user.
*/
@PostMapping("/sign-up")
public ResponseEntity<String> signUp(@RequestBody ApplicationUser user) {
public ResponseEntity<ApplicationUser> signUp(@RequestBody ApplicationUser user) {
// Set up a success response code
HttpStatus returnCode = HttpStatus.OK;
// Set up a response header to return a valid HTTP Response
HttpHeaders responseHeaders = new HttpHeaders();
String responseBody = "";
// Detect any existing users
List<ApplicationUser> existingUsers = applicationUserRepository.findByUsername(user.username);
if (existingUsers.size() > 0) {
log.warn("Sign-up requested for existing user");
returnCode = HttpStatus.CONFLICT;
} else {
log.info("Signing up new User: {}", user.username);
user.password = bCryptPasswordEncoder.encode(user.getPassword());
applicationUserRepository.save(user);
returnCode = HttpStatus.OK;
// Check for any users with the same email
List<ApplicationUser> sameEmailUsers = applicationUserRepository.findByEmail(user.email);
if (sameEmailUsers.size() > 0) {
log.warn("Sign-up requested, existing email registration found");
returnCode = HttpStatus.CONFLICT;
} else {
// Save the new user in the DB
log.info("Signing up new User: {}", user.username);
user.password = bCryptPasswordEncoder.encode(user.getPassword());
applicationUserRepository.save(user);
}
}
String responseBody = "";
HttpHeaders responseHeaders = new HttpHeaders();
user.password = "";
return new ResponseEntity<ApplicationUser>(user, responseHeaders, returnCode);
}

/**
* Update an existing user.
*/
@PutMapping("/{key}")
public ResponseEntity<ApplicationUser> updateUser(
@RequestBody ApplicationUser user,
@PathVariable("key") String key) {
user.setId(key);
user.password = bCryptPasswordEncoder.encode(user.getPassword());
applicationUserRepository.save(user);
// Set up a success response code
HttpStatus returnCode = HttpStatus.OK;
// Set up a response header to return a valid HTTP Response
HttpHeaders responseHeaders = new HttpHeaders();
user.password = "";
return new ResponseEntity<ApplicationUser>(user, responseHeaders, returnCode);
}

/**
* Get a user by ID.
*/
@GetMapping("/{key}")
public ResponseEntity<ApplicationUser> getUser(@PathVariable("key") String key) {
// Set up a success response code
HttpStatus returnCode = HttpStatus.OK;
ApplicationUser returnUser = new ApplicationUser();
// Find any existing users
Optional<ApplicationUser> existingUser = applicationUserRepository.findById(key);
if (existingUser.isPresent()) {
log.info("Retrieved user by ID");
returnUser = existingUser.get();
} else {
log.warn("Unable to find User: {}", key);
returnCode = HttpStatus.NOT_FOUND;
}
// Return the response
HttpHeaders responseHeaders = new HttpHeaders();
returnUser.password = "";
return new ResponseEntity<ApplicationUser>(returnUser, responseHeaders, returnCode);
}

/**
* Find users by username or email.
*/
@GetMapping("/")
public ResponseEntity<ApplicationUser> findUser(
@RequestParam(value = "username", defaultValue = "") String username,
@RequestParam(value = "email", defaultValue = "") String email) {
// Set up a success response code
HttpStatus returnCode = HttpStatus.OK;
// Set up a response header to return a valid HTTP Response
HttpHeaders responseHeaders = new HttpHeaders();
ApplicationUser returnUser = new ApplicationUser();
// Find any existing users
List<ApplicationUser> existingUsers;
if (!(username.isEmpty())) {
existingUsers = applicationUserRepository.findByUsername(username);
} else if (!(email.isEmpty())) {
existingUsers = applicationUserRepository.findByEmail(email);
} else {
existingUsers = new ArrayList<ApplicationUser>();
}
if (existingUsers.size() > 0) {
log.info("Retrieved user by Username");
returnUser = existingUsers.get(0);
} else {
log.warn("Unable to find User: {}:{}", username, email);
returnCode = HttpStatus.NOT_FOUND;
}
// Return the response
returnUser.password = "";
return new ResponseEntity<ApplicationUser>(returnUser, responseHeaders, returnCode);
}

/**
* Delete an existing user.
*/
@DeleteMapping("/{key}")
public ResponseEntity<String> deleteUser(@PathVariable("key") String key) {
// Set up a success response code
HttpStatus returnCode = HttpStatus.OK;
// Set up a response header to return a valid HTTP Response
HttpHeaders responseHeaders = new HttpHeaders();
String responseBody = "";
applicationUserRepository.deleteById(key);
return new ResponseEntity<String>(responseBody, responseHeaders, returnCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ public ServiceInstance findCrazyIvan() {
targetIndex = ivanIndex.getAndIncrement();
}

log.info("Returning Ivan instance {} of {} retrieved from Consul", targetIndex, instances.size());
log.info("Returning Ivan instance {} of {} retrieved from Consul",
targetIndex, instances.size());

// Return the targeted value
return instances.get(targetIndex);
Expand Down Expand Up @@ -198,10 +199,12 @@ public ServiceInstance findClyman(String sceneId) {

// Get the cluster name out of our cache response
SceneLocation targetLocation = cachedLocations.get(0);
log.info("Retrieved Scene Location out of cache at {}", targetLocation.clusterIdentifier);
log.info("Retrieved Scene Location out of cache at {}",
targetLocation.clusterIdentifier);

// Now that we have a cluster, find an Ivan instance
List<ServiceInstance> activeInstances = findClymanByCluster(targetLocation.clusterIdentifier);
List<ServiceInstance> activeInstances =
findClymanByCluster(targetLocation.clusterIdentifier);
if (activeInstances.size() > 0) {
if (clymanIndex.compareAndSet(activeInstances.size(), 0)) {
targetIndex = clymanIndex.getAndIncrement();
Expand All @@ -222,7 +225,8 @@ public ServiceInstance findClyman(String sceneId) {
SceneLocation targetLocation = dbLocations.get(targetIndex);

// Now that we have the cache, find the Crazy Ivan instance
List<ServiceInstance> activeInstances = findClymanByCluster(targetLocation.clusterIdentifier);
List<ServiceInstance> activeInstances =
findClymanByCluster(targetLocation.clusterIdentifier);
if (activeInstances.size() > 0) {
if (clymanIndex.compareAndSet(activeInstances.size(), 0)) {
targetIndex = clymanIndex.getAndIncrement();
Expand Down Expand Up @@ -276,6 +280,7 @@ public ServiceInstance findAvc() {
* Find an instance of the Project Service.
* @return A ServiceInstance object with the instance details found
*/
@Override
public ServiceInstance findProjectService() {
log.info("Finding Project Service Instance");

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/ao/adrestia/filters/PersistenceFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ public Object run() {
headers.setContentType(MediaType.APPLICATION_JSON);
if (httpAuthActive) {
String credentialsString = "";
credentialsString =
Base64.getEncoder().encodeToString((ivanUsername + ":" + ivanPassword).getBytes(StandardCharsets.ISO_8859_1));
credentialsString = Base64.getEncoder().encodeToString(
(ivanUsername + ":" + ivanPassword).getBytes(StandardCharsets.ISO_8859_1));
headers.add("Authorization", "Basic " + credentialsString);
}
HttpEntity<String> entity = new HttpEntity<String>("", headers);
Expand Down
39 changes: 28 additions & 11 deletions src/main/java/com/ao/adrestia/filters/RoutingFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

@Component
Expand Down Expand Up @@ -171,13 +174,18 @@ public Object run() {
if (urlPathList.length == 4) {
// We need to strip out the scene url elements if we hit
// the registration API's
if (urlPathList[3].equals("register") || urlPathList[3].equals("deregister") || urlPathList[3].equals("align")) {
if (urlPathList[3].equals("register")
|| urlPathList[3].equals("deregister")
|| urlPathList[3].equals("align")) {
newTail = "/" + stripSceneUrlElements(urlPathList, parsedUrlList);
}
}
}

} else if (urlPathList[1].equals("asset") || urlPathList[1].equals("history") || urlPathList[1].equals("relationship") || urlPathList[1].equals("collection")) {
} else if (urlPathList[1].equals("asset")
|| urlPathList[1].equals("history")
|| urlPathList[1].equals("relationship")
|| urlPathList[1].equals("collection")) {
// We have an asset request, so route to AVC
ServiceInstance targetInstance = discoveryClient.findAvc();
if (targetInstance != null) {
Expand Down Expand Up @@ -222,27 +230,36 @@ public Object run() {
log.error("Error setting service URL", e);
}

// If Authentication is enabled, then inject Basic Auth credentials
// If Authentication is enabled, then update auth headers
if (httpAuthActive) {
// Move the current principal to a different HTTP header
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User requestUser = (User)authentication.getPrincipal();
log.debug("Injecting Aesel header field with user {}", requestUser.getUsername());
context.addZuulRequestHeader("X-Aesel-Principal", requestUser.getUsername());

// Then, inject Basic Auth credentials for the downstream service
String credentialsString = "";
if (isAvcRequest) {
log.debug("Setting AVC Credentials with User {}", avcUsername);
credentialsString =
Base64.getEncoder().encodeToString((avcUsername + ":" + avcPassword).getBytes(StandardCharsets.ISO_8859_1));
credentialsString = Base64.getEncoder().encodeToString(
(avcUsername + ":" + avcPassword).getBytes(StandardCharsets.ISO_8859_1));
} else if (isClymanRequest) {
log.debug("Setting CLyman Credentials with User {}", clymanUsername);
credentialsString =
Base64.getEncoder().encodeToString((clymanUsername + ":" + clymanPassword).getBytes(StandardCharsets.ISO_8859_1));
credentialsString = Base64.getEncoder().encodeToString(
(clymanUsername + ":" + clymanPassword).getBytes(StandardCharsets.ISO_8859_1));
} else if (isIvanRequest) {
log.debug("Setting Ivan Credentials with User {}", ivanUsername);
credentialsString =
Base64.getEncoder().encodeToString((ivanUsername + ":" + ivanPassword).getBytes(StandardCharsets.ISO_8859_1));
credentialsString = Base64.getEncoder().encodeToString(
(ivanUsername + ":" + ivanPassword).getBytes(StandardCharsets.ISO_8859_1));
} else if (isProjectRequest) {
log.debug("Setting Projects Credentials with User {}", projectsUsername);
credentialsString =
Base64.getEncoder().encodeToString((projectsUsername + ":" + projectsPassword).getBytes(StandardCharsets.ISO_8859_1));
credentialsString = Base64.getEncoder().encodeToString(
(projectsUsername + ":" + projectsPassword).getBytes(StandardCharsets.ISO_8859_1));
}
context.addZuulRequestHeader("Authorization", "Basic " + credentialsString);


}
return null;
}
Expand Down

0 comments on commit ecf5c38

Please sign in to comment.