I am working on a project that is a .NET WebForms solution that contains many PageMethod's to act like an MVC hybrid.
We have a registration service in our product that upon typing your username, will call a service to immediately check if the username has already been chosen (without calling a post-back).
What would be the most appropriate way of preventing someone from mapping out the entire username database by brute-forcing requests for true/false answers on if a string exists as a username.
The best method I can currently think of is throttling requests based on session, which would not completely stop the problem, but like encryption, make the time-cost to effectively succeed not worth it for the intruders.
In my head I was thinking along the lines of:
Public Function SomeFunction() as String
Dim throttle As Throttle = New Throttle()
If Not HttpContext.Current.Session("IsUsernameExistsThrottle") Is Nothing Then
throttle = DirectCast(HttpContext.Current.Session("IsUsernameExistsThrottle"), Throttle)
If Not throttle.SetExpiry Is Nothing Then
If throttle.SetExpiry > DateTime.Now Then
Throw New Exception("Please wait 1 minute before accessing this functionality again.")
Else
throttle.SetExpiry = Nothing
End If
Else
throttle.RequestCount += 1
If throttle.RequestCount >= 10 Then
throttle.SetExpiry = DateTime.Now.AddMinutes(1)
End If
End If
Else
throttle.SetExpiry = Nothing
throttle.RequestCount = 1
End If
HttpContext.Current.Session("IsUsernameExistsThrottle") = throttle
Return SomeValue
End Function
Public Class Throttle
Public RequestCount As Integer
Public SetExpiry As DateTime?
End Class
What this function would do is allow the first 10 requests for free, and then throttle every request afterwards to 1 per minute.
Any thoughts or suggestions, or recommendations for best practice?
Thanks,
Related
Scenario: Deactivate the user whose login date is less than 42 from today. I have an user whose last login date is 1/22/2020(US Date format)/22/1/2020 5:12 pm. Here I wrote a batch apex for deactivating. My code has executed successfully and my batch status is completed but the user record is not deactivating.
Here is the code:
global class User_Deactivation implements Database.Batchable<SObject>
{
dateTime dt = date.today()-42;
public String query = 'SELECT Name, LastLoginDate, Id From User WHERE IsActive = true AND LastLoginDate=:dt ';
global Database.querylocator start(Database.BatchableContext bc)
{
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext bc,List<User> scope)
{
List<User> userList = new List<User>();
for(User s:scope)
{
User u =(user)s;
userList.add(u);
}
if(userList.size() > 0)
{
for(User usr : userList)
{
usr.isActive = false;
}
}
update userList;
}
global void finish(Database.BatchableContext bc)
{
AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email
FROM AsyncApexJob
WHERE Id = :BC.getJobId()];
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] {a.CreatedBy.Email};
mail.setToAddresses(toAddresses);
mail.setSubject('Apex Job Status: ' + a.Status);
mail.setPlainTextBody('The batch Apex job processed ' + a.TotalJobItems + ' batches with '+ a.NumberOfErrors + ' failures.');
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
}
please help me out on this
Multiple things you can improve here, where do I begin...
Initialisation(?) piece
dateTime dt = date.today()-42;
String query = 'SELECT Name, LastLoginDate, Id From User WHERE IsActive = true AND LastLoginDate=:dt';
Do you need Date or DateTime match? The way you wrote it it'll match only people who logged in exactly at midnight. System.debug(dt); would say 2020-01-23T00:00:00.000Z. It shouldn't be an equals sign, should be "less than" or "less or equal".
Or even better - you can make it bit more clear what you want to do, bit more "semantic" so the poor guy who's going to maintain it can understand it without extra comments. This reads more natural and uses the SOQL date literals, special "constants" to simplify your logic: SELECT Id, LastLoginDate FROM User WHERE isActive = true AND LastLoginDate != LAST_N_DAYS:42
What is this section of code anyway. It's not really static variables, it's not a constructor... I think it'll behave as a constructor. Be very, very careful with constructors for batches. The state of the class at the end of the constructor gets saved (serialised) and restored every time the class is scheduled to run. It's tempting to put some initialisation code into constructor, maybe read some custom settings, precalculate stuff... But then you'll be in for nasty surprise when admin adds new custom setting and the batch doesn't pick it up. In your case it's even worse, I'd suspect it'll serialise the dt and your today() will be frozen in time, not what you expected. To be safe move all initialisation logic to start()
And I'd even say whoever gave you the requirement didn't think it through. When you make new user they get a link they need to click in next 72h. If they didn't do it (maybe it was sent late Friday and they want to login on Monday) - this thing will dutifully kill their access at Friday night without giving them any chance to login. You need some "grace period". Maybe something like WHERE isActive = true AND (LastLoginDate < :x OR (LastLoginDate = null AND CreatedDate < :x))
start()
Queries in strings work and that's how a lot of batch documentation is written but they are poor practice. Where possible use a compiled query, in brackets. You get minimal improvement in execution (precompiled), you get compile-time warnings when you mess up (better than a runtime error which you might not notice if you don't monitor jobs). And most importantly - if somebody wants to delete a field - SF will detect a dependency and stop him/her. Use return Database.getQueryLocator([SELECT ...]); wherever you can.
execute()
Your scope already is a list of users, why do you do extra casts to User? Why do you add them to a helper list? Why 2 loops?
for(User u : scope){
u.isActive = false;
}
update users;
and you're done?
P.S. Why "global" all over the place?
I am attempting to create a DocuSign envelope from a template document using the CreateEnvelopeFromTemplates method, available within their v3 SOAP API web service. This is being instantiated from a asp.NET v4.0 web site.
Upon calling the method armed with the required parameter objects being passed in. I am recieving an exception from the web service, basically telling me that the Template ID is not a valid GUID.
669393: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
Line 14889:
Line 14890: public DocuSignDSAPI.EnvelopeStatus CreateEnvelopeFromTemplates(DocuSignDSAPI.TemplateReference[] TemplateReferences, DocuSignDSAPI.Recipient[] Recipients, DocuSignDSAPI.EnvelopeInformation EnvelopeInformation, bool ActivateEnvelope) {
Line 14891: return base.Channel.CreateEnvelopeFromTemplates(TemplateReferences, Recipients, EnvelopeInformation, ActivateEnvelope);
Line 14892: }
Line 14893:
The template reference, a guid. Must be specified as the "Template" string property against TemplateReference object. This is then added to a dynamic array of TemplateReferences, which is one of the input parameters of the CreateEnvelopeFromTemplates method.
Actual template GUID: f37b4d64-54e3-4723-a6f1-a4120f0e9695
I am building up my template reference object using the following function that i wrote to try and make the functionality reusable:
Private Function GetTemplateReference(ByVal TemplateID As String) As TemplateReference
Dim templateReference As New TemplateReference
Dim guidTemplateID As Guid
With TemplateReference
.TemplateLocation = TemplateLocationCode.Server
If Guid.TryParse(TemplateID, guidTemplateID) Then
.Template = guidTemplateID.ToString
End If
End With
Return TemplateReference
End Function
The TemplateID is being passed in from a appSetting configuration value at the time of the TemplateReferences array instantiation like so...
templateReferences = New TemplateReference() {GetTemplateReference(ConfigurationManager.AppSettings("DocuSignTemplate_Reference"))}
recipients = New Recipient() {AddRecipient("myself#work.email", "My Name")}
envelopeInformation = CreateEnvelopeInformation()
envelopeStatus = client.CreateEnvelopeFromTemplates(templateReferences, recipients, envelopeInformation, True)
As you can see from my GetTemplateReference function I am also parsing the GUID before setting it back as a string so i know its valid. The template is managed and stored at the DocuSign end, hence specifying the document location.
I am referring to their own documentation:
CreateEnvelopeFromTemplates
Why oh why is the method not liking my Template ID? I can successfully use their REST API to call the same method, using their own code samples. Worst case I can make use of this but would rather interact with the web service as I would need to construct all the relevent requests in either XML or JSON.
I would really appreciate if someone could perhaps shed some light on this problem.
Thanks for taking the time to read my question!
Andrew might be spot on with the AccountId mention - are you setting the AccountId in the envelope information object? Also, have you seen the DocuSign SOAP SDK up on Github? That has 5 sample SOAP projects including one MS.NET project. The .NET project is in C# not Visual Basic, but still I think it will be helpful to you. Check out the SOAP SDK here:
https://github.com/docusign/DocuSign-eSignature-SDK
For instance, here is the test function for the CreateEnvelopeFromTemplates() function:
public void CreateEnvelopeFromTemplatesTest()
{
// Construct all the recipient information
DocuSignWeb.Recipient[] recipients = HeartbeatTests.CreateOneSigner();
DocuSignWeb.TemplateReferenceRoleAssignment[] finalRoleAssignments = new DocuSignWeb.TemplateReferenceRoleAssignment[1];
finalRoleAssignments[0] = new DocuSignWeb.TemplateReferenceRoleAssignment();
finalRoleAssignments[0].RoleName = recipients[0].RoleName;
finalRoleAssignments[0].RecipientID = recipients[0].ID;
// Use a server-side template -- you could make more than one of these
DocuSignWeb.TemplateReference templateReference = new DocuSignWeb.TemplateReference();
templateReference.TemplateLocation = DocuSignWeb.TemplateLocationCode.Server;
// TODO: replace with template ID from your account
templateReference.Template = "server template ID";
templateReference.RoleAssignments = finalRoleAssignments;
// Construct the envelope information
DocuSignWeb.EnvelopeInformation envelopeInfo = new DocuSignWeb.EnvelopeInformation();
envelopeInfo.AccountId = _accountId;
envelopeInfo.Subject = "create envelope from templates test";
envelopeInfo.EmailBlurb = "testing docusign creation services";
// Create draft with all the template information
DocuSignWeb.EnvelopeStatus status = _apiClient.CreateEnvelopeFromTemplates(new DocuSignWeb.TemplateReference[] { templateReference },
recipients, envelopeInfo, false);
// Confirm that the envelope has been assigned an ID
Assert.IsNotNullOrEmpty(status.EnvelopeID);
Console.WriteLine("Status for envelope {0} is {1}", status.EnvelopeID, status.Status);
}
This code calls other sample functions in the SDK which I have not included, but hopefully this helps shed some light on what you're doing wrong...
This problem arises when you don't set up the field AccountId. This field can be retrieved from your account. In Docusign's console go to Preferences / API and look here
Where to find AccountID Guid in Docusign's Console
Use API Account ID (which is in GUID format) and you should be OK.
I have a requirement that a certain email distribution list should be notified every so often (still to be determined) about user accounts that are nearing expiration.
I'm wondering the best way to achieve this, I know its generally a bad idea to spawn another thread within asp.net to handle this type of thing, so I'm thinking maybe a simple service is the way to go but for something so small this seems like it might be slightly overkill.
Ideally I'd like something that doesnt require much babysitting (eg. checking service is running).
I have also suggested having a page in the site with this type of information but it is likely that it could be a few days before this is checked. We also cannot let users extend their own expiration date.
Are there any other viable options.
The best suitable method to work on it according to is
create a application which will select list of all users whose account expiry date is nearby (eg. 10 days from today) as per your requirement.
This application will be scheduled as an daily execution (you will create an exe with log file to display errors raised and total number of emails sent in one execution.)
This application will fetch all the records based on criteria and send the emails to all yours using the basic HTML template. and once the email is sent, you will update a column (notificationFlag) in your database as 1 if you have sent is once in last 10 days. else by default it will be 0
you can schedule the exe by the end of the day at 12:10 am (just incase your database server and webserver is not matching in time) every day. .
This is something I've done which is similar to Prescott's comment on your answer.
I have a website with an administrative page that reports on a bunch of expiration dates.
This page also accepts a QueryString parameter SEND_EMAILS, so anytime an administrative user of the site passes the QueryString parameter SEND_EMAILS=true a bunch of emails go out to all the users that are expiring.
Then I just added a windows scheduled task to run daily and load the page with the SEND_EMAILS=true parameter.
This was the simple code I used to issue the webrequest from the console in the scheduled task:
namespace CmdLoadWebsite
{
class Program
{
static void Main(string[] args)
{
string url = "http://default/site/";
if (args.Length > 0)
{
url = args[0];
}
Console.WriteLine(GetWebResult(url));
}
public static string GetWebResult(string url)
{
byte[] buff = new byte[8192];
StringBuilder sb = new StringBuilder();
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
Stream webStream = response.GetResponseStream();
int count = 0;
string webString;
do
{
count = webStream.Read(buff, 0, buff.Length);
if (count != 0)
{
webString = Encoding.ASCII.GetString(buff, 0, count);
sb.Append(webString);
}
}
while (count > 0);
return(sb.ToString());
}
}
}
I've got an ASP.Net application, which starts throwing NUllReferenceExceptions, after a long period of running. The code in question is used early on in each individual session, where we're trying to establish some kind of referrer information. The thing is, I can't fathom out what can throw this exception.
The method in question (topmost in the stack trace) is:
Private Function ResolveReferrer(ByVal wrRequest As HttpRequest) As Referral
'1) If we don't find a domain, try and get a match on any query strings
If wrRequest.QueryString.Count > 0 Then
For Each item As Referral In Me
For Each sKey As String In wrRequest.QueryString.Keys
If Not sKey Is Nothing AndAlso item.Names.Contains(sKey.ToLower) Then
Return item
End If
Next sKey
Next item
End If
Dim strSubDomain As String = Utility.RequestSubDomain(wrRequest.Url)
'2) If we don't find one on the domain, see if we can find the domain in query string
If Not wrRequest.QueryString.Item("domain") Is Nothing Then
strSubDomain = wrRequest.QueryString.Item("domain")
strSubDomain = HttpUtility.UrlDecode(strSubDomain)
' OK found a "domain" query string, so make up a referrer object to return
' ... just use the domain we've found for all the parameters
Dim oRef As New Referral(strSubDomain, strSubDomain, strSubDomain)
Return oref
End If
'3) If no query string of "domain", then see if the referring field is presented by the browser
If Not wrRequest.UrlReferrer Is Nothing Then
Dim sURL As String = wrRequest.UrlReferrer.ToString
strSubDomain = Utility.RequestSubDomain(wrRequest.UrlReferrer)
Dim oRef As New Referral(sURL, sURL, strSubDomain)
Return oRef
End If
'4) See if we can find the domain defined in the web.config
For Each item As Referral In Me
' See if we can find a referrer from the domain name
If String.Compare(strSubDomain, item.FromDomain, False) = 0 Then
Return item
End If
Next item
'5) If we still can't find one, make one up with a value of "Unknown"
Return New Referral("Unknown", "Unknown", "Unknown", "Unknown")
End Function
The class that this is a part of inherits from ArrayList. I've checked, and the only things added to this ArrayList are instances of the Referral class (which has multiple constructors, all simple).
What we do know is that, we can have a request that comes in with no referrer information, and it causes the exception to be thrown. At the same time, if a request with a referrer comes in, it works fine. In neither case is anything passed in the query string (so I think you can skip down to the '3 comment.
So, my question is, what in this method can cause a NullReferenceException to be thrown? If you need additional snippets added, or class definitions, just shout.
Utility.RequestSubDomain has reasonable complexity, so I doubt that it's being inlined and removed from the stack trace. And it's top is:
Public Shared Function RequestSubDomain(ByVal uri As System.Uri) As String
If uri Is Nothing Then
Return ""
End If
Any help, or suggestions for finding more information would be appreciated. It's obviously (as with so many problems) only happening in production, so I don't want to switch on debugging.
I took a good hard look and the only two things that come to mind that seem like the most likely are:
wrRequest could be null.
The ArrayList could have null values in it resulting in an enumerator that returns null values.
If it were me I would first focus my attention on this section. Is there any way possible that the Me.GetEnumerator will return an enumerator with one of the items having a null value?
For Each item As Referral In Me
item.Names ' Can item be null here causing the exception on the getter of Names?
Next item
As it turned out, there was a NULL in the arraylist - it turns out that, in certain circumstances, multiple threads were working on the same object (which inherits from arraylist) and calling Add(). So the null was appearing because the internal index was being incremented twice by different threads.
I'm integrating a number of e-comm sites into different banks and decided the easiest method was to add in the dotnetcharge (www.dotnetcharge.com) library. It works well and means I can keep much of my code the same for each bank type and transaction. However, their support is a bit sucky (4 emails sent, 1 reply) and I'm utterly baffled on the 3D Secure issue.
Does anyone have experience with dotnetcharge and 3D Secure? I have set the MerchantURL and the actual 3D Secure screen comes up - but I'm unsure how to get the system to 'flow' properly. Does anyone have any code examples or even pointers in the right direction? Failing that, does anyone know how to make support respond!
This particular integration is with SagePay, which also has God-awful documentation and support.
Code for reference is as follows;
Dim Amount As Decimal = ordertotal
' ApplySecure3D options:
' 0 = If 3D-Secure checks are possible and rules allow, perform the checks and apply the authorization rules.
' 1 = Force 3D-Secure checks for this transaction only (if your account is 3D-enabled) and apply rules for authorization.
' 2 = Do not perform 3D-Secure checks for this transaction only and always authorize.
' 3 = Force 3D-Secure checks for this transaction (if your account is 3D-enabled) but ALWAYS obtain an auth code, irrespective of rule base.
Dim ProtxLogin As String = "xxx"
Dim ProtxPassword As String = "xxx"
Dim ProtxApply3DSecure As Integer = 1
Dim ProtxMerchantURL As String = "https://www.mydomain.com/processing/"
Dim Number As String = txtCardNo.Text '//luhn/mod10 here.
Dim AVS As String = txtCVN.Text
Dim DD As String = "01"
Dim MM As String = ddlValidTo_month.SelectedValue.ToString()
Dim YY As String = ddlValidTo_year.SelectedValue.ToString()
Dim ProcessingResult As Integer = 0
Dim Protx As New dotnetCHARGE.CC()
Protx.Login = ProtxLogin
Protx.Password = ProtxPassword
Protx.ApplySecure3D = ProtxApply3DSecure
Protx.MerchantUrl = ProtxMerchantURL
Dim AVSResponse As String = ""
Dim CVV2 As String = ""
Protx.OrderID = GoogleOrderNumber
Protx.Month = MM
Protx.Year = YY
Protx.TransactionType = dotnetCHARGE.TransactionType.Sale
Protx.Amount = ordertotal
Protx.Number = Number
Protx.Currency = "GBP"
Protx.CustomerID = CustomerId
'//loads of params removed for brevity
Protx.ClientIP = Request.UserHostAddress.ToString()
Protx.CardType = ddlCardType.SelectedValue.ToString()
Protx.Description = "My Order"
Protx.Code = AVS
Protx.TestMode = True
Protx.TransactionType = dotnetCHARGE.TransactionType.Sale
ProcessingResult = Protx.Charge(Processor.Protx)
Help appreciated.
I decided to return to this question to explain how the final result was acheived. Hopefully some SO users will find it useful.
To acheive the correct 'flow' you'll need two pages. You won't realistically be able to do the whole transaction processing in a single page. The first page will have the card entry details; card number, expiry date, CVN, billing address etc. On hitting pay/submit I'd recommend saving the transaction to your datasource as 'unprocessed' or something similiar. Once all your details are saved - no card processing done thus far - redirect with HTTPS to the second page.
Your customer may never know this page exist depending on how you set this up. The second page will have the .netCharge code within it as my question and process the card. When 3D secure is enabled (.Apply3DSecure = 1), the customer will be redirected to their bank to enter some further details and it will return to this second page. It doesn't behave like a postback or a refresh, so do not worry about a returning call to the page processing twice. You will receive 1 of 3 possible statuses; Authorised, Error and Declined. Your page can redirect to further necessary pages (therefore the customer nevers know this middle page exists) or display the results directly on this processing page.
There is one final 'gotcha', which you'll see very quickly. The second page (the processing page) needs the card details to actually process. You can't just pass card details on a form or even a querystring, that's irresponsible. .netCharge comes with an .Encrypt and .Decrypt function; just pass it the value to encrypt and a hash of some sort and save these details temporarily on the first page, read and decrypt on the second page and then remove them. This means the details are secure, are saved for less than 5 seconds in most cases and you have no exposure because they are destroyed.
I hope this helps - if anyone has any questions, just give me a shout.