CPQ Quote API, I can't save the quote - salesforce

I can't save the quote.
Doing the query:
select
ApexClass.name, Id, CreatedDate, CreatedById, JobType,
ApexClassId, Status, JobItemsProcessed, TotalJobItems,
NumberOfErrors, CompletedDate, MethodName, ExtendedStatus,
ParentJobId, LastProcessed, LastProcessedOffset
from
AsyncApexJob
order by
CreatedDate desc
I get this error:
Calculation error on quote Q-13761: "UNAUTHORIZED"
Code:
public with sharing class QuoteCalculator {
public void calculate(QuoteModel quote, String callbackClass) {
system.debug('quote: ' +quote);
system.debug('callbackClass: ' +callbackClass);
QuoteCalculatorContext ctx = new QuoteCalculatorContext(quote, callbackClass);
SBQQ.ServiceRouter.load('SBQQ.QuoteAPI.QuoteCalculator', null, JSON.serialize(ctx));
system.debug('QuoteCalculator.calculate');
}
private class QuoteCalculatorContext {
private QuoteModel quote; //The quote and callbackClass properties are called
in the API code by the exact names seen here.
private String callbackClass; //Altering these property names will cause
calculator API calls to fail.
private QuoteCalculatorContext(QuoteModel quote, String callbackClass) {
this.quote = quote;
this.callbackClass = callbackClass;
}
}
}
anonymous window:
QuoteReader reader = new QuoteReader();
QuoteModel quote = reader.read('a0p1w000BhfXzAAJ');
System.debug(quote);
quote.lineItems[0].record.SBQQ__Quantity__c = 2;
QuoteCalculator calculator = new QuoteCalculator();
calculator.calculate(quote, 'MyCallback')

