-
Notifications
You must be signed in to change notification settings - Fork 2
Origo Tests #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ben-w-martin
wants to merge
83
commits into
online-photo-submission:master
Choose a base branch
from
ben-w-martin:origo-test
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Origo Tests #24
Changes from all commits
Commits
Show all changes
83 commits
Select commit
Hold shift + click to select a range
ac5fded
init integration project
ben-w-martin 911417d
add: createCallbackSubscription method to OrigoClient
ben-w-martin 97a2c50
committing to access on different device
ben-w-martin c5e7a92
add: base class for third party http responses, updated touchnet and …
ben-w-martin 29d4f78
add: HttpActionResult wrapper allows for use of try/catch in sending …
ben-w-martin 9aa52cb
refactor: checkexistingcallbacksubs and createcallbacksubscription ut…
ben-w-martin 3f0e812
add: createFilter method
ben-w-martin c8d1036
refactor: naming conv. in httpactionresult, OrigoService will now aut…
ben-w-martin 844c73a
add: uploadUserPhoto method. refactor: HttpActionResult result proper…
ben-w-martin 1106df6
add: approvePhoto method
ben-w-martin e17860f
add: listEvents method to OrigoClient. Start developing origoSerivice.
ben-w-martin 5adb36b
commit for switching working device
ben-w-martin 2dd0a53
refactor: listEvents method accepts params for query string
ben-w-martin 9aa8173
refactor: deciding where to place loggers, client or service. add: so…
ben-w-martin e2dceb2
add: OrigoEventLoggingService class configures local directory for st…
ben-w-martin 8ba43a3
building theoretical stucture to origoservice in lieu of API key
ben-w-martin 433f8be
add: writeEventToJson, event id:timestamp values will be stored local…
ben-w-martin 5786eb9
add: json logs with timestamps / ids created for new test events
ben-w-martin 9800e6d
add: scrubbed backup properties file
ben-w-martin f724f9a
add: OrigoEventLoggingService creates max of last 2 request logs, del…
ben-w-martin 4e51661
refactor: event logs use timestamp of log generation, in the future w…
ben-w-martin d61e88f
refactor: Only one log is maintained at a time, idempotent to old eve…
ben-w-martin 018bd2b
info: finishing for the day. Eventlogging now maintains one log file.…
ben-w-martin a753758
add: IOrigoStorageService interface. refactor: change implementing me…
ben-w-martin bd2199d
refactor: disuse HttpActionResult in favor of refactored origoRespons…
ben-w-martin 0ec34fb
add: utils with some easy time manipulating methods. add: Origoservic…
ben-w-martin 99f1466
refactor: api keys only go in app.properties
ben-w-martin e9b25ae
cleanup: removing unused classes. Refactor: reduce duplicate code in …
ben-w-martin f7866bc
add: cloudCardClient to host CC api methods. add: HttpClient to host …
ben-w-martin b325ca2
add: createfilter works properly
ben-w-martin 35c8d3c
add: cloudcardservice, provisioningservice. successfully provisioning…
ben-w-martin e9806f5
add: PeopleService interface, OrigoPeopleService class.
ben-w-martin 0973e4b
bug: needs simplification. There are too many layers, all too strongl…
ben-w-martin f06ebba
add: OrigoStorageService. focusing on getting photodownloader to work…
ben-w-martin 2d91082
bug: origo rejecting photo for unsupported type.
ben-w-martin 87dddec
fix: downloader stores photos to origo. Needs continued debugging and…
ben-w-martin 8267adc
feature: OrigoStorageService successfully downloads photos to Mobile …
ben-w-martin eb3748c
refactor: cleanup code, rm comments
ben-w-martin b23381d
fix: unfinished comment
ben-w-martin 13fd8d0
Merge branch 'master' into origo-storage
ben-w-martin edd645d
sanitized application.properties, rm'd from gitignore
ben-w-martin 719b894
fix: update app.properties to master + new origo features
ben-w-martin c9ad64d
fix: update gitignore to match master
ben-w-martin 07a589f
refactor: cleaning up redundant code
ben-w-martin ca572db
update: README shows config steps for Origo storage service
ben-w-martin 68c8bb1
Update README.md
ben-w-martin 0b890c5
rm unintended changes, unused files
ben-w-martin efb2f31
Delete src/main/groovy/com/cloudcard/photoDownloader/Utils.groovy
ben-w-martin 9f89400
Update TouchNetClient.groovy
ben-w-martin 1e7b946
Delete src/main/groovy/com/cloudcard/photoDownloader/PhotoStatusMessa…
ben-w-martin 34f431b
Update TouchNetClient.groovy
ben-w-martin afdab19
Merge branch 'origo-storage' of github.com:ben-w-martin/cloudcard-pho…
ben-w-martin ffb71c6
Fixed: unintended changes to touchnetClient, build.gradle
ben-w-martin 476570b
remove unused library
ben-w-martin 0a8a11d
add: httpClientSpec, initial unit tests for origo storage.
ben-w-martin bcf8d3c
add: test to ensure failure with invalid URL
ben-w-martin 5756ac7
add: tests for handleResponseLogging. Re-ran application with Origo A…
ben-w-martin 9940e18
add OrigoClientSpec with assoc. refactors
ben-w-martin a6e845b
refactor OrigoClientSpec
ben-w-martin cb24745
BUG: after successful upload/approve, downloader service errors. Phot…
ben-w-martin 9b66254
Fixed: was using findResult instead of findResults >:(
ben-w-martin 4657b36
transfer devices
ben-w-martin 8188859
add: more unit tests for origoStorageService. refactor the same.
ben-w-martin 9559dc9
refactor origoStorageService.resolveFileType
ben-w-martin a7cd0ee
all tests pass. application origo storage service works as expected
ben-w-martin f3c66dd
fix: noticed potential bug with isAuthenticated property
ben-w-martin 6a7db68
add given: block to each test with setup logic. Fixed possible infini…
ben-w-martin 6fc0c03
add: initialization test to origoStorageServiceSpec
ben-w-martin 1fcc500
some final refactoring. Tests are green. API requests work properly.
ben-w-martin 28949fd
final tests, refactors, cleanup for PR.
ben-w-martin fdb8107
one more test, final, final cleanup
ben-w-martin 25fffa2
rm comment
ben-w-martin bfbad57
fix: OrigoClient was missing conditionalOnProperty annotation
ben-w-martin 3b8f3d3
feature: override current photo. Refactor: origoclient / origostorage…
ben-w-martin 57c42f8
refactor origoClient, tests. All passing
ben-w-martin 24136ef
24/25 tests passing for OrigoStorageServiceSpec
ben-w-martin 022f255
replace HttpClient with SimpleResponseLogger, update tests
ben-w-martin 5cf8cc7
Tests are green. Tested successfully with test-api's. Includes some r…
ben-w-martin c645508
Fix log in origoClient init method
ben-w-martin 2ae79aa
fix Touchnet client spaces. Delete unused file
ben-w-martin a38dda9
add tests for responseWrapper
ben-w-martin 8f49a1f
update application.properties to production api endpoints
ben-w-martin 0be511a
fix bug in init method
ben-w-martin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,4 +34,4 @@ application-test.properties* | |
downloaded-photos | ||
summary | ||
|
||
lib/ | ||
lib/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
src/main/groovy/com/cloudcard/photoDownloader/AuthException.groovy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.cloudcard.photoDownloader | ||
|
||
class AuthException extends Exception { | ||
|
||
public static final String MESSAGE = "Error while attempting to authenticate" | ||
|
||
AuthException() { | ||
|
||
super(MESSAGE); | ||
} | ||
|
||
AuthException(String message) { | ||
|
||
super("$message $MESSAGE") | ||
} | ||
} |
286 changes: 286 additions & 0 deletions
286
src/main/groovy/com/cloudcard/photoDownloader/OrigoClient.groovy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,286 @@ | ||
package com.cloudcard.photoDownloader | ||
|
||
import io.jsonwebtoken.Jwts | ||
import jakarta.annotation.PostConstruct | ||
import org.slf4j.Logger | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.beans.factory.annotation.Value | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty | ||
import org.springframework.stereotype.Component | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
|
||
import java.security.Key | ||
import java.security.KeyFactory | ||
import java.security.spec.PKCS8EncodedKeySpec | ||
|
||
import static com.cloudcard.photoDownloader.ApplicationPropertiesValidator.throwIfBlank | ||
|
||
@Component | ||
@ConditionalOnProperty(value = "downloader.storageService", havingValue = "OrigoStorageService") | ||
class OrigoClient { | ||
// Makes requests to Origo API | ||
|
||
private static final Logger log = LoggerFactory.getLogger(OrigoClient.class) | ||
|
||
@Autowired | ||
UnirestWrapper unirestWrapper | ||
|
||
@Autowired | ||
SimpleResponseLogger simpleResponseLogger | ||
|
||
@Value('${Origo.eventManagementApi}') | ||
String eventManagementApi | ||
|
||
@Value('${Origo.mobileIdentitiesApi}') | ||
String mobileIdentitiesApi | ||
|
||
@Value('${Origo.certIdpApi}') | ||
String certIdpApi | ||
|
||
@Value('${Origo.organizationId}') | ||
String organizationId | ||
|
||
@Value('${Origo.contentType}') | ||
String contentType | ||
|
||
@Value('${Origo.applicationVersion}') | ||
String applicationVersion | ||
|
||
@Value('${Origo.applicationId}') | ||
String applicationId | ||
|
||
@Value('${Origo.clientId}') | ||
String clientId | ||
|
||
@Value('${Origo.clientSecret}') | ||
String clientSecret | ||
|
||
@Value('${Origo.usePkiAuth}') | ||
boolean usePkiAuth | ||
|
||
@Value('${Origo.tokenUrl}') | ||
String tokenUrl | ||
|
||
@Value('${Origo.privateKey}') | ||
String privateKey | ||
|
||
String accessToken | ||
|
||
boolean isAuthenticated = false | ||
|
||
Map<String, String> requestHeaders | ||
|
||
@PostConstruct | ||
init() { | ||
throwIfBlank(eventManagementApi, "The Origo Event Management API URL must be specified.") | ||
throwIfBlank(mobileIdentitiesApi, "The Origo Mobile Identities API URL must be specified.") | ||
throwIfBlank(organizationId, "The Origo organization ID must be specified.") | ||
throwIfBlank(clientSecret, "The Origo client secret must be specified.") | ||
throwIfBlank(clientId, "The Origo client ID must be specified.") | ||
throwIfBlank(contentType, "The Origo content-type header must be specified.") | ||
throwIfBlank(applicationVersion, "The Origo application version must be specified.") | ||
throwIfBlank(applicationId, "Your organization's Origo Application ID must be specified.") | ||
throwIfBlank(usePkiAuth.toString(), "Authentication Preference must be specified.") | ||
if (usePkiAuth) { | ||
throwIfBlank((tokenUrl).toString(), "Token URL must be specified.") | ||
} | ||
|
||
log.info('=================== Initializing Origo Client ===================') | ||
log.info(" Origo Event Management API URL : $eventManagementApi") | ||
log.info(" Origo Mobile Identities API URL : $mobileIdentitiesApi") | ||
log.info(" Origo organization ID : $organizationId") | ||
log.info(" Origo content type header : $contentType") | ||
log.info(" Origo application version : $applicationVersion") | ||
log.info(" Origo application ID : $applicationId") | ||
log.info(" Authentication Preference : ${usePkiAuth ? "PKI" : "Password"}") | ||
|
||
simpleResponseLogger.source = this.class.simpleName | ||
|
||
} | ||
|
||
ben-w-martin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
void authenticate(ResponseWrapper responseWithToken) { | ||
// Stores token for future requests | ||
|
||
String token = "" | ||
|
||
if (responseWithToken.success) { | ||
token = responseWithToken.body.access_token | ||
accessToken = token | ||
requestHeaders = [ | ||
'Authorization' : "Bearer $token" as String, | ||
'Content-Type' : contentType, | ||
'Application-Version': applicationVersion, | ||
'Application-ID' : applicationId | ||
] | ||
isAuthenticated = true | ||
} else { | ||
log.error("Error while authenticating with Origo.") | ||
isAuthenticated = false | ||
} | ||
|
||
} | ||
|
||
ResponseWrapper makeAuthenticatedRequest(Closure request) { | ||
|
||
ResponseWrapper response | ||
|
||
if (!isAuthenticated && authenticate(requestAccessToken()) || isAuthenticated) { | ||
response = request() | ||
} else { | ||
response = new ResponseWrapper(new AuthException()) | ||
} | ||
|
||
response | ||
} | ||
|
||
String getJswt(String keyStr) { | ||
// Input is an UNENCRYPTED RSA / PEM-encoded PKCS#8 private key. | ||
|
||
if (!keyStr) { | ||
throw new IllegalArgumentException("Key string not provided") | ||
} | ||
|
||
keyStr = keyStr.replaceAll("-----BEGIN PRIVATE KEY-----", "").replaceAll("-----END PRIVATE KEY-----", "").replaceAll("\\s", "") | ||
.replaceAll("\\n", "").trim() | ||
|
||
byte[] keyBytes = Base64.getDecoder().decode(keyStr) | ||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes) | ||
KeyFactory keyFactory = KeyFactory.getInstance("RSA") | ||
Key key = keyFactory.generatePrivate(keySpec) | ||
|
||
long currentTimeMils = System.currentTimeMillis() - 5 | ||
Date currentTime = new Date(currentTimeMils) | ||
Date expiration = new Date(currentTimeMils + 3600000) | ||
|
||
Jwts.builder() | ||
.subject(clientId) | ||
.issuer(clientId) | ||
.audience().add(tokenUrl).and() | ||
.notBefore(currentTime) | ||
.issuedAt(currentTime) | ||
.expiration(expiration) | ||
.id(UUID.randomUUID().toString()) | ||
.signWith(key) | ||
.compact() | ||
} | ||
|
||
ResponseWrapper requestAccessToken() { | ||
if (usePkiAuth) requestAccessTokenPki() | ||
else requestAccessTokenPassword() | ||
} | ||
|
||
ResponseWrapper requestAccessTokenPki() { | ||
// https://doc.origo.hidglobal.com/api/authentication/ | ||
|
||
String pkiCredentials = getJswt(privateKey) | ||
|
||
String url = "$certIdpApi/authentication/customer/$organizationId/token" | ||
Map<String, String> headers = ["Content-Type": "application/x-www-form-urlencoded"] | ||
String body = "grant_type=client_credentials&client_assertion=$pkiCredentials" | ||
|
||
ResponseWrapper response | ||
try { | ||
response = new ResponseWrapper(unirestWrapper.post(url, headers, body)) | ||
} catch (Exception e) { | ||
response = new ResponseWrapper(e) | ||
} | ||
simpleResponseLogger.log("requestAccessTokenPki", response, "Error while authenticating with Origo.") | ||
|
||
return response | ||
|
||
} | ||
|
||
ResponseWrapper requestAccessTokenPassword() { | ||
// https://doc.origo.hidglobal.com/api/authentication/ | ||
|
||
String url = "$certIdpApi/authentication/customer/$organizationId/token" | ||
Map<String, String> headers = ["Content-Type": "application/x-www-form-urlencoded"] | ||
String body = "client_id=${clientId}&client_secret=${clientSecret}&grant_type=client_credentials" | ||
|
||
ResponseWrapper response | ||
try { | ||
response = new ResponseWrapper(unirestWrapper.post(url, headers, body)) | ||
} catch (Exception e) { | ||
response = new ResponseWrapper(e) | ||
} | ||
simpleResponseLogger.log("requestAccessTokenPassword", response, "Error while authenticating with Origo.") | ||
|
||
return response | ||
|
||
} | ||
|
||
ResponseWrapper uploadUserPhoto(Photo photo, String fileType) { | ||
// https://doc.origo.hidglobal.com/api/mobile-identities/#/Photo%20ID/post-customer-organization_id-users-user_id-photo | ||
ResponseWrapper response | ||
|
||
String url = "$mobileIdentitiesApi/customer/$organizationId/users/${photo.person.identifier}/photo" | ||
Map<String, String> headers = requestHeaders.clone() as Map | ||
headers["Content-Type"] = "application/vnd.assaabloy.ma.credential-management-2.2+$fileType" as String | ||
|
||
try { | ||
response = new ResponseWrapper(unirestWrapper.post(url, headers, photo.bytes)) | ||
} catch (Exception e) { | ||
response = new ResponseWrapper(e) | ||
} | ||
|
||
simpleResponseLogger.log("uploadUserPhoto", response) | ||
|
||
return response | ||
} | ||
|
||
ResponseWrapper accountPhotoApprove(String userId, String id) { | ||
// https://doc.origo.hidglobal.com/api/mobile-identities/#/Photo%20ID/put-customer-organization_id-users-user_id-photo-photo_id-status | ||
|
||
ResponseWrapper response | ||
|
||
String url = "$mobileIdentitiesApi/customer/$organizationId/users/$userId/photo/$id/status" | ||
String serializedBody = new ObjectMapper().writeValueAsString([status: "APPROVE"]) | ||
|
||
try { | ||
response = new ResponseWrapper(unirestWrapper.put(url, requestHeaders, serializedBody)) | ||
} catch (Exception e) { | ||
response = new ResponseWrapper(e) | ||
} | ||
|
||
simpleResponseLogger.log("accountPhotoApprove", response) | ||
|
||
return response | ||
} | ||
|
||
ResponseWrapper getUserDetails(String userId) { | ||
// https://doc.origo.hidglobal.com/api/mobile-identities/#/Users/get-customer-organization_id-users-user_id | ||
|
||
ResponseWrapper response | ||
|
||
String url = "$mobileIdentitiesApi/customer/$organizationId/users/$userId" | ||
|
||
try { | ||
response = new ResponseWrapper(unirestWrapper.get(url, requestHeaders)) | ||
} catch (Exception e) { | ||
response = new ResponseWrapper(e) | ||
} | ||
|
||
simpleResponseLogger.log("getUserDetails", response) | ||
|
||
return response | ||
} | ||
|
||
ResponseWrapper deletePhoto(String userId, String photoId) { | ||
// https://doc.origo.hidglobal.com/api/mobile-identities/#/Photo%20ID/delete-customer-organization_id-users-user_id-photo-photo_id | ||
|
||
ResponseWrapper response | ||
|
||
String url = "$mobileIdentitiesApi/customer/$organizationId/users/$userId/photo/$photoId" | ||
|
||
try { | ||
response = new ResponseWrapper(unirestWrapper.delete(url, requestHeaders)) | ||
} catch (Exception e) { | ||
response = new ResponseWrapper(e) | ||
} | ||
|
||
simpleResponseLogger.log("deletePhoto", response) | ||
|
||
return response | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a situation where this isn't always the same value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not unless there was a change in API Version. Would you like these properties to stay filled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the moment, I've included the properties that don't change in the existing application.properties file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made a little mistake. While the content-type shouldn't change except for a couple requests (Already accounted for in OrigoClient methods), All of the API links change from test API to the Production API. I'll add the production API links to the Application.properties.