obfuscated page content while using output cache in mvc 5 - obfuscation

I'm developing a web application using MVC5 and I use output cache attribute on one of my actions.
the problem is after multiple page visits, my page will have binary content that looks like this:
page screenshot
there's my source:
[OutputCache(Duration = 86400, Location = OutputCacheLocation.ServerAndClient,VaryByParam = "ID;slug")]
[SeoOptionsFilter]
public ActionResult CourseDetail(int ID, string slug)
{
var courseComplexObject =
_courseRepository.GetCourseDetail(ID, slug);
var course = courseComplexObject.Model;
TempData["breadcrumb"] = BreadcrumbViewBuilder
.New().Home().Courses()
.AddMore(course.CategoryName, "/Category/" + course.CategoryId.ToString() + "/" + course.CategorySlug)
.AddMore(course.Name, "/CourseDetails/" + course.CourceId.ToString() + "/" + course.Slug + ".aspx",
true)
.Build();
//TODO: What if course is null
SetViewBagIfUserIsRegistered(course, ID);
TempData["CourseVideoCatOn"] = GetCourseVideoCategory(ID);
TempData["CourseID"] = ID;
TempData.Keep();
// Response.Cache.SetOmitVaryStar(true);
return View(courseComplexObject.Model);
}
I commented my custom action filter [SeoOptionsFilter]
but the result already has errors.
this error occurs randomly in page visits

Related

irregular arbitrarily Cross-Origin request blocked error in Django on Ionos hosting

I am currently making a quiz project for school. The quiz is supposed to evaluate in the front-end, which works perfectly fine, and after that it should send the information to the backend. The backend should save the details to the database and after that it should send the information back to the front-end. The front-end should show a message with some details about how you performed compared to all the others, the information is gotten from the database.
So, the weird thing about all that is that it works sometimes and sometimes not. If I test it on localhost, the code works perfectly fine. The error only occurs on the Ionos server (I got a hosting contract so I do not have access to the console).
Here is the link to my website: https://tor-netzwerk-seminarfach2024.com/ . If you click on the upper left corner and then on the quiz button, you will get to the quiz. If I look at the console and network analytics, when the error occurs, the following message shows up:
Cross-Origin request blocked: The same source rule prohibits reading
the external resource on https://api. tor-netzwerk-seminarfach2024.
com/apache-test/. (Reason: CORS request failed).
https://api.tor-netzwerk-seminarfach2024.com/apache-test/ is my backend and https://tor-netzwerk-seminarfach2024.com/ is my front-end.
The settings file in Django is not the problem. To make it clear, the error does not occur always. Sometimes you have to go again to the website and try it one a few more times until you get the error.
If I look at the network analytics, then I see that the client request works as usual, but the answer field from the server is completely empty. Let's move on to the weirdest part of all: the Data actually gets saved in the Database, there is just sometimes no response?
It would be way to much for a minimum working product but here is the most important code. If you want to test it you can just visit the link to my Website:https://tor-netzwerk-seminarfach2024.com/
Server side with Django
from rest_framework.response import Response
from .models import Lead
import json
from .serializer import TestingSerializer
def updateDigits():
leadObj = Lead.objects.all()
currentScore = leadObj[0].sumScore; currentRequests = leadObj[0].sumRequests
valueBtnOne = leadObj[0].buttonOne; valueBtnTwo = leadObj[0].buttonTwo;
valueBtnThree = leadObj[0].buttonThree; valueBtnFour = leadObj[0].buttonFour;
valueBtnFive = leadObj[0].buttonFive; valueBtnSix = leadObj[0].buttonSix;
valueBtnSeven = leadObj[0].buttonSeven; valueBtnEight = leadObj[0].buttonEight;
valueBtnNine = leadObj[0].buttonNine;
return [valueBtnOne,valueBtnTwo,valueBtnThree,valueBtnFour,valueBtnFive,valueBtnSix,valueBtnSeven,valueBtnEight,valueBtnNine,currentScore,currentRequests]
#api_view(["POST"])
def home(request):
body_unicode = request.body.decode('utf-8')
body = json.loads(body_unicode)
result = body["Result"]
isItRight = body["whichOnesRight"]
ip_adress = get_client_ip(request)
queryset = Lead.objects.all()
if Lead.objects.all().exists():
currentValues = updateDigits()
queryset.update(
sumScore = result + currentValues[9],
sumRequests = 1 + currentValues[10],
buttonOne = currentValues[0] + isItRight[0],
buttonTwo = currentValues[1] + isItRight[1],
buttonThree = currentValues[2] + isItRight[2],
buttonFour = currentValues[3] + isItRight[3],
buttonFive = currentValues[4] + isItRight[4],
buttonSix = currentValues[5] + isItRight[5],
buttonSeven = currentValues[6] + isItRight[6],
buttonEight = currentValues[7] + isItRight[7],
buttonNine = currentValues[8] + isItRight[8],
)
currentValues = updateDigits()
else:
obj = Lead()
obj.save()
serializer = TestingSerializer(queryset[0], many =False)
return Response({**serializer.data, "myResult": result})
The Django model
from django.db import models
class Lead(models.Model):
sumScore = models.IntegerField(default=0)
sumRequests = models.IntegerField(default=0)
buttonOne = models.IntegerField(default=0)
buttonTwo = models.IntegerField(default=0)
buttonThree = models.IntegerField(default=0)
buttonFour = models.IntegerField(default=0)
buttonFive = models.IntegerField(default=0)
buttonSix = models.IntegerField(default=0)
buttonSeven = models.IntegerField(default=0)
buttonEight = models.IntegerField(default=0)
buttonNine = models.IntegerField(default=0)
serializer just serializes _ _ all _ _ ...
Front end with React
function evaluateAndCheckQuiz(){
let countNotChosen = howManyNotChosen()
if(countNotChosen){ answerQuestions(countNotChosen); return }
let points = evaluateQuiz()
document.getElementById("scoreText").style.display = "block"
document.getElementById("scoreNumber").textContent = points
return points
}
Calling sendAndGetQuizAverage with evaluateAndCheckQuiz as a parameter
function sendAndGetQuizAverage(points){
const myPostRequest = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({"Result":points, "whichOnesRight": whichOnesRight}),
};
let data
fetch('https://api.tor-netzwerk-seminarfach2024.com/apache-test/', myPostRequest).then(
(response) => data = response.json()).then(
(data) => {showResultAnalyse(data)})
}
function showResultAnalyse(data){
console.log(data)
let averageScore = (data["sumScore"] / data["sumRequests"]).toFixed(2)
let myResult = data["myResult"]
changeLinksToStatistics([
data["buttonOne"],data["buttonTwo"],data["buttonThree"],
data["buttonFour"],data["buttonFive"],data["buttonSix"],
data["buttonSeven"],data["buttonEight"],data["buttonNine"]
],
data["sumRequests"]
)
swal(
{
text:`${checkWhichText(myResult,averageScore,data["sumRequests"] )}`,
icon: myResult > averageScore ?"success": "error",
button:"Oki doki",
}
);
}