Preface
I had (almost) the same exact code base as yours, and got the same error message.
In my case there was an other sandbox I could test my code, and it turned out to be working properly there.
Cause
Later found out that the Salesforce CPQ's Calculation Quote API is using Heroku to do the calculations in order to avoid apex limits exhaustion.
From this it can be deducted, that it needs to have a Connected App. I checked the Apps -> Connected Apps setup, and found that no record was listed under the "Connected Apps OAuth Usage" page for the Salesforce CPQ. (On my other sandbox there was a "Steelbrick CPQ" row.)
From this I concluded that this might be the reason for this behaviour.
Seems like something went wrong during the "Authorize new Calculation Service" process. (Or there was a sandbox refresh and something else went wrong during it.)
Solution
The bad news is that the option to authorize a new calculation service is only visible for the first time you configure the package, which you might already done. (Well... if you haven't done, then this is a great news, because your problem is probably solved. :D) (Otherwise read further.)
The good news is I figured out a solution for the case when you already done this, yet that "Steelbrick CPQ" row is missing.
Created a scratch org and installed the Salesforce CPQ package, then before I clicked on the "Authorize new Calculation Service" link under the "Pricing and Calculation" tab in the Settings Editor, I checked the source code in hope of finding something of interest.
I did.
This link: https://rest-na.steelbrick.com/oauth/auth/https%3A%2F%2Ftest.salesforce.com/SBQQ
(⚠️NOTE: You might have to change it according to your location. There are several servers across the globe:
rest-au.steelbrick.com
rest-eu.steelbrick.com
rest-jp.steelbrick.com
rest-na.steelbrick.com
But for me the above pasted link was generated on the settings page. Which is only interesting, because I live in the EU, yet, for some reason I got the link to the rest-NA server... whatever.gif
So just make sure if you click on the link, in the address bar you can find the appropriate salesforce instance URL.)
Conclusion
With this link you won't have to reinstall the package, you just have to click on it, and allow the access from Steelbrick and the missing row will appear, and you will be authorized to use the Calculation API.

Related

Getting "Signature is invalid." when using Artifact Binding during the artifact consumption step

I have an IdP and an SP setup using the ITfoxtec SAML2 libraries, and everything works great when not using artifact binding, or when not validating signatures. When using artifact binding and validating signatures I'm getting a "Signature is invalid." exception in the ACS when trying to retrieve and bind the actual response/assertion.
It seems to unbind the artifact response fine, then when it goes to retrieve and unbind the artifact from the ArtifactResolutionService it fails, specifically on the last line of this block:
var soapEnvelope = new Saml2SoapEnvelope();
saml2AuthnResponse = new Saml2AuthnResponse(config);
await soapEnvelope.ResolveAsync(httpClient, saml2ArtifactResolve, saml2AuthnResponse);
I've checked that my signature validation certificate is correct and I've dug through the source code but am scratching my head. I've tried to validate the "saml2p:ArtifactResponse" myself but there isn't much out there.
If I put this line before the chunk above everything works as expected as it no longer validates the signature:
config.SignatureValidationCertificates.Clear();
One thing I noticed is that in the 'saml2p:ArtifactResponse' there is a signature inside of that node but not inside the contained 'saml2p:Response' node. Is it possible that the saml2p:Response is being isolated and then a signature check is being performed? I tried to see if it was supposed to be signing the response/assertion in the artifact cache on the IdP side (artifactSaml2AuthnResponseCache), but it doesn't sign response at all. I'm doing this before putting it in the cache just like in the example and just like I do when using POST binding:
var token = saml2AuthnResponse.CreateSecurityToken(relyingParty.Issuer, subjectConfirmationLifetime: 5, issuedTokenLifetime: 60);
artifactSaml2AuthnResponseCache[saml2ArtifactResolve.Artifact] = saml2AuthnResponse;`
EDIT: I have determined that the ArtifactResponse just isn't signed properly. Another tool claims the digest in the XML doesn't match the computed value. This is after stepping through the source and grabbing the XML that the code is trying to validate directly. I can see that the ArtifactResolve is being signed and validated properly (and I checked with the external tool) but the ArtifactResponse isn't. Even in the code it fails at the final validation of the signature (and not at any checks before it).
EDIT 2: Found the problem in the source. The .ToXmlDocument() extension is breaking the signed XML. The final test was done by 'replacing' it in the spot with a new method that just returns the string directly with "envelope.ToString(SaveOptions.DisableFormatting)":
protected virtual XmlDocument ToSoapXml()
{
var envelope = new XElement(Saml2Constants.SoapEnvironmentNamespaceX + Saml2Constants.Message.Envelope);
envelope.Add(GetXContent());
return envelope.ToXmlDocument();
}
protected string ToSoapXmlString()
{
var envelope = new XElement(Saml2Constants.SoapEnvironmentNamespaceX + Saml2Constants.Message.Envelope);
envelope.Add(GetXContent());
return envelope.ToString(SaveOptions.DisableFormatting);//.ToXmlDocument();
}
And directly save that to the SoapResponseXml of the Saml2SoapEnvelope:
protected override Saml2SoapEnvelope BindInternal(Saml2Request saml2Request, string messageName)
{
if (!(saml2Request is Saml2ArtifactResponse))
throw new ArgumentException("Only Saml2ArtifactResponse is supported");
BindInternal(saml2Request);
SoapResponseXml = ToSoapXmlString();// ToSoapXml().OuterXml;
return this;
}
I would initiate a pull request for this change but honestly I'm not that up to speed with Git. I'm also not sure if this is the best way to fix the issue.
Thank you for your question and code to solve the problem. I'll look into the problem.
EDIT: I'm trying to reproduce the error but no luck. The sample is both an IdP an RP, what have you changed to get the error?

How to get Hikvision DeepinViews license plate number from URL?

I cant find the solution anywhere and mine doesn't seem to work.
I just want to see the last plate string in the browser,or the few last plates,doesn't matter.
http://login:password#MY.IP/ISAPI/Traffic/channels/1/vehicleDetect/plates/
<AfterTime><picTime>2021-12-09T09:07:15Z</picTime></AfterTime>
I do have a plate taken exactly at the time im using in pictime,but the result im getting is;
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<ResponseStatus xmlns="
http://www.hikvision.com/ver20/XMLSchema
" version="2.0">
<requestURL>
/ISAPI/Traffic/channels/1/vehicleDetect/plates/
<AfterTime>
<picTime>2021-12-09T09:01:15Z</picTime>
</AfterTime>
</requestURL>
<statusCode>4</statusCode>
<statusString>Invalid Operation</statusString>
<subStatusCode>invalidOperation</subStatusCode>
</ResponseStatus>
POSTMAN
Edit:
Are you certain that the ISAPI setting is enabled in the camera configuration?
It's not possible in the browser without some tool to send and process your API request.
Have you tried using Postman?
Don't forget to use a Digest Auth header.
from requests.auth import HTTPDigestAuth
import requests
url = 'http://<Your IP>/ISAPI/Traffic/channels/1/vehicleDetect/plates/'
data = "<AfterTime><picTime>20220912T192011+0400</picTime></AfterTime>"
r=requests.get(url, data =data,auth=HTTPDigestAuth('admin', 'password'))
print(r.text)
Try this one after enabling this setting in camera
Screenshot

CakePhp 4.x basic Authentication

I am following the CakePHP 4.x tutorial to the letter (as far as I can see) until chapter "CMS Tutorial - Authentication".
Half way through "Now, on every request, the AuthenticationMiddleware will inspect the request session to look for an authenticated user. If we are loading the /users/login page, it will also inspect the posted form data (if any) to extract the credentials."
When I try to access articles or users I get an error:
( ! ) Fatal error: Interface
'Authentication\AuthenticationServiceProviderInterface' not found in
C:\wamp64\www\cake\src\Application.php on line 41
I have tried to figure out why this would be, but I cannot find it. I have tried looking up the same problem on the internet, no dice. Not even a mention that this could be security related (I found a mention about strict brower settings earlier but it was related to another problem).
I have uploaded my code on Github here: https://github.com/plafeber/cakephp-tutorial
I would greatly appreciate any feedback. I was under the assumption that if I create the full code set from the tutorial, given of course I run CakePHP 4.1.5 and follow the related Cake 4.x manual, that it would work. However, I already found out that I have to change the line about the use of DefaultPasswordHasher compared to what was in the code. So I can imagine the Tutorial page is not exactly as it should be.
This would be hte correct line about the use of the DefaultPasswordHasher in User.php;
//the use line
use Cake\Auth\DefaultPasswordHasher as AuthDefaultPasswordHasher;
//and the function
protected function _setPassword(string $password) : ?string
{
if (strlen($password) > 0) {
$hasher = new AuthDefaultPasswordHasher();
return $hasher->hash($password);
}
}
The solution to this was to navigate to the Cake install dir (containing the src and config folder and so on), then running the Composer call again. This apparently placed the filed in the right directories and then the error no longer appeared.

Alexa Skill kit -Lambda function - Could not validate SpeechletRequest (java)

I tried to create a HelloWorld Skill based on https://github.com/amzn/alexa-skills-kit-java but when I tested the lambda function it showed this error
{
"errorMessage":"com.amazon.speech.speechlet.SpeechletRequestHandlerException: Could not validate SpeechletRequest null using verifier ApplicationIdSpeechletRequestVerifier, rejecting request",
"errorType": "java.lang.RuntimeException",
"stackTrace": [ "com.amazon.speech.speechlet.lambda.SpeechletRequestStreamHandler.handleRequest(SpeechletRequestStreamHandler.java:101)",
"helloworld.HelloWorldSpeechletRequestStreamHandler.handleRequest(HelloWorldSpeechletRequestStreamHandler.java:43)"
],
"cause": {
"errorMessage": "Could not validate SpeechletRequest null using
verifier ApplicationIdSpeechletRequestVerifier, rejecting request",
"errorType": "com.amazon.speech.speechlet.SpeechletRequestHandlerException",
"stackTrace": [
"com.amazon.speech.speechlet.SpeechletRequestHandler.handleSpeechletCall(SpeechletRequestHandler.java:73)",
"com.amazon.speech.speechlet.lambda.SpeechletRequestStreamHandler.handleRequest(SpeechletRequestStreamHandler.java:98)",
"helloworld.HelloWorldSpeechletRequestStreamHandler.handleRequest(HelloWorldSpeechletRequestStreamHandler.java:43)"
]
}
}
This is my Java file
public final class HelloWorldSpeechletRequestStreamHandler extends SpeechletRequestStreamHandler {
private static final Set<String> supportedApplicationIds = new HashSet<String>();
static {
/*
* This Id can be found on https://developer.amazon.com/edw/home.html#/ "Edit" the relevant
* Alexa Skill and put the relevant Application Ids in this Set.
*/
supportedApplicationIds.add("amzn1.echo-sdk-ams.app.[amzn1.echo-sdk-ams.app.56bcdaf9-97fc-47f9-9918-43cb6a90d9f5]");
}
public HelloWorldSpeechletRequestStreamHandler() {
super(new HelloWorldSpeechlet(), supportedApplicationIds);
}
}
What am i missing??
You have the wrong ID in the supported application ID. That id needs to be the ID of the Alexa Skills application, which can be found on the Skill Information page. It should look something like this:
supportedApplicationIds.add("amzn1.ask.skill.c236d019-7d2a-5c96-a02f-ef8ab6f8e023");
I know the demo has is with [place id here] But you really replace the whole thing.
For me I got this exception because I was trying to run my lambda function without a proper test event JSON under the Actions tab. If you click the 'Actions' tab and then click 'Configure Test Event' you are supposed give your function input in JSON form that it can interpret. After much looking I figured out that you can get this JSON by going to the developer console where you made your skill that has all your skill configurations. On the left hand side click on the 'Test' tab and then go to the section that says 'Service Simulator'. There is a text box that says 'Enter Utterance' where you can enter a voice command to your function in text e.g 'Alexa tell [yourApp] to say Hello'. Click the 'Ask [yourApp] ' button and a Lambda request JSON will be generated on the left hand box, with the output on the right. Then just copy and paste that JSON in the left into your test event in your lambda console and then you should be good.
I tried to create a Address Skill included in https://github.com/amzn/alexa-skills-kit-java, but I got the same type of error.
It turns out that the problem was in DeviceAddressSpeechletRequestStreamHandler and creating instance of Set<String> supportedApplicationIds in static {} block.
When I moved new HashSet<>(); to declaring attributes of class, it started working.
I would put the static code onto the class you have created that extends SpeechletLambda. This is, I believe, where the evaluation takes place and gets resolved before this class is loaded and its static code executed.
Alternatively you can just turn the validation off. If someone knows your development environment well enough to call your private lambda function, they probably know enough to spoof your application ID. So there isn't a lot of security value to validating it. For an example of turning it off, see here.

App Engine Instance ID

Is it possible to get info on what instance you're running on? I want to output just a simple identifier for which instance the code is currently running on for logging purposes.
Since there is no language tag, and seeing your profile history, I assume you are using GAE/J?
In that case, the instance ID information is embedded in one of the environment attributes that you could get via ApiProxy.getCurrentEnvironment() method. You could then extract the instance id from the resulting map using key BackendService.INSTANCE_ID_ENV_ATTRIBUTE.
Even though the key is stored in BackendService, this approach will also work for frontend instances. So in summary, the following code would fetch the instance ID for you:
String tInstanceId = ApiProxy.getCurrentEnvironment()
.getAttributes()
.get( BackendService.INSTANCE_ID_ENV_ATTRIBUTE )
.toString();
Please keep in mind that this approach is quite undocumented by Google, and might subject to change without warning in the future. But since your use case is only for logging, I think it would be sufficient for now.
With the advent of Modules, you can get the current instance id in a more elegant way:
ModulesServiceFactory.getModulesService().getCurrentInstanceId()
Even better, you should wrap the call in a try catch so that it will work correctly locally too.
Import this
import com.google.appengine.api.modules.ModulesException;
import com.google.appengine.api.modules.ModulesServiceFactory;
Then your method can run this
String instanceId = "unknown";
try{
instanceId = ModulesServiceFactory.getModulesService().getCurrentInstanceId();
} catch (ModulesException e){
instanceId = e.getMessage();
}
Without the try catch, you will get some nasty errors when running locally.
I have found this super useful for debugging when using endpoints mixed with pub-sub and other bits to try to determine why some things work differently and to determine if it is related to new instances.
Not sure about before, but today in 2021 the system environment variable GAE_INSTANCE appears to contain the instance id:
instanceId = System.getenv("GAE_INSTANCE")

Resources