salesforce RemoteAction and Chrome developer tools -> Network log exposing data - salesforce

I created a VF page and RemoteAction there is no system.debug or console.log anywhere in apex class or vf page respectively, as a result of which in Chrome developer tools -> console log there is not output but surprisingly in Network tab if you select apexremote you get to see the data returned from the query even the encrypted fields in the object.
If you see the code below, credit_card__c field is encrypted, and I am not even exposing it in my VF, still in chrome developer tools network tab I see the entire data.
How can I stop any log in my network tab of chrome developer tools ?
global class AccountRemoteService {
public String accountName { get; set; }
public static Account account { get; set; }
global AccountRemoteService(){}
#RemoteAction
global static Account getAccount(String accountName)
{
account = [SELECT id, name, credit_card__c FROM Account WHERE name = :accountName ];
return account;
}
}
VF Page
<apex:page controller="AccountRemoteService">
<script type="text/javascript">
function getRemoteAccount()
{
//get the values of input text and place into the variable.
var paramAccountName = document.getElementById('accName').value;
AccountRemoteService.getAccount( paramAccountName,
function(result, event)
{
alert('event.status==>'+event.status);
alert('event.type === '+event.type);
alert('event.message ==>'+event.message);
if (event.status)
{
// demonstrates how to get ID for HTML and Visualforce tags
document.getElementById("{!$Component.theBlock.thePageBlockSection.accountId.Id}").innerHTML = result.Id;
document.getElementById("{!$Component.theBlock.thePageBlockSection.accountName.Nam}").innerHTML = result.Name;
}
else if (event.type === 'exception')
{
document.getElementById("errors-js").innerHTML = event.message;
} else
{
document.getElementById("errors-js").innerHTML = 'No Records Found..';
}
}, {escape:true});
}
</script>
Account Name :<input id="accName" type="text" />
<button onclick="getRemoteAccount()">Get Account</button>
<div id="errors-js"> </div>
<apex:pageBlock id="theBlock">
<apex:pageBlockSection id="thePageBlockSection" columns="2">
<apex:pageBlockSectionItem id="accountId">
<apex:outputText id="Id"/>
</apex:pageBlockSectionItem>
<apex:pageBlockSectionItem id="accountName" >
<apex:outputText id="Nam" />
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
</apex:pageBlock>

There are a lot of things here that you are doing that should probably be done a different way, that I would just mention as an aside.
You are querying account by name not by a unique value which can lead to possibly more than one account in the result or no results and you are returning to a single Account record. In either of those cases an exception would be thrown. Use a List instead.
If you are going to use remote action you should perform an access to ensure the user has access to the field you are returning so as not to expose data unintentionally
I would recommend that you use the platform safeguards in visualforce to enforce field encryption. Use the to reference the object from your controller, you already have the account accessible to the visualforce page it's public with get; set;
In short there are certain situation where encrypted fields are not masked, I would recommend using the platform feature that helps make this easier. See here for more details.
You can achieve the same or a similar effect using actionRegion commandButtons and partial page re-rendering.

Related

Merge fields won't show on visualforce page