Salesforce Code Coverage Failure. Your code coverage is 12%. You need at least 75% coverage to complete this deployment

I wanted to deploy my code to production. In this apex code, I am calling a third party api for opportunity on click of button which triggers the doSomething() from VF page. I want to fix this issue and push the below code to my production account.
Here is my apex class code
{
private ApexPages.StandardController standardController;
public DetailButtonController(ApexPages.StandardController standardController)
{
this.standardController = standardController;
}
public PageReference doSomething()
{
// Apex code for handling record from a Detail page goes here
Id recordId = standardController.getId();
Opportunity record = (Opportunity) standardController.getRecord();
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http http = new Http();
req.setEndpoint('https://mergeasy.com/merge_file');
req.setMethod('POST');
//function to Convert date to mm/dd/yyy
Date dToday = record.Closing_Date__c;
String clos_date = 'On or before ' + dToday.month() + '/' + dToday.day() + '/' + dToday.year();
Date dAcc = record.Offer_Acceptance_Date__c;
String acc_date = dAcc.month() + '/' + dAcc.day() + '/' + dAcc.year();
String str1 = '' + record.Purchase_Price__c ;
String f_p_price = str1.SubStringBefore('.');
String str2 = '' + record.Escrow_Deposit__c ;
String e_d_price = str2.SubStringBefore('.');
String str3 = '' + record.Balance__c ;
String b_price = str3.SubStringBefore('.');
if(record.Second_Seller_Name_Phone__c==null && record.Second_Seller_Email__c==null && record.Name!=null && record.Company_Profile__c!=null){
req.setBody('seller_name='+record.Name+'&buyer_name='+record.Company_Profile__c+'&county='+record.County_Contract__c+'&street_address='+record.Left_Main__Address_1__c+'&p_price='+f_p_price+'&escrow_deposit='+e_d_price+'&title_agent='+record.Escrow_Agent_Name__c+'&title_address='+record.Escrow_Address__c+'&title_phone='+record.Escrow_Number__c+'&balance='+b_price+'&accept_date='+acc_date+'&closing_date='+clos_date+'&inspection_days='+record.Inspection_Days__c+'&special_clause='+record.Special_Clauses__c+'&doc_id=XXXXXXXXXX&doc_name=Contract.pdf&delivery_method=docusign&sign_order=true&recipient1_email='+record.Email__c+'&recipient1_name='+record.Name+'&recipient2_name='+record.Company_Profile__c+'&recipient2_email=developer.c2c#gmail.com&docusign_doc_name=Contract - Attorney Involved&email_subject=Contract:'+record.Left_Main__Address_1__c+'&email_body=Hi please sign the attached contract');
}
else if(record.Second_Seller_Name_Phone__c!=null && record.Second_Seller_Email__c!=null && record.Name!=null && record.Company_Profile__c!=null){
String name = record.Name + ' and ' + record.Second_Seller_Name_Phone__c ;
req.setBody('seller_name='+name+'&buyer_ame='+record.Company_Profile__c+'&county='+record.County_Contract__c+'&street_address='+record.Left_Main__Address_1__c+'&p_price='+f_p_price+'&escrow_deposit='+e_d_price+'&title_agent='+record.Escrow_Agent_Name__c+'&title_address='+record.Escrow_Address__c+'&title_phone='+record.Escrow_Number__c+'&balance='+b_price+'&accept_date='+acc_date+'&closing_date='+clos_date+'&inspection_days='+record.Inspection_Days__c+'&special_clause='+record.Special_Clauses__c+'&doc_id=XXXXXXXXXX&doc_name=Contract.pdf&delivery_method=docusign&sign_order=true&recipient1_email='+record.Email__c+'&recipient1_name='+record.Name+'&recipient2_name='+record.Second_Seller_Name_Phone__c+'&recipient2_email='+record.Second_Seller_Email__c+'&recipient3_email=developer.c2c#gmail.com&recipient3_name='+record.Company_Profile__c+'&docusign_doc_name=Contract - Normal(1S1B).pdf&email_subject=Contract:'+record.Left_Main__Address_1__c+'&email_body=Hi please sign the attached contract');
}
req.setHeader('Authorization', 'Bearer XXXXXXXXXXXXXX');
try {
res = http.send(req);
} catch(System.CalloutException e) {
System.debug('Callout error: '+ e);
System.debug(res.toString());
}
return null;
}
}
Here is the test class, which is showing 90% code coverage.
//testClasst.apxc
#isTest
public class testClassBt {
#isTest
static void testPostCallout() {
System.Test.setMock(HttpCalloutMock.class, new TestClass());
Opportunity opp = new Opportunity();
opp.Name='Rickson Developer';
opp.StageName='Underwrite';
opp.CloseDate= date.newInstance(1991, 2, 21);
opp.Closing_Date__c= date.newInstance(1991, 2, 21);
opp.Offer_Acceptance_Date__c =date.newInstance(1991, 2, 21);
opp.Purchase_Price__c = 1200.00;
opp.Escrow_Deposit__c= 1200.00;
opp.Company_Profile__c='RFTA Properties, LLC';
opp.County_Contract__c='Orange';
opp.Left_Main__Address_1__c='123 Main Street';
opp.Escrow_Agent_Name__c='Test Agent';
opp.Escrow_Address__c='123 Main street';
opp.Escrow_Number__c='9892132382';
opp.Inspection_Days__c=34;
opp.Special_Clauses__c='Test';
insert opp;
ApexPages.StandardController standardController = new ApexPages.StandardController(opp);
DetailButtonController strResp = new DetailButtonController(standardController);
strResp.doSomething();
}
}
//TestClass.apxc
#isTest
global class TestClass implements HttpCalloutMock {
global HTTPResponse respond(HTTPRequest request) {
HttpResponse response = new HttpResponse();
response.setHeader('Content-Type', 'application/json');
response.setBody('{"animal": {"id":1, "name":"Tiger"}}');
response.setStatusCode(200);
return response;
}
}
assuming that during the validation process you run just the test methods of this class, did you try to run your test class in Sandbox first?
Some IDE and the Salesforce Developer Console itself show you the covered lines after the unit test execution.
Just follow the green lines to debug the code and understand where the exception has been thrown.
If you could post the Test class too, we can help you more.

