Repository navigation
Authenticate a Cloud Request Engine on a license key alone, and send the properties named - #97
Merged
Merged
Conversation
Cover the rows of the specification's table of credentials, the data request for a license key alone, a resource key alone, both keys, a resource key with requested properties and all three, the accessible properties request for each, the report of requested properties the answer left out, and the license key staying out of every URL, log entry and error message when a request fails.
A license key without a resource key is accepted when the properties wanted are named. The properties set with setProperty or setProperties, which reached newEngine and were never used, now go to the cloud service as 'values' whenever the list is not empty, beside a resource key, a license key or both, so the service adds them to its answer. The builder refuses at build time only the two combinations the cloud service refuses on every request, being no credential at all and a license key with no requested properties, each with a message that says why the combination cannot work. When a license key is set, the accessible properties request is a POST that carries the license key, and the resource key if there is one, in its body, so discovery finds what the license key grants and the key never appears in a URL. A resource key alone is sent in the query string as before. The engine warns once about requested properties the answer left out. A blank resource key counts as absent, so the data endpoint for a license key alone is the plain json endpoint.
The previous commit sent the properties set with setProperty and setProperties as 'values'. On rc that list reached newEngine and was never sent, so a configuration that set it, as the .NET cloud web examples do, would change the cloud service's answer. The .NET full build showed that answer losing device.deviceid. The engine builder now keeps its own list, set with setRequestedProperties as a list or as one comma separated string, and only that list is sent. setProperty and setProperties behave exactly as they did on rc, and the refusal for a license key with no requested properties names setRequestedProperties.
This was referenced Nov 10, 2026
…cense-key-only # Conflicts: # pipeline/pipeline.cloudrequestengine/src/main/java/fiftyone/pipeline/cloudrequestengine/flowelements/CloudRequestEngineBuilder.java # pipeline/pipeline.cloudrequestengine/src/main/java/fiftyone/pipeline/cloudrequestengine/flowelements/CloudRequestEngineDefault.java
… texts As in .NET, the names sent as values keep the letter case they were given in, and a name given twice in different cases is sent once as first given. With a license key, names the answer leaves out are reported as not covered by the keys, and with a resource key, requested properties and no license key, as not properties, as cloud-request-engine.md says. A null list is refused with the parameter's name.
…perty The cloud service reads property names without regard to letter case, so the names sent as values keep the case they were given in, and a repeat in another case is dropped, as .NET now does. A null list of requested properties is refused with an IllegalArgumentException naming the parameter, rather than a NullPointerException from inside the list. With a resource key, requested properties and no license key, a property the resource key does not carry is answered as null with its reason, so a name the answer leaves out is not a property. The warning for that case says so and names no license key, as cloud-request-engine.md asks.
…e key The warning for a resource key with requested properties and no license key names no license key, because none was given.
Discovery on rc fetches the evidence keys on another thread while it fetches the accessible properties, so the fake cloud records calls from two threads. A plain ArrayList could lose a call or hold a null, which failed the resource key discovery and data request tests depending on the order the tests ran in. rc's own discovery tests use a synchronized list for the same reason.
This was referenced Nov 11, 2026
rc's Cloud Request Engine tests build engines with a license key, and a license key makes the discovery requests POSTs, which the shared mock answered with the data response. The mock now answers a POST to the accessible properties or evidence keys endpoint as it answers a GET, and the three tests that count the data request count only that request.
The fake cloud looked each request's URL up in a HashMap that both discovery threads wrote to, so a lost entry left a call with no URL and failed Process_ResourceKeyAndLicenseKey_SendsBoth in one run.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
For #12.
The Cloud Request Engine specification says a request authenticates with a Resource Key, a License Key or both, and that a list of Requested Properties is sent as
values. The table of accepted combinations, as specifications#131 left it onrc, is:The discovery section says:
James Rosewell decided on 10 November that only what the cloud service refuses today is refused at build. A Resource Key with a property list builds, with or without a License Key, and the list is sent.
On
rcthis repository could not do any of that.CloudRequestEngineBuilder.javarefused to build without a resource key, so a license key alone could not build.propertiesthatsetPropertyandsetPropertiescollect, and never used them. The engine could not be told which properties to ask for.CloudRequestEngineDefault.javabuilt theaccessiblepropertiesURL from the resource key alone, so the products a license key adds were never discovered.Change
CloudRequestEngineBuilderrefuses only the two invalid rows, being no credential at all and a license key with no requested properties. Each message says why the combination cannot work, in the wording of the .NET draft, 51degrees-dotnet#129, and of the three build messages in the Pythonrcconstants.py, less the one for a resource key with properties, which is no longer refused. A key that is empty or only white space counts as absent, and keys are trimmed, because one read from an environment variable often carries a trailing newline.setRequestedPropertiesnames them, as a list or as one comma separated string, which is the form a configuration file gives. Names are trimmed and sent as given, a name given again in another letter case is sent once as first given, and the list is sent asvalueswhenever the list is not empty, beside a resource key, a license key or both.setPropertyandsetPropertiesare unchanged and their list is still not sent, for the reason under Effect on existing callers.resourceonly when a resource key is set, thenlicensewhen a license key is set, thenvalues. The data endpoint keeps its route. With no resource key it is the plainjsonendpoint, as it already was onrcwhen no resource key was set.accessiblepropertiesrequest is a POST carryinglicense, andresourceif there is one, in its body, so the key never appears in a URL. This follows the Pythonrcengine (cloudrequestengine.py). A resource key alone is still sent with GET in the query string.evidencekeysis unchanged.CloudRequestEngineDefaultcompares the requested names with the answer, ignoring letter case, and warns once about any left out. A property present but null counts as answered. The warning never fails the request.Constants.javagainsNoCredential,LicenseKeyNeedsProperties,PropertiesNotCoveredandRequestedNamesNotProperties.Review fixes
The same two findings as on 51degrees-dotnet#129, made the same way so the two languages match, against cloud-request-engine.md at e89679e9, and two more.
Names are sent as given (commit fd7242a).
normalizedlower-cased every name it sent asvalues, because "the cloud service answers in lower case". That is about the answer, which the comparison already reads without regard to case ("The comparison MUST be case insensitive, because the remote server lowercases Property names in its response"), and every measurement in the specification sends mixed case names. The names are now trimmed and sent as given, and a repeat that differs only in letter case is dropped, keeping the first spelling.A name that is not a property gets its own warning (commits fd7242a and e99d627). The section says "With a Resource Key, Requested Properties and no License Key, a Property no licence grants is answered as null with its
nullreason, and so counts as answered. A name the answer leaves out is then one that is not a Property, and the report says so rather than naming a License Key that was never given." With no license key the engine now logsRequestedNamesNotProperties, with the same text as .NET, and with a license keyPropertiesNotCoveredas before.A null list is refused (commit fd7242a). Both
setRequestedPropertiesoverloads throwIllegalArgumentExceptionnamingproperties, as .NET throwsArgumentNullException, where beforeaddAllthrew aNullPointerExceptionfrom inside the list.The json route under a license key alone, measured. A runner posted to the plain route with the 51Did test license key and no resource key, on 11 November 2026 (run 43020665840).
POST https://cloud.51degrees.com/api/v4/accessiblepropertieswithlicenseanswered HTTP 200, listing the productFODidwith 6 properties.POST https://cloud.51degrees.com/api/v4/jsonwithlicense,values=FODid.IdProbGlobalandUser-Agentanswered HTTP 200, with the body{}. The same request with novaluesanswered HTTP 400, "This request authenticated with a license key but did not specify which properties to return." So the route accepts a license key alone and refuses one with no list, which is the second refused row of the Accepted combinations table.The new engine constructor has its caller. The
CloudRequestEngineDefaultconstructor that takes the requested properties is used byCloudRequestEngineBuilder.newEngine, the only place in this repository that builds the engine, in this change. The overloads that take no list delegate to it with an empty list, so code that builds the engine directly keeps working and sends novalues.The merge of
rc(commit 3722d66).rcat ddb7d04 holds the eager discovery change, #86, and #99, and both conflicted with this draft inCloudRequestEngineBuilder.javaandCloudRequestEngineDefault.java. The engine is built with this draft's trimmed keys and requested properties and then givenrc's discovery,rc'ssetDiscoveryStateandgetDiscoveryStateare kept, and theaccessiblepropertiesrequest keeps this draft's POST under a license key, withrc's timeouts on both routes.A test fix the merge needed (commit f45eced). Discovery on
rcfetches the evidence keys on another thread while it fetches the accessible properties, so the tests' fake cloud records calls from two threads. Its plainArrayListcould lose a call or hold a null, which failed the resource key discovery and data request tests in one run (run 43023140398), depending on the order the tests ran in. It now uses a synchronized list, asrc's ownCloudRequestEngineDiscoveryTestsdoes.Two more test fixes from the merge of
rcat 528e0df (commits a9ecacc, 72faa2d and 4656994).rc'sCloudRequestEngineTestsbuild engines with a license key, and a license key makes the discovery requests POSTs, which the shared mock inCloudRequestEngineTestsBaseanswered with the data response, so three of those tests failed in the first full build of the merge (run 43072007057). The mock now answers a POST to the accessible properties or evidence keys endpoint as it answers a GET, and the three tests that count the data request count only that request (72faa2d). Separately, the license key tests' fake cloud looked each request's URL up in aHashMapthat both discovery threads wrote to, which failedProcess_ResourceKeyAndLicenseKey_SendsBothin one run (run 43078881682), so it is now aConcurrentHashMap(4656994).Effect on existing callers
setPropertyorsetPropertiesare still not sent, so a caller who set them keeps the answer they had. No Java example sets them on the Cloud Request Engine, but two .NET examples do, as short names such asDeviceId. When an earlier head of the .NET draft sent that list asvalues, its full build (run 42787764085) failed 118 of the 156 cloud web example tests, because the answer then held nodevice.deviceid. An earlier head of this draft reusedsetPropertyandsetPropertiesin the same way. That is why the list has its own option here too.rcand sent as is. It now counts as absent, so the build is refused unless a license key is also set. A key with white space around it is now trimmed.-and_is sent exactly as before.Keys in logs
The license key travels only in request bodies, never in a URL, so no URL this engine logs or puts in an error message can hold it.
Process_FailedRequest_LicenseKeyInNoUrlLogOrMessagechecks that a good request and a failed one leave the key out of every URL, every log call and every error. Masking a key that the cloud service repeats in its own answer is #85's work, which adds the sharedLicenseKeyMask, and this change does not touch those lines. An earlier version of this branch carried its own mask invalidateResponse, and it is gone, because it clashed with #85 and showed half of a key of 8 characters or fewer, where logging.md asks for the last 4.Tests, red and green
The new
CloudRequestEngineLicenseKeyTests.javacovers every row of the table, the data request for each combination, discovery for a license key alone, a resource key with properties and both keys, the report of properties left out, and the key staying out of URLs, logs and errors.Process_SetProperties_NotSentchecks thatsetPropertyandsetPropertiesstill send nothing, andProcess_RequestedPropertiesAsText_Sentcovers the comma separated form. It uses a recordingHttpClientand needs no live key. The first targeted red and green runs build and testpipeline.cloudrequestengineon Java 8 and Java 17, and the later ones on Java 8 and Java 21.rc(commit a6376df): run 42794288211. On Java 8 and on Java 17, 14 of the 23 new tests fail, 7 as failures and 7 as errors.setRequestedPropertiesdoes not exist onrc, so the red copy callssetPropertyandsetPropertiesin its place, whichrccollects and never sends.Build_LicenseKeyAndProperties_Accepted,Process_LicenseKeyAlone_SendsLicenseAndValues,Process_RequestedPropertiesAsText_Sent,Discovery_LicenseKeyAlone_PostsCredentialsInTheBody,Process_FailedRequest_LicenseKeyInNoUrlLogOrMessage,Process_PropertyDropped_ReportedOnceAndRequestSucceedsandProcess_PropertyPresentButNull_CountsAsAnsweredare refused with "A resource key is required".Build_NoCredential_Refused,Build_LicenseKeyWithoutProperties_RefusedandProcess_SetProperties_NotSentget that message instead of one that explains the 401 or the 400.Build_BlankValuesCountAsAbsentbuilds with a resource key of two spaces.Discovery_BothKeys_PostsBothInTheBodyfinds a GET.Process_ResourceKeyAndProperties_SendsResourceAndValuesandProcess_AllThree_SendsResourceLicenseAndValuesfind novalues.pipeline.cloudrequestenginetests pass on Java 8 and on Java 17.rc(commit 46de8b5): run 43022436285. On Java 21, 8 of 26CloudRequestEngineLicenseKeyTestsfail, being the five that send names (the name given twice among them), the property dropped report, the new not-a-property test and the null list. On Java 8 the same 8 fail, andDiscovery_ResourceKeyAndProperties_GetsWithTheKeyInTheQueryalso errors with aNullPointerExceptionfrom the fake cloud's unsafe list, which f45eced fixed, so that error is not from this change.CloudRequestEngineTestsand all 26CloudRequestEngineLicenseKeyTestspass.Full pull request build
Run 43084404100, attempt 1, on the head 4656994, which merges
rcat 528e0df. Eight of the 9 jobs without macOS passed, being Ubuntu Java 8, 11, 17 and 21, Ubuntu ARM Java 17, and Windows Java 8, 11 and 21. Windows Java 17 ran every test and passed them, both Selenium contract runs 9 of 9 included, and then failed when it uploaded its results, with "Failed to CreateArtifact: Failed to make request after 5 attempts: Unexpected token '<', "<!DOCTYPE "... is not valid JSON", during a GitHub Actions outage from about 20:30 UTC. EverySetting 'PullRequestSha'line, in Configure and in the 9 jobs, is 4656994, and Configure printed "Left out 6 macOS entries". The release build with macOS on Java's finalrchead runs every test again.macOS was left out of this draft's full build under the 9 November decision. The rc head is built with macOS before it merges into main.
Other drafts
#89 (reserved Evidence names), #84 (missing product), #85 (license key masking) and #86 (eager discovery) change
CloudRequestEngineDefault.javaandConstants.java, and all four merged intorcon 10 November. #86 renamed the method that makes theaccessiblepropertiesrequest and set a timeout beside the line this change replaces, and the merge ofrcin 3722d66, under Review fixes, carried the POST under a license key across. The head mergesrcat 528e0df, which holds all four.What remains
DeviceDetectionCloudPipelineBuilder, pass a license key on but have no option for the properties wanted. The .NET draft adds one to its pipeline builder base. Java's pipeline builders have no such option yet.getFormDatainCloudRequestEngineDefault.javaat other lines, and it merged intorcat 20:26 UTC on 11 November. The head is 4 commits behindrc(Move cxx to the 51degrees-cxx release 4.5.105 and read by index on an engine built with no names #82, Make the data update follow the specification #79, Give every environment variable one _51DEGREES_ name #105 and Send one Evidence name once, whatever its letter case, by precedence #107) and GitHub reports it MERGEABLE and CLEAN, so it merges as it is. The build ofrc's head with macOS beforercgoes to main proves the two together.