I just started learning Apex recently, and there's still a lot of topics that are hard for me to navigate at this time. I've searched everywhere for a solution that works, but I still haven't been able to figure it out.
I've created a button on my Salesforce org that renders a PDF from a visualforce page, and attaches it to the record as a File. This is to be used with Docusign later on to capture signatures for contracts. The problem is that, when using merge fields in the VF page, they either do not show at all, or I get this exception: "sObject row was retrieved via SOQL without querying the requested field".
Now, the exception explicitly says that I need to query the fields, and this is what I've found I need to do to make this work, but I have not been able to figure out how to do this properly. I've tried running a query in several places in my controller extension to no avail (I am using a standardController that SF created for my custom object).
Here's my extension's code:
public class attachPDFToQuote {
public final i360__Quote__c q {get; set;} //Quote object
//constructor
public attachPDFToQuote (ApexPages.StandardController stdController) {
q = (i360__Quote__c)stdController.getRecord();
/* for(i360__Quote__c query:[SELECT Id, Correspondence_Name__c, Name FROM i360__Quote__c WHERE Id=: q.Id]){
System.debug(i360__Quote__c.Correspondence_Name__c);
}*/
}
public PageReference attachPDF() {
/* for(i360__Quote__c query:[SELECT Id, Correspondence_Name__c, Name FROM i360__Quote__c WHERE Id=: q.Id]){
System.debug(i360__Quote__c.Correspondence_Name__c);
}*/
//generate and attach the PDF document
PageReference pdfPage = Page.ProjectAgreement;
Blob pdfBlob; //create a blob for the PDF content
if (!Test.isRunningTest()) { //if we are not in testing context
pdfBlob = pdfPage.getContent(); //generate the pdf blob
} else { //otherwise, we are in testing context. Create the blob manually.
pdfBlob = Blob.valueOf('PDF');
}
ContentVersion cvAttach = new ContentVersion(ContentLocation= 'S');
cvAttach.PathOnClient= 'Project Agreement.pdf';
cvAttach.Title= 'Project Agreement';
cvAttach.VersionData= pdfBlob;
insert cvAttach;
Id conDoc = [SELECT ContentDocumentID FROM ContentVersion WHERE Id=: cvAttach.Id].ContentDocumentId;
ContentDocumentLink ConDocLink = new COntentDocumentLink();
conDocLink.LinkedEntityId= q.Id;
conDocLink.ContentDocumentId= conDoc;
conDocLink.ShareType= 'V';
insert conDocLink;
//redirect the user
PageReference pageWhereWeWantToGo = new ApexPages.StandardController(q).view(); //redirect the User back to the Quote detail page
pageWhereWeWantToGo.setRedirect(true); //indicate that the redirect should be performed on the client side
return pageWhereWeWantToGo; //send the User on their way
}
}
I kept the commented code where I try to query the object fields so they show in VF. I also tried a couple of different ways, but nothing seems to work. Please let me know if I need to add anything else.
Thank you!
You didn't post your Visualforce page's code.
Even if it's same page (if your apex class is used in ProjectAgreement VF as <apex:page standardController="i360__Quote__c" extensions="attachPDFToQuote" - the act of grabbing a PDF version of the page counts as callout, a separate http traffic to fresh instance of the page so to speak.
So I suspect you need something like
PageReference pdfPage = Page.ProjectAgreement;
pdfPage.getParameters().put('id', q.Id);
Blob = pdfPage.getContent();
If that works... next step would be to look at your VF code.
If the page has merge fields such as {!i360__Quote__c.Name}, {!i360__Quote__c.Correspondence_Name__c} then magic should happen. Salesforce should figure out which fields are needed by looking at your VF page and silently query them for you. So you wouldn't even need the query in your constructor, you could just save stdController.getId() to class variable and then use that id in pdfPage.getParameters().set(...)
But if your VF page has references to {!quote.Correspondence_Name__c} then you need to keep the explicit query in there.

How to Create New Case in Salesforce from third-party web application using API

We have a web application that users log into and consume our products. From this application, we'd like to have a form that users can submit to create cases in our Salesforce instance. I'm looking for a REST API endpoint that I can POST the new case information to, which will then create a new case record in Salesforce. I'm a little confused on the right way to approach this based on the Salesforce docs (Apex, Lightning Platform, Force.com, etc.). Has anyone implemented this or can share the right approach?
Easiest way to would be to create a force.com site, which is essentially a visualforce page. Your page can then use a controller to read values and create Cases.
For e.g. this visualforce page updates a custom object record by using id passed in url:
<apex:page controller="MyService"></apex:page>
#RestResource(urlMapping='/myservice')
global class MyService {
#HttpGet
global static void doGet() {
RestContext.response.addHeader('Content-Type', 'text/plain');
String id = RestContext.request.params.get('id');
abc__c veh = [select name, abc__c from abc__c where id =:id];
if(veh!=null)
{
veh.abc__c = true;
try {
update veh;
} catch (DMLException e) {
RestContext.response.responseBody = Blob.valueOf('DML ERROR');
}
RestContext.response.responseBody = Blob.valueOf('OK');
}
else
RestContext.response.responseBody = Blob.valueOf('FAIL');
}
}