Azure AD Microsoft Identity Web OpenIdConnectEvents - How to access optional claims from the user token during sign out

Using Net Core 3.1 with Microsoft Identity Web and Azure AD.
I'm trying to setup some logging for when a user signs in and out of my web app project. The logging needs to include details of the user as well as the IP Address of the client endpoint they used during sign in and sign out. I then pass the IP Address through an extension method for capturing Geo Location info that is added to the log event for that user authentication.
In startup.cs I have configured some extended options for the OpenIdConnectOptions, they are:
OnTokenValidated
OnRedirectToIdentityProviderForSignOut
OnSignedOutCallbackRedirect
The OpenIdEvents class I created is just simply to move away the methods from the startup.cs file for cleanliness.
Extract from startup.cs below:
// Create a new instance of the class that stores the methods called
// by OpenIdConnectEvents(); i.e. when a user logs in or out the app.
// See section below :- 'services.Configure'
OpenIdEvents openIdEvents = new OpenIdEvents();
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
// The claim in the Jwt token where App roles are available.
options.TokenValidationParameters.RoleClaimType = "roles";
// Advanced config - capturing user events. See OpenIdEvents class.
options.Events ??= new OpenIdConnectEvents();
options.Events.OnTokenValidated += openIdEvents.OnTokenValidatedFunc;
// This is event is fired when the user is redirected to the MS Signout Page (before they've physically signed out)
options.Events.OnRedirectToIdentityProviderForSignOut += openIdEvents.OnRedirectToIdentityProviderForSignOutFunc;
// DO NOT DELETE - May use in the future.
// OnSignedOutCallbackRedirect doesn't produce any claims to read for the user after they have signed out.
options.Events.OnSignedOutCallbackRedirect += openIdEvents.OnSignedOutCallbackRedirectFunc;
});
So far I have found a solution to capture the required claims of the user for when they sign in, the 'TokenValidatedContext' passed to the first method 'OnTokenValidatedFunc' contains details of the security token which in itself shows the optional claims that I had configured including the IP Address (referred to as "ipaddr")
Some of these optional claims were configured in the App manifest file in Azure, they are present in the security token in this first method so pretty sure Azure is setup correctly.
Extract from Azure App Manifest File:
"optionalClaims": {
"idToken": [
{
"name": "family_name",
"source": null,
"essential": false,
"additionalProperties": []
},
{
"name": "given_name",
"source": null,
"essential": false,
"additionalProperties": []
},
{
"name": "ipaddr",
"source": null,
"essential": false,
"additionalProperties": []
}
],
"accessToken": [],
"saml2Token": []
},
'OnTokenValidatedFunc' method shown below:
/// <summary>
/// Invoked when an IdToken has been validated and produced an AuthenticationTicket.
/// See weblink: https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectevents.ontokenvalidated?view=aspnetcore-3.0
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task OnTokenValidatedFunc(TokenValidatedContext context)
{
var token = context.SecurityToken;
var userId = token.Claims.First(claim => claim.Type == "oid").Value;
var givenName = token.Claims.First(claim => claim.Type == "given_name").Value;
var familyName = token.Claims.First(claim => claim.Type == "family_name").Value;
var userName = token.Claims.First(claim => claim.Type == "preferred_username").Value;
string ipAddress = token.Claims.First(claim => claim.Type == "ipaddr").Value;
GeoHelper geoHelper = new GeoHelper();
var geoInfo = await geoHelper.GetGeoInfo(ipAddress);
string logEventCategory = "Open Id Connect";
string logEventType = "User Login";
string logEventSource = "WebApp_RAZOR";
string logCountry = "";
string logRegionName = "";
string logCity = "";
string logZip = "";
string logLatitude = "";
string logLongitude = "";
string logIsp = "";
string logMobile = "";
string logUserId = userId;
string logUserName = userName;
string logForename = givenName;
string logSurname = familyName;
string logData = "User login";
if (geoInfo != null)
{
logCountry = geoInfo.Country;
logRegionName = geoInfo.RegionName;
logCity = geoInfo.City;
logZip = geoInfo.Zip;
logLatitude = geoInfo.Latitude.ToString();
logLongitude = geoInfo.Longitude.ToString();
logIsp = geoInfo.Isp;
logMobile = geoInfo.Mobile.ToString();
}
// Tested on 31/08/2020
Log.Information(
"{#LogEventCategory}" +
"{#LogEventType}" +
"{#LogEventSource}" +
"{#LogCountry}" +
"{#LogRegion}" +
"{#LogCity}" +
"{#LogZip}" +
"{#LogLatitude}" +
"{#LogLongitude}" +
"{#LogIsp}" +
"{#LogMobile}" +
"{#LogUserId}" +
"{#LogUsername}" +
"{#LogForename}" +
"{#LogSurname}" +
"{#LogData}",
logEventCategory,
logEventType,
logEventSource,
logCountry,
logRegionName,
logCity,
logZip,
logLatitude,
logLongitude,
logIsp,
logMobile,
logUserId,
logUserName,
logForename,
logSurname,
logData);
await Task.CompletedTask.ConfigureAwait(false);
}
See Debug shots below:
When expanding the claims, you can see the claim for "ipaddr" is shown:
MY ISSUE:
The other event types fired from OpenIdConnectEvents for when the user signs out, does not function in the same way and this is where I am stuck!
There are two different event types I have tried testing with:
OnRedirectToIdentityProviderForSignOut
OnSignedOutCallbackRedirect
Each one is fired at a slightly different point during the user sign out process i.e. the 'OnRedirectToIdentityProviderForSignOutFunc' is fired when the user is being re-directed to the Microsoft Sign Out page, just before they actually hit the button and sign out.
This is not an ideal event type to work with given the user could abort signing out of the application and the log generated would not reflect this, however I have so far found that I could at least access most of the claims of the user, BUT the "ipaddr" claim is not listed and I simply don't know why or how to get it.
When I look at the Debug info I find the security token is not shown at all and the only way to access the user claims was to read another part of the context by navigating to context.HttpContext.User.Claims
Debug Screenshot:
The method for this shown below:
public async Task OnRedirectToIdentityProviderForSignOutFunc(RedirectContext context)
{
var user = context.HttpContext.User;
string ipAddress = user.Claims.FirstOrDefault(claim => claim.Type == "ipaddr").Value;
var userId = user.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var givenName = user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value;
var familyName = user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value;
var userName = user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
// The IP Address claim is missing!
//string ipAddress = claims.First(claim => claim.Type == "ipaddr").Value;
await Task.CompletedTask.ConfigureAwait(false);
}
The above method only gives a partial solution given I still need the IP Address claim but is not present at all, but the choice in using this event type as explained above is not ideal anyway.
AND FINALLY:
Trying to subscribe to the final option 'OnSignedOutCallbackRedirect' has been a complete waste of time so far given none of the user claims are present at all in the context. It seems that Microsoft dumps them once the user has hit the Sign out button and returned back to the 'Signed Out' page in the web app.
Really I want a solution for when the user has actually signed out, not half way through the process of signing out, BUT I must be able access the user claims including the IP Address which is not present in either of the above two events fired during this process.
All I want is to simply capture the details (claims) of the user and the IP Address of the client session they are connecting from and log this when they sign in and sign out of the web application. Is this really too much to ask!
Documentation on this is very sparse, I would much appreciate some clues from anyone out there who understands well how MS Identity Web and OpenIDConnect Events function behind the scenes.
Solution 1 = Being able to access the IP Address claim from the context during 'OnRedirectToIdentityProviderForSignOut' but it is currently missing...
Solution 2 (Preferred) = Being able to access the user claims during 'OnSignedOutCallbackRedirect' but currently none of them are listed at all.
Thanks in advance...
I need to be able to access the claims from the user once they have signed out of the application using one of two possible events that are generated from OpenIdConnect
The user signed out at that point. He/She is no longer there, so it makes sense it has no claims, it's going back to the default, empty anonymous user because it's signed out.
As mentioned in my comments above, and also taking on board Jean-Marc Prieur's comments above on the fact that no claims will ever be present once the user has completely signed out, I ended up just grabbing the details of the user context through OnRedirectToIdentityProviderForSignOutFunc method and then used a separate GeoHelper class to fetch the IP Address of the destination where the person was when singing out (or should we say was about to sign out!
Yes appreciate this isn't the most ideal cause & affect, but to be honest it's not going to present an big issue for me and may not for others given in most cases, logging when someone signs out is not business critical, its more to get a idea of system usage. By the time someone has reached the MS popup page to logout, we should likely assume 99% of cases that they will proceed and actually logout.
So below is the code I used to achieve the above scenario:
startup.cs class (extract from startup.cs code bloat)
// Create a new instance of the class that stores the methods called
// by OpenIdConnectEvents(); i.e. when a user logs in or out the app.
// See section below :- 'services.Configure'
OpenIdEvents openIdEvents = new OpenIdEvents();
// The following lines code instruct the asp.net core middleware to use the data in the "roles" claim in the Authorize attribute and User.IsInrole()
// See https://learn.microsoft.com/aspnet/core/security/authorization/roles?view=aspnetcore-2.2 for more info.
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
// The claim in the Jwt token where App roles are available.
options.TokenValidationParameters.RoleClaimType = "roles";
// Advanced config - capturing user events. See OpenIdEvents class.
options.Events ??= new OpenIdConnectEvents();
options.Events.OnTokenValidated += openIdEvents.OnTokenValidatedFunc;
// This is event is fired when the user is redirected to the MS Signout Page (before they've physically signed out)
options.Events.OnRedirectToIdentityProviderForSignOut += openIdEvents.OnRedirectToIdentityProviderForSignOutFunc;
// DO NOT DELETE - May use in the future.
// OnSignedOutCallbackRedirect doesn't produce any user claims to read from for the user after they have signed out.
options.Events.OnSignedOutCallbackRedirect += openIdEvents.OnSignedOutCallbackRedirectFunc;
});
My custom class for the Geolocation:
namespace MyProject.Classes.GeoLocation
{
/// <summary>
/// See weblink for API documentation: https://ip-api.com/docs or https://ip-api.com/docs/api:json
/// Note: Not free for commercial use - fee plan during development only!
/// Sample query: http://ip-api.com/json/{ip_address}?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,isp,mobile,query
/// </summary>
public class GeoHelper
{
private readonly HttpClient _httpClient;
public GeoHelper()
{
_httpClient = new HttpClient()
{
Timeout = TimeSpan.FromSeconds(5)
};
}
public async Task<GeoInfo> GetGeoInfo(string ip)
{
try
{
var response = await _httpClient.GetAsync($"http://ip-api.com/json/{ip}?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,isp,mobile,query");
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<GeoInfo>(json);
}
}
catch (Exception)
{
// Do nothing, just return null.
}
return null;
}
}
}
OpenIdEvents.cs class:
namespace MyProject.Classes.Security
{
public class OpenIdEvents
{
// Create the concurrent dictionary to store the user's IP Addresss when they sign in, the value is fetched
// from the dictionary when they sing out. given this information is not present within the contect passed through the event.
private readonly ConcurrentDictionary<string, string> IpAddressDictionary = new ConcurrentDictionary<string, string>();
/// <summary>
/// Invoked when an IdToken has been validated and produced an AuthenticationTicket.
/// See weblink: https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectevents.ontokenvalidated?view=aspnetcore-3.0
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task OnTokenValidatedFunc(TokenValidatedContext context)
{
var token = context.SecurityToken;
var userId = token.Claims.First(claim => claim.Type == "oid").Value;
var givenName = token.Claims.First(claim => claim.Type == "given_name").Value;
var familyName = token.Claims.First(claim => claim.Type == "family_name").Value;
var username = token.Claims.First(claim => claim.Type == "preferred_username").Value;
var ipAddress = token.Claims.First(claim => claim.Type == "ipaddr").Value;
// Add the IP Address from the user's ID Token to the dictionary, we will remove
// it from the dictionary when the user requests a sign out through OpenIDConnect.
IpAddressDictionary.TryAdd(userId, ipAddress);
GeoHelper geoHelper = new GeoHelper();
var geoInfo = await geoHelper.GetGeoInfo(ipAddress);
string logEventCategory = "Open Id Connect";
string logEventType = "User Sign In";
string logEventSource = "MyProject";
string logCountry = "";
string logRegionName = "";
string logCity = "";
string logZip = "";
string logLatitude = "";
string logLongitude = "";
string logIsp = "";
string logMobile = "";
string logUserId = userId;
string logUserName = username;
string logForename = givenName;
string logSurname = familyName;
string logData = "User with username [" + username + "] forename [" + givenName + "] surname [" + familyName + "] from IP Address [" + ipAddress + "] signed into the application [MyProject] Succesfully";
if (geoInfo != null)
{
logCountry = geoInfo.Country;
logRegionName = geoInfo.RegionName;
logCity = geoInfo.City;
logZip = geoInfo.Zip;
logLatitude = geoInfo.Latitude.ToString();
logLongitude = geoInfo.Longitude.ToString();
logIsp = geoInfo.Isp;
logMobile = geoInfo.Mobile.ToString();
}
// Tested on 31/08/2020
Log.Information(
"{#LogEventCategory}" +
"{#LogEventType}" +
"{#LogEventSource}" +
"{#LogCountry}" +
"{#LogRegion}" +
"{#LogCity}" +
"{#LogZip}" +
"{#LogLatitude}" +
"{#LogLongitude}" +
"{#LogIsp}" +
"{#LogMobile}" +
"{#LogUserId}" +
"{#LogUsername}" +
"{#LogForename}" +
"{#LogSurname}" +
"{#LogData}",
logEventCategory,
logEventType,
logEventSource,
logCountry,
logRegionName,
logCity,
logZip,
logLatitude,
logLongitude,
logIsp,
logMobile,
logUserId,
logUserName,
logForename,
logSurname,
logData);
await Task.CompletedTask.ConfigureAwait(false);
}
/// <summary>
/// Invoked before redirecting to the identity provider to sign out.
/// See weblink: https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectevents.onredirecttoidentityproviderforsignout?view=aspnetcore-3.0&viewFallbackFrom=aspnetcore-3.1
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task OnRedirectToIdentityProviderForSignOutFunc(RedirectContext context)
{
var user = context.HttpContext.User;
var userId = user.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var givenName = user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value;
var familyName = user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value;
var username = user.Identity.Name;
string logEventCategory = "Open Id Connect";
string logEventType = "User Sign Out";
string logEventSource = "MyProject";
string logCountry = "";
string logRegionName = "";
string logCity = "";
string logZip = "";
string logLatitude = "";
string logLongitude = "";
string logIsp = "";
string logMobile = "";
string logUserId = userId;
string logUserName = username;
string logForename = givenName;
string logSurname = familyName;
IpAddressDictionary.TryRemove(userId, out string ipAddress);
if (ipAddress != null)
{
// Re-fetch the geo-location details which may be different than the login session
// given the user might have been signed in using a cell phone and move locations.
GeoHelper geoHelper = new GeoHelper();
var geoInfo = await geoHelper.GetGeoInfo(ipAddress);
if (geoInfo != null)
{
logCountry = geoInfo.Country;
logRegionName = geoInfo.RegionName;
logCity = geoInfo.City;
logZip = geoInfo.Zip;
logLatitude = geoInfo.Latitude.ToString();
logLongitude = geoInfo.Longitude.ToString();
logIsp = geoInfo.Isp;
logMobile = geoInfo.Mobile.ToString();
}
}
string logData = "User with username [" + username + "] forename [" + givenName + "] surname [" + familyName + "] from IP Address [" + ipAddress + "] signed out the application [MyProject] Succesfully";
// Tested on 31/08/2020
Log.Information(
"{#LogEventCategory}" +
"{#LogEventType}" +
"{#LogEventSource}" +
"{#LogCountry}" +
"{#LogRegion}" +
"{#LogCity}" +
"{#LogZip}" +
"{#LogLatitude}" +
"{#LogLongitude}" +
"{#LogIsp}" +
"{#LogMobile}" +
"{#LogUserId}" +
"{#LogUsername}" +
"{#LogForename}" +
"{#LogSurname}" +
"{#LogData}",
logEventCategory,
logEventType,
logEventSource,
logCountry,
logRegionName,
logCity,
logZip,
logLatitude,
logLongitude,
logIsp,
logMobile,
logUserId,
logUserName,
logForename,
logSurname,
logData);
await Task.CompletedTask.ConfigureAwait(false);
}
/// <summary>
/// Invoked before redirecting to the SignedOutRedirectUri at the end of a remote sign-out flow.
/// See weblink: https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectevents.onsignedoutcallbackredirect?view=aspnetcore-3.0
/// Not currently in use becuase neither the user's ID Token or claims were present. We had to use the above method instead.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task OnSignedOutCallbackRedirectFunc(RemoteSignOutContext context)
{
await Task.CompletedTask.ConfigureAwait(false);
}
}
}