Making a call using a visualforce page using twilio integration

i am trying to develop visualforce page. i want to make a call. iwrite a code
public class SampleClass{
String account = 'XXXXXXXXXXXXXXXXXXX';
String token = 'YYYYYYYYYYYYYYYYYY';
public PageReference hello(){
TwilioRestClient client = new TwilioRestClient(account, token);
Map<String,String> params = new Map<String,String> {
'To' => '+919953938584',
'From' => '+919910728457',
'Url' => 'http://twimlets.com/holdmusic?Bucket=com.twilio.music.ambient'
};
TwilioCall call = client.getAccount().getCalls().create(params);
return null ;
}
}
and visualforce page
<apex:page controller="SampleClass">
<apex:form >
<apex:commandButton action="{!hello}" value="Make a Call"/>
</apex:form>
</apex:page>
both the numbers are verified so call is going and a music is playing.now i want that when the user click make a call button then he can talk to the dialled phone number.means his voice went to the number which is dialled and he can hear that voice.in other way that user can dial a phone number and talk to a person using only browser .is this possible .Please guideline.
I think you want to have one leg of you call from the Browser? So Browser to Phone handset? If this is the case you can use Twilio Client.
There are some quick starts on the Twilio site that should get you started.
Hope this helps!

SalesForce Email attachemnt

Using the send email feature of SalesForce it allows file attachment. I am looking a way to store this attachment in the salesforce to the object from which the email is sent.
I know this limitation form salesforce
"Attachments aren't stored on emails sent from Salesforce. To be saved with the email, attachments must be either associated with the email later or sent to Salesforce using using Email-to-Case, Email-to-Salesforce, On-Demand Email-to-Case, orSalesforce for Outlook."
is there any work around?
One option can be to use Apex code for uploading the same in Attachment object of Object and then sending e-mail.
//Code for attaching a file from Local Machine
//VF Page
<apex:page controller="AttachmentUploadController">
<apex:sectionHeader title="Visualforce Example" subtitle="Attachment Upload Example"/>
<apex:form enctype="multipart/form-data">
<apex:pageMessages />
<apex:pageBlock title="Upload a Attachment">
<apex:pageBlockSection showHeader="false" columns="2" id="block1">
<apex:pageBlockSectionItem >
<apex:outputLabel value="File" for="file"/>
<apex:inputFile value="{!attachment.body}" filename="{!attachment.name}" id="file"/>
</apex:pageBlockSectionItem>
<apex:pageBlockSectionItem >
<apex:commandButton action="{!upload}" value="Upload and send an Email"/>
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>
//Controller
public with sharing class AttachmentUploadController
{
public Attachment attachment
{
get
{
if (attachment == null)
attachment = new Attachment();
return attachment;
}
set;
}
public PageReference upload()
{
String parentId = System.currentPagereference().getParameters().get('pid');
attachment.OwnerId = UserInfo.getUserId();
attachment.ParentId = parentId;
attachment.IsPrivate = true;
try
{
insert attachment;
//Start: Send Email with Attachment
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[]{<<email IDs>>};
mail.setToAddresses(toAddresses);
//Set email file attachments
List<Messaging.Emailfileattachment> fileAttachments = new List<Messaging.Emailfileattachment>();
// Add to attachment file list
Messaging.Emailfileattachment efa = new Messaging.Emailfileattachment();
efa.setFileName(attachment.Name);
efa.setBody(attachment.Body);
fileAttachments.add(efa);
//create attachment for object
Attachment att = new Attachment(name = attachment.name, body = attachment.body, parentid = Trigger.new[0].id);
insertAttList.add(att);
mail.setFileAttachments(fileAttachments);
//Send email
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
//END : Send Email with Attachment
PageReference page = new PageReference('/' + parentId);
return page;
}
catch (DMLException e)
{
ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,system.Label.Error_uploading_attachment));
return null;
}
finally
{
attachment = new Attachment();
}
}
}
Points to note :
I have only hardcoded ToAddress in apex code but you can add the same thing in VF page. Infact you can give UI experience same as Send an Email.
I have not tried or executed the code given above; please refer this as a reference to get an idea about the Algo/flow.
Have you looked into Salesforce For Outlook? It is an add-on for Outlook where it syncs with Salesforce for Tasks, Meetings, Events, ECT. It also allows you to save the email and attachments to an Account in Salesforce with 1 click of a button. I'm the sole Admin for over 700 users and the whole field uses this and loves it.
In order to store any kind of email attachments in Salesforce, Salesforce org needs to be connected with some outlook mailbox. And as mentioned the salesforce functionality like Email-to-Case/On-Demand email-to-case services helps to store all the attachments which are sent through email.
In this case, if an attachment is sent with the mail, the attachment should be available under the "Case Attachment" tab.
That quote you included is from a comment thread that's over a DECADE old.
You need to enable Enhanced Emails. Here is Salesforce's response from 4 years ago on what you need to do: https://ideas.salesforce.com/s/idea/a0B8W00000GdhVxUAJ/email-attachments-should-be-saved-to-salesforce?sfdcIFrameOrigin=null

How to handle security/authentication on a DNN-based web API

I am building a REST API for a DotNetNuke 6 website, making use of DNN's MVC-based Services Framework. However, I don't have any background in authentication, so I'm not even sure where to start.
Basically, we want our clients to be able to make GET requests for their portal's data, and we want some clients (but not all) to be able to POST simple updates to their user data.
I've been trying to search for information, but the trouble is I'm not sure what I'm searching for. DNN has different logins and roles, but I'm not sure if or how they factor in. I've heard of things like oAuth but my understanding of it is at the most basic level. I don't know if it's what I need or not and if or how it applies to DNN. Can anyone point me in the right direction?
UPDATE:
Based on the answer below about tying it with a module and further research, here is what I have done:
I created a module just for this service, and I added two special permissions for it: "APIGET" and "APIPOST." I assigned these to some test roles/test accounts in DNN. I wrote a custom authorize attribute that, given the module ID, checks if the current user has the necessary permission (either through roles or directly). As far as I can tell, tab ID is irrelevant in my case.
It appears to be working both with a web browser (based on the DNN account I'm logged into) and with a php script that sends an HTTP request with an account username/password.
The authorize attribute:
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Security;
using DotNetNuke.Security.Permissions;
using System.Web;
public class MyAuthorize : DotNetNuke.Web.Services.AuthorizeAttributeBase
{
public const string AuthModuleFriendlyName = "MyAuthModule";
public const string GETPermission = "APIGET";
public const string POSTPermission = "APIPOST";
public string Permission { get; set; }
protected override bool AuthorizeCore(HttpContextBase context)
{
ModuleController mc = new ModuleController();
ModuleInfo mi = mc.GetModuleByDefinition(PortalController.GetCurrentPortalSettings().PortalId, AuthModuleFriendlyName);
ModulePermissionCollection permCollection = mi.ModulePermissions;
return ModulePermissionController.HasModulePermission(permCollection, Permission);
}
}
The controller:
("mytest" is the endpoint for both GET and POST)
public class MyController : DnnController
{
[ActionName("mytest")]
[AcceptVerbs(HttpVerbs.Get)]
[DnnAuthorize(AllowAnonymous = true)]
[MyAuthorize(Permission = MyAuthorize.GETPermission)]
public string myget(string id = "")
{
return "You have my permission to GET";
}
[ActionName("mytest")]
[AcceptVerbs(HttpVerbs.Post)]
[DnnAuthorize(AllowAnonymous = true)]
[MyAuthorize(Permission = MyAuthorize.POSTPermission)]
public string mypost(string id = "")
{
return "You have my permission to POST";
}
}
The main way that you tie a service in the DNN Services Framework into DNN permissions is to associate the permissions with a module instance. That is, you'll require users of your service to identify which module they're calling from/about (by sending ModuleId and TabId in the request [headers, query-string, cookies, form]), then you can indicate what permissions they need on that module to take a particular action on the service.
You can use the SupportedModules attribute on your service, and pass in a comma-delimited list of module names, to ensure that only your own modules are being allowed. Then, add the DnnModuleAuthorize attribute at the service or individual action level to say what permission the user needs on that module. In your instance, you can also add the AllowAnonymous attribute on the GET actions, and have one DnnModuleAuthorize on the service, for the POST methods (and anything else). Note that you cannot put the AllowAnonymous attribute on the controller; it will override authorizations put at the action, making it impossible to make actions more restrictive.
You'll also want to add the ValidateAntiForgeryToken attribute on the POST actions, to protect against CSRF attacks.
If you don't have a module that naturally associates its permissions with your service, you can create one just for that purpose, solely to expose itself as a permissions management utility.
Once you've figured out the authorization piece above, DNN will take care of authentication using your forms cookie (i.e. AJAX scenarios are taken care of automatically), or via basic or digest authentication (for non-AJAX scenarios). That said, if you're doing non-AJAX, you'll need to figure out a way to validate the anti-forgery token only when it applies.
The Services Framework in DNN is what you are after. It allows you to provide a REST API that plugs directly into DNN security.
Here are some articles to help you get started:
http://www.dotnetnuke.com/Resources/Wiki/Page/Services-Framework-WebAPI.aspx
http://www.dotnetnuke.com/Resources/Blogs/EntryId/3327/Getting-Started-with-DotNetNuke-Services-Framework.aspx
Note, there are some difference in DNN 6 and DNN 7 when using the Services Framework:
http://www.dotnetnuke.com/Resources/Blogs/EntryId/3514/Converting-Services-Framework-MVC-to-WebAPI.aspx
Just wanted to note that the DnnModuleAuthorize attribute takes a PermissionKey parameter for custom permissions so you can do stuff like this:
[DnnModuleAuthorize(PermissionKey = "DELETEDATA")]
[HttpPost]
public HttpResponseMessage DeleteData(FormDataCollection data)
It doesn't look like you can supply your own error message with this so you might to wrap your method body like this instead and leave off the custom permission attribute:
[DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.View)]
[HttpPost]
public HttpResponseMessage DeleteData(FormDataCollection data)
{
var errorMessage = "Could not delete data";
if (ModulePermissionController.HasModulePermission(ActiveModule.ModulePermissions,"DELETEDATA"))
{
// do stuff here
}
else
{
errorMessage = "User does not have delete permission";
}
var error = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content =
new StringContent(
errorMessage)
};
return error;
}
Just wanted to add to #Richards comment for using the [DnnModuleAuthorize(PermissionKey = "DELETEDATA")] for custom permissions.
The full attribute should be:
[DnnModuleAuthorize(PermissionKey = "DELETEDATA", AccessLevel = SecurityAccessLevel.Edit)]
Leaving it blank does nothing as shown here: https://github.com/dnnsoftware/Dnn.Platform/blob/f4a5924c7cc8226cfe79bbc92357ec1a32165ada/DNN%20Platform/Library/Security/Permissions/PermissionProvider.cs#L810
I guess you require a plugin that allows you to construct GET and POST APIs. you can use this plugin I found on the DNN store. https://store.dnnsoftware.com/dnn-rest-api-custom-api-authentication-authorization.

Resources