Unable to send attachment - Salesforce Docusign API

I am trying to send attachment (record has one attachment) in opportunity record via Apex and Docusign "CreateAndSendEnvelope" API.
But I am getting this error "The DocuSign EnvelopeId:Exception - System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: An Error Occurred during anchor tag processing. Invalid document faultcode=soap:Client faultactor=https://demo.docusign.net/api/3.0/dsapi.asmx"
Below is the piece of code used.
// Render the contract
System.debug('Rendering the contract');
PageReference pageRef = new PageReference('/apex/RenderContract');
pageRef.getParameters().put('id',contract.Id);
//Blob pdfBlob = pageRef.getContent();
Attachment att = [SELECT Id, Name, Body, ContentType FROM Attachment WHERE Parentid = :contract.Id LIMIT 1];
Blob pdfBlob = att.Body;
// Document
DocuSignAPI.Document document = new DocuSignAPI.Document();
document.ID = 1;
document.pdfBytes = EncodingUtil.base64Encode(pdfBlob);
document.Name = 'Contract';
document.FileExtension = 'pdf';
envelope.Documents = new DocuSignAPI.ArrayOfDocument();
envelope.Documents.Document = new DocuSignAPI.Document[1];
envelope.Documents.Document[0] = document;
// Recipient
System.debug('getting the contact');
Contact contact = [SELECT email, FirstName, LastName
from Contact where id = :contract.CustomerSignedId];
DocuSignAPI.Recipient recipient = new DocuSignAPI.Recipient();
recipient.ID = 1;
recipient.Type_x = 'Signer';
recipient.RoutingOrder = 1;
recipient.Email = contact.Email;
recipient.UserName = contact.FirstName + ' ' + contact.LastName;
// This setting seems required or you see the error:
// "The string '' is not a valid Boolean value.
// at System.Xml.XmlConvert.ToBoolean(String s)"
recipient.RequireIDLookup = false;
envelope.Recipients = new DocuSignAPI.ArrayOfRecipient();
envelope.Recipients.Recipient = new DocuSignAPI.Recipient[1];
envelope.Recipients.Recipient[0] = recipient;
// Tab
DocuSignAPI.Tab tab1 = new DocuSignAPI.Tab();
tab1.Type_x = 'SignHere';
tab1.RecipientID = 1;
tab1.DocumentID = 1;
tab1.AnchorTabItem = new DocuSignAPI.AnchorTab();
tab1.AnchorTabItem.AnchorTabString = 'By:';
DocuSignAPI.Tab tab2 = new DocuSignAPI.Tab();
tab2.Type_x = 'DateSigned';
tab2.RecipientID = 1;
tab2.DocumentID = 1;
tab2.AnchorTabItem = new DocuSignAPI.AnchorTab();
tab2.AnchorTabItem.AnchorTabString = 'Date Signed:';
envelope.Tabs = new DocuSignAPI.ArrayOfTab();
envelope.Tabs.Tab = new DocuSignAPI.Tab[2];
envelope.Tabs.Tab[0] = tab1;
envelope.Tabs.Tab[1] = tab2;
System.debug('Calling the API');
try {
DocuSignAPI.EnvelopeStatus es
= dsApiSend.CreateAndSendEnvelope(envelope);
envelopeId = es.EnvelopeID;
} catch ( CalloutException e) {
System.debug('Exception - ' + e );
envelopeId = 'Exception - ' + e;
}
Any ideas how to overcome this error?
Thanks.
The Original Poster's (OP's) comment is
it worked fine on rendering the whole record to pdf...but now i tried sending attachments only instead of whole record.. i started to get this error.
So my guess is that the envelope request has a document problem.
Best way to debug: see what is being sent to the DocuSign platform.
Try the beta API logger or the regular logger. Then add the log to your question by editing your question.
This problem came across me with same error .
" An Error Occurred during anchor tag processing. Invalid document faultcode=soap:Client faultactor=https://demo.docusign.net/api/3.0/dsapi.asmx "
you need to replace anchor tab string with desired string given in your attached document where signature is required.
Replace :
tab1.AnchorTabItem.AnchorTabString = 'By:';
tab2.AnchorTabItem.AnchorTabString = 'Date Signed:';
To :
tab1.AnchorTabItem.AnchorTabString = 'Signature label in your document';
tab2.AnchorTabItem.AnchorTabString = 'Signature label in your document';

Appcelerator Cloud Services Posts Object Query using AngularJS

All,
I am using Appcelerator Cloud Services as my backend for an AngularJS/Ionic Framework/PhoneGap mobile app I am working on. I am trying to query the ACS Posts object by user_id and tags_array.
My code is at the following gist :
https://gist.github.com/bp4151/d6828f8d7af983316f99
I am formatting the query string as follows:
getByFriends: function(user_ids, tag) {
var query = "where={$and[{'user_id':$in['" + user_ids + "']},{'tags_array':$in['" + tag + "']}]}";
return $http.get('https://api.cloud.appcelerator.com/v1/posts/query.json?key=' + globals.ACSKey + '&query=' + query + '&_session_id = ' + globals.session_id);
},
I modified the code to the following, but I am still having no luck
getByFriends: function(user_ids, tag) {
//return $http.get('https://api.cloud.appcelerator.com/v1/posts/query.json?key=' + globals.ACSKey + '&query=' + query + '&_session_id = ' + globals.session_id);
return $http.get('https://api.cloud.appcelerator.com/v1/posts/query.json?key=' + globals.ACSKey + '&_session_id = ' + globals.session_id, {
params: {
query: "where={$and[{'user_id':$in['" + user_ids + "']},{'tags_array':$in['" + tag + "']}]}"
}
});
},
I expect only one post record to be returned with the id I am passing in (532f233f1316e90b760eca00) but I am getting all posts back no matter what I try. If anyone can point my in the right direction, I would really appreciate it.
Thanks,
Bruce
I figured it out. The issues are
the params block is not set up properly,
I need to decode the string that is being sent.
I was confused by the documentation that stated query: where=... The correct way to do this with Angular is as I coded it below using "where":...
get: function(user_id, tag) {
console.log('PostService.get');
return $http.get('https://api.cloud.appcelerator.com/v1/posts/query.json?key=' +
globals.ACSKey,
{
params: {
"where": decodeURIComponent('{"user_id": {"$in":["' + user_id + '"]},
"tags_array":"' + tag + '"}')
}
});
},

Resources