I'm trying to write contacts into an ADLDS ldap for using them as a phone book for a Yealink T48G. Sometimes the name of the contact includes some special characters like "ö", "ß" and "é". If these characters are contained in the fields "givenName" or "displayName" neither the phone nor the ldap client can show them correctly and instead show some other chars (for example "ö" -> "ö"), however the "name" and "dn" fields show these characters correctly.
If I insert the contactvalues via ADSI-Edit or any other tool, the phone shows the name correctly, but my application is no longer able to read the inserted special chars from givenName and shows some questionmark-boxes, however the dn and name fields are read correctly.
I've already tried using utf-8, utf-16, utf-32, iso-8859-1 and windows-1252 as encoding for my application.
So the question is how can I store these special characters using C# in the givenName property for an inetOrgPerson in an ADLDS instance?
shown correctly:
shown incorrectly:
My code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.DirectoryServices.Protocols;
using System.Net;
namespace LdapContacts
{
public class LdapClient
{
private LdapConnection connection;
public LdapClient(string host, int port, string distinguishedUsername, string password)
{
connection = new LdapConnection(new LdapDirectoryIdentifier(host, port));
connection.AuthType = AuthType.Basic;
connection.Credential = new NetworkCredential(distinguishedUsername, password);
connection.Bind();
}
public AddResponse SendAddRequest(string distinguishedName, List<DirectoryAttribute> attributes)
{
AddRequest request = new AddRequest(distinguishedName, attributes.ToArray());
return connection.SendRequest(request) as AddResponse;
}
public SearchResponse SendSearchRequest(string distinguishedName, string filter)
{
SearchRequest request = new SearchRequest();
request.DistinguishedName = distinguishedName;
request.Filter = filter;
request.Scope = SearchScope.Subtree;
return connection.SendRequest(request) as SearchResponse;
}
}
public class ContactsToLdap
{
private static void Main(string[] args)
{
LdapClient client = new LdapClient(Settings.LdapHost, Settings.LdapPort, Settings.LdapUsername, Settings.LdapPassword);
client.SendAddRequest("CN=Testöäüß,CN=Users,CN=testpart,DC=csdomain,DC=local", new List<DirectoryAttribute>()
{
new DirectoryAttribute("telephoneNumber", "0123456"),
new DirectoryAttribute("objectClass", "inetOrgPerson"),
new DirectoryAttribute("uid", "io3e"),
new DirectoryAttribute("givenName", "â é testnameöüÄß")
});
//distinguished name of contactsfolder
SearchResponse result = client.SendSearchRequest(Settings.LdapContactsFolder, "(objectClass=inetOrgPerson)");
foreach (SearchResultEntry sResult in result.Entries)
{
//display the index of the current entry
Console.Write((result.Entries.IndexOf(sResult) + 1) + ":\n");
foreach (DirectoryAttribute attribute in sResult.Attributes.Values)
{
//output the name of the attribute
Console.Write("\t" + attribute.Name + " = ");
for (int i = 0; i < attribute.Count; i++)
{
// convert the attribute to a string if it is an byte[]
// output if inserted with ADSI-Edit: ? ? testname????
// output if inserted with this code: â é testnameöüÄß
if (attribute[i].GetType().Equals(typeof(byte[])))
{
Console.Write(Encoding.UTF8.GetString((byte[])attribute[i]) + "; ");
}
else
{
Console.Write(attribute[i] + "; ");
}
}
Console.WriteLine();
}
Console.WriteLine();
}
}
}
}
The issue was resolved by setting the protocolversion that should be used to version 3.
connection = new LdapConnection(new LdapDirectoryIdentifier(host, port));
connection.SessionOptions.ProtocolVersion = 3;
Related
I have an application that is written in C# so that it can be a stand-alone .exe with parameters passed in via cmd at runtime. I am trying to update a file in google drive without changing the fileid so that any links using that id will not be broken. The following code works until it gets to the third section using Google.Apis.Drive.v3.Data.File updateFile in which the file is always 'null'. The request.ResponseBody has the correct fileId, the correct path and all but always comes up null.
How can I get this to update a file?
Is there some code update/change that no longer works?
Any help would be appreciated-
Section 1- This is the start of the code that calls the next two sections:
// System Library Requirements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Security;
// Microsoft Library Requirements
using Microsoft.Win32;
// Google API Library Requirements
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using GoogleV2 = Google.Apis.Drive.v2;
using Google.Apis.Drive.v3.Data;
using GoogleV2data = Google.Apis.Drive.v2.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using Google.Apis.Requests;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Api.Gax;
using Google.Cloud.Channel.V1;
// JSON reading/serializing/De-serializing Library Requirements
using Newtonsoft.Json;
// Mime-Type Library Requirements
using MimeKit;
using Google.Apis.Upload;
// CyberArk Library Requirements
using CyberArk.AIM.NetPasswordSDK;
using CyberArk.AIM.NetPasswordSDK.Exceptions;
static class Globals
{
//cyberark values will change based on the domain and the app and the safe
public static string google_client_secret;
public static string cyberark_client_id = "...xxxx.....apps.googleusercontent.com";
public static string cyberark_object = "...xxx...";
public static string cyberark_app_id = "...xxx...";
public static string cyberark_safe = "...xxx...";
//google app values will change based on the domain and gcp project information e.g. np vs prod
public static string google_app_name = "my_google_app_name";
public static string google_client_id = "my_google_client_id";
public static string google_data_store = "my_google_data_store";
}
class Program
{
static void Main(string[] args)
{
Globals.google_client_secret = NetPasswordSDK.CyberArkProgram.GetMyPassword(NetPasswordSDK.MyCredentials.MY_APP_ID, NetPasswordSDK.MyCredentials.MY_SAFE, NetPasswordSDK.MyCredentials.MY_OBJECT, NetPasswordSDK.MyCredentials.MY_SECURE_STRING);
var paramFolderID = "";
var paramDriveFromFolderName = "";
var paramUserID = "";
var paramTeamID = "";
var paramFileName = "";
var paramFilePath = "";
var paramLocalFolderPath = "";
if (args == null || args.Length == 0)
{
Console.WriteLine("No Arguments passed");
}
else
{
paramFolderID = args[0];
paramDriveFromFolderName = args[1];
paramUserID = args[2];
paramTeamID = args[3];
paramFileName = args[4];
paramFilePath = args[5];
paramLocalFolderPath = args[6];
Console.WriteLine("FolderID " + args[0]);
Console.WriteLine("DriveFromFolderName " + args[1]);
Console.WriteLine("UserID " + args[2]);
Console.WriteLine("TeamID " + args[3]);
Console.WriteLine("FileName " + args[4]);
Console.WriteLine("FilePath " + args[5]);
Console.WriteLine("LocalFolderPath " + args[6]);
}
string googleUpdateText = "";
string googleUpdateFilePath = "";
string googleUpdateLocalFolderPath = "";
string googleUpdateFileName = "";
string googleUpdateParentFolder = "";
string googleUpdateTeamDriveID = "";
string googleUpdateParentFolderID = "";
var proxy = WebRequest.DefaultWebProxy;
proxy.Credentials = CredentialCache.DefaultCredentials;
var httpClientHandler = new HttpClientHandler()
{
Proxy = proxy
};
{
{
//googleUpdateParentFolderID = paramFolderID;
//googleUpdateParentFolder = paramDriveFromFolderName;
//googleUpdateTeamDriveID = paramTeamID;
//googleUpdateFileName = paramFileName;
//googleUpdateFilePath = paramFilePath;
//googleUpdateLocalFolderPath = paramLocalFolderPath;
// HC values for testing
googleUpdateFilePath = "C:\\Path\\to_update_file\\update_file.txt";
googleUpdateLocalFolderPath = "C:\\Path\\to_update_file\\";
googleUpdateFileName = "update_file.txt";
googleUpdateParentFolder = "Folder_on_Google_Drive";
googleUpdateTeamDriveID = "Team_Drive_ID";
googleUpdateParentFolderID = "Parent_Folder_ID";
Console.WriteLine("value of googleUploadFilePath: " + googleUpdateFilePath);
Console.WriteLine("value of googleUploadLocalFolderPath: " + googleUpdateLocalFolderPath);
Console.WriteLine("value of googleUploadFileName: " + googleUpdateFileName);
Console.WriteLine("value of googleUploadParentFolder: " + googleUpdateParentFolder);
Console.WriteLine("value of googleUploadTeamDriveID: " + googleUpdateTeamDriveID);
Console.WriteLine("value of googleUploadParentFolderID: " + googleUpdateParentFolderID);
googleUpdateText = (JsonConvert.SerializeObject(GoogleAPI.GoogleAPI_Drive.GDrive_Update_File(googleUpdateFilePath, googleUpdateLocalFolderPath, googleUpdateFileName, "no description", googleUpdateParentFolder, paramUserID, "", "", googleUpdateTeamDriveID, googleUpdateParentFolderID), Formatting.Indented));
Console.WriteLine("value of googleUpdateText: " + googleUpdateText);
Console.WriteLine("this is the end of the update text");
};
// end
}
// end of Main
}
// end of program class
}
Section 2- This is the middle part of the code that updates files in a list of files which calls each file to update it.
public static String GDrive_Update_File(string pathToFile, string localFilePath, string fileName, string description, string parentFolder = "", string userid = "", string sharedUserIds = "", string sharerole = "", string teamID = "", string parentFolderID = "")
{
FileResponseJSON fileJson = new FileResponseJSON();
List<string> fileArray = new List<string> { };
IList<FileResponseJSON> filesJSON = new List<FileResponseJSON>();
IList<Google.Apis.Drive.v3.Data.File> files = new List<Google.Apis.Drive.v3.Data.File>();
List<string> parents = new List<String> { };
List<string> kids = new List<String> { };
UserCredential credential = GoogleAPI_Functions.GetCredentials(userid);
var service = new DriveService(new BaseClientService.Initializer
{
ApplicationName = APP_NAME,
HttpClientInitializer = credential,
});
if (!String.IsNullOrEmpty(parentFolder))
{
FilesResource.ListRequest listRequest = service.Files.List();
listRequest.Q = String.Format("(name contains '{0}') and (mimeType = 'application/vnd.google-apps.folder')", parentFolder);
listRequest.Fields = "files(*)";
if (teamID.Length > 3)
{
listRequest.SupportsTeamDrives = true; // new
listRequest.Corpora = "teamDrive"; // new
listRequest.IncludeTeamDriveItems = true;//new
listRequest.TeamDriveId = teamID;//new
}
// List files.
IList<Google.Apis.Drive.v3.Data.File> folders = listRequest.Execute().Files;
foreach (var fldr in folders)
{
if (parentFolderID.Length > 3)
{
if (fldr.Id.Equals(parentFolderID))
{
parents.Add(fldr.Id);
}
}
else
{
parents.Add(fldr.Id);
}
Console.WriteLine("value of fldr.Id " + fldr.Id);
}
}
// Upload File MetaData
// mimeType is important and so it the 'trashed' true/false or it won't be found
FilesResource.ListRequest fileRequest = service.Files.List();
string query = "name contains 'update_file.txt' and (mimeType = 'text/plain') and trashed = false";
fileRequest.Q = String.Format(query);
fileRequest.Fields = "files(*)";
fileRequest.SupportsTeamDrives = true; // new
fileRequest.Corpora = "teamDrive"; // new
fileRequest.IncludeTeamDriveItems = true;//new
fileRequest.TeamDriveId = teamID;//new
IList<Google.Apis.Drive.v3.Data.File> myFiles = fileRequest.Execute().Files;
Console.WriteLine("Files:");
if (myFiles != null && myFiles.Count > 0)
{
foreach (var myf in myFiles)
{
myf.Name = localFilePath + myf.Name;
Console.WriteLine("{0} ({1})", myf.Name, myf.Id);
UpdateFile(service, myf.Name, myf.Id);
}
}
else
{
Console.WriteLine("No files found.");
}
Console.Read();
var UpdatedID = "";
return UpdatedID;
}
Section 3- This section is supposed to update each file --> THIS IS THE SECTION WITH THE PROBLEM (The Google.Apis.Drive.v3.Data.File updatedFile always returns null)
public static Google.Apis.Drive.v3.Data.File UpdateFile(DriveService _service, string _uploadFile, string _fileId)
{
Google.Apis.Drive.v3.Data.File file = new Google.Apis.Drive.v3.Data.File();
byte[] byteArray = System.IO.File.ReadAllBytes(_uploadFile);
System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);
FilesResource.UpdateMediaUpload request = _service.Files.Update(file, _fileId, stream, GetMimeType(_uploadFile));
void Upload_ProgressChanged(IUploadProgress progress) =>
Console.WriteLine(progress.Status + " " + progress.BytesSent);
void Upload_ResponseReceived(Google.Apis.Drive.v3.Data.File myfile) =>
Console.WriteLine(myfile.Name + " was uploaded successfully");
request.ProgressChanged += Upload_ProgressChanged;
request.ResponseReceived += Upload_ResponseReceived;
request.Upload();
Google.Apis.Drive.v3.Data.File updatedFile = request.ResponseBody;
return updatedFile;
}
update: I used IUploadProgress.Exception and got the following error-
Exception: The service drive has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
File not found: 1XYZ..... [404]
Errors [
Message[File not found: 1XYZ.......] Location[fileId - parameter] Reason[notFound] Domain[global]
]
at Google.Apis.Upload.ResumableUpload.<HandleResponse>d__78.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Google.Apis.Upload.ResumableUpload.<SendNextChunkAsync>d__77.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Google.Apis.Upload.ResumableUpload.<UploadCoreAsync>d__74.MoveNext() 0
Update again, I was able to see the uri that was being generated and copy/paste it into the browser, and when I did I got a 405 Method Not Allowed; additionally when I tried it without the https and just used http I got a 403 Forbidden and that SSL is required to perform this operation
This is the uri without the ids
https://www.googleapis.com/upload/drive/v3/files/[file_id]?uploadType=resumable&upload_id=[upload_id]
Update:
During the process the api produces these two urls that both work and allow me access
WebContentLink
"https://drive.google.com/uc?id=[docID]&export=download"
WebViewLink
"https://drive.google.com/file/d/[docID]/view?usp=drivesdk"
Latest update applied fixes from DalmTo but am getting new errors-
// - removed // Google.Apis.Drive.v3.Data.File file = new
// - added //
var fileMetadata = new Google.Apis.Drive.v3.Data.File()
{
Id = _fileId
};
var fsSource = new MemoryStream(Encoding.UTF8.GetBytes(_uploadFile ?? ""));
var request = _service.Files.Update(fileMetadata, fileMetadata.Id, fsSource, GetMimeType(_uploadFile));
New error:
Exception: The service drive has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
The resource body includes fields which are not directly writable. [403]
Errors [
Message[The resource body includes fields which are not directly writable.] Location[ - ] Reason[fieldNotWritable] Domain[global]
]
Not sure what would cause this.
File id is writeable so you should be able to send it with the file metadata in your request.
var fileMetadata = new Google.Apis.Drive.v3.Data.File()
{
Id = "xxx"
};
var fsSource = new MemoryStream(Encoding.UTF8.GetBytes(uploadString ?? ""));
var request = service.Files.Update(fileMetadata, fileMetadata.Id , fsSource, GetMimeType(uploadFilePath));
Update
From comments you are sending an empty metadata file object with your request.
Google.Apis.Drive.v3.Data.File file = new Google.Apis.Drive.v3.Data.File();
There for Drive is always going to give you a new file id. I am sending the original file id as part of the metadata there for it will use the file id.
I'm using SyslogNet library to send SIEM Syslog message from my app to 3rd party vendors SIEM system in LEEF format (in this it's QRadar).
However, I the message doesn't get sent and nowhere the procedure produce any exception.
I can see and confirm that the string correlates to the correct template/format according to Rfc5424 for LEEF messages (see example below the code).
The only indication that the message was not sent properly is a WireShark trace showing the that the message was consisted of only a few bytes and more importantly is considered a malformed packet:
The code is as follows:
using System.IO;
using System.Text;
using System.Reflection;
using SyslogNet.Client;
using SyslogNet.Client.Serialization;
using SyslogNet.Client.Transport;
using Severity = SyslogNet.Client.Severity;
namespace Providers.Syslog
{
public class SyslogLogger
{
private readonly SyslogSettings _syslogSettings;
private readonly SyslogUdpSender _syslogUdpSender;
public SyslogLogger(SyslogSettings syslogSettings)
{
_syslogSettings = syslogSettings;
_syslogUdpSender = new SyslogUdpSender(syslogSettings.SyslogServerName, syslogSettings.SyslogServerPort);
}
public void Log(string message)
{
var syslogMessage = new SyslogMessage(
null,
(Facility)_syslogSettings.Facility,
Severity.Informational,
null,
_syslogSettings.Identity,
message);
_syslogUdpSender.Send(syslogMessage, new SyslogMessageSerializer());
}
private class SyslogMessageSerializer : SyslogMessageSerializerBase, ISyslogMessageSerializer
{
private const int AppNameMaxLength = 32;
public void Serialize(SyslogMessage message, Stream stream)
{
var priorityValue = CalculatePriorityValue(message.Facility, message.Severity);
string timestamp = null;
if (message.DateTimeOffset.HasValue)
{
var dt = message.DateTimeOffset.Value;
var day = dt.Day < 10 ? " " + dt.Day : dt.Day.ToString();
timestamp = string.Concat(dt.ToString("MMM "), day, dt.ToString(" HH:mm:ss"));
}
var headerBuilder = new StringBuilder();
headerBuilder.Append("<").Append(priorityValue).Append(">");
if (!string.IsNullOrEmpty(timestamp))
{
headerBuilder.Append(timestamp).Append(" ");
}
if (!string.IsNullOrEmpty(message.HostName))
{
headerBuilder.Append(message.HostName).Append(" ");
}
if (!message.Message.IsSiemOrientadTemplate())
{
var appName = message.AppName;
headerBuilder.Append(string.IsNullOrWhiteSpace(appName)
? appName
: (appName.Length > AppNameMaxLength ? appName.Substring(0, AppNameMaxLength) : appName) + ": ");
headerBuilder.Append(message.Message ?? "No Content");
}
var encoding = new UTF8Encoding(false);
var bytes = encoding.GetBytes(headerBuilder.ToString());
stream.Write(bytes, 0, bytes.Length);
}
}
}
}
e.g. of a message (I removed actual data and put on placeholders):
"LEEF:2.0|CompanyNameString|ProduceNameString|VersionString|int|^|
^cat=Test - QRADAR^sev=6^DescriptionString=^Timestamp=Apr 03 2017 14:48:02^userNameString=domain\DisplayName^accountName=samAccountName^proto=Syslog"
Any help will be much appreciated as it's been a while and I couldn't find a solution. Thanks in advance!
Finally found the issue.
I overlooked the fact that the content was in an "if" clause for non-SIEM systems.
Moving this line out of the clause resolve the issue for all syslog provider types.
headerBuilder.Append(message.Message ?? "No Content");
I am using the below code to create a instance in the hp cloud(or any openstack). I am having issues with determining the base url.There could be other errors and i would appreciate anybody seeing them also. So do i find out the base url. I had a look through the hp docs but to no avail ?! Also i am unsure of how to obtain the image id, i presume the flavor is 'small' etc?
using System;
using System.Web;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using net.openstack.Core.Domain;
using net.openstack.Providers.Rackspace;
namespace Openstack2
{
class Program
{
static void Main(string[] args)
{
Uri baseUrl = new Uri("https://horizon.hpcloud.com/auth/login/");
CloudIdentity cloudId = new CloudIdentity()
{
Username = "#####",
Password = "####"
};
CloudIdentityProvider cip = new CloudIdentityProvider(cloudId, baseUrl);
UserAccess ua = cip.Authenticate(cloudId);
CloudServersProvider provider = new CloudServersProvider(cloudId);
Metadata metaData = new Metadata(); // Add some metadata just because we can
metaData.Add("Description", "Example 4 - Getting Started");
string serverName = "Server a14";
string imageId = "###";
string flavorId = "standard.xsmall";
NewServer newServer = provider.CreateServer(serverName, imageId, flavorId,DiskConfiguration.Manual, metaData);
}
}
}
The above code is based on the rackspace sdk to connect to the hp cloud, so that could be an issue. But i also used the following code based on the other .net openstack api.:
var identityUrl = "https://horizon.hpcloud.com/auth/login/";
var imageUrl = "http://server:9292";
var username = "####";
var password = "###";
var cloudId = new CloudIdentity() { Username = username, Password = password };
var cloudIdProvider = new CloudIdentityProvider(new Uri(identityUrl));
cloudIdProvider.Authenticate(cloudId);
var cloudServersProvider = new CloudServersProvider(cloudId, cloudIdProvider);
var newServer = cloudServersProvider.CreateServer("Team 101 Server a14", "Team 101 Server a14", "standard.xsmall");
Still will not connect to my hp openstack. I think i will half to ditch c# and maybe go with powershell or nova.
i am using HP Cloud and this is how i get the baseUrl:
...
using HPCloud.Common;
using HPCloud.Objects;
using HPCloud.Objects.Utility;
using HPCloud.Objects.DataAccess;
using HPCloud.Objects.Domain;
using HPCloud.Objects.Domain.Compute;
using HPCloud.Objects.Domain.Admin;
session = Session.CreateSession("accessKey", "secretKey", "tennantID");
private Session session = null;
public static string GenerateUrl(Session session, string bucket_name, string key)
{
string baseUrl = session.Context.ServiceCatalog.GetService(HPCloud.Objects.Domain.Admin.Services.ObjectStorage).Url;
return baseUrl + "/" + bucket_name + "/" + key;
}
You will need to get accesskey, secretkey, and tennantID from your cloud admin page.
You may need to add HPCloud-API and BouncyCastle from Nuget.
so now you can use the following to drop a file in your cloud bucket:
public static bool PutFile(Session session, string bucket_name, string file_path, out string key)
{
if (!File.Exists(file_path))
{
throw new FileNotFoundException(file_path);
}
bool success = false;
key = System.IO.Path.GetFileName(file_path);
try
{
var soRepo = session.Factory.CreateStorageObjectRepository();
string fullUrl = GenerateUrl(session, bucket_name, key);
soRepo.Copy(file_path, fullUrl, false);
success = true;
}
catch
{
success = false;
key = string.Empty;
}
return success;
}
Here's a screenshot of the field I'm trying to update:
How do I update the URL field?
WebLink type fields consist of two parts: LinkID and DisplayString. In order to set a LinkID (which corresponds to the variable ${id} in your screenshot, a DisplayString needs also to be set to an empty string.
Here is a full code example that uses Rally .NET REST toolkit:
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Rally.RestApi;
using Rally.RestApi.Response;
namespace aRestApp_CollectionOfTasks
{
class Program
{
static void Main(string[] args)
{
//Initialize the REST API
RallyRestApi restApi;
restApi = new RallyRestApi("user#co.com", "secret", "https://rally1.rallydev.com", "v2.0");
//Set our Workspace and Project scopings
String workspaceRef = "/workspace/1111"; //please replace this OID with an OID of your workspace
String projectRef = "/project/2222"; //please replace this OID with an OID of your project
bool projectScopingUp = false;
bool projectScopingDown = true;
Request storyRequest = new Request("Defect");
storyRequest.Workspace = workspaceRef;
storyRequest.Project = projectRef;
storyRequest.ProjectScopeUp = projectScopingUp;
storyRequest.ProjectScopeDown = projectScopingDown;
storyRequest.Fetch = new List<string>()
{
"Name",
"_ref",
"c_JiraLink"
};
storyRequest.Query = new Query("FormattedID", Query.Operator.Equals, "DE170");
QueryResult queryStoryResults = restApi.Query(storyRequest);
foreach (var s in queryStoryResults.Results)
{
Console.WriteLine(" Name: " + s["Name"] + " JiraLink's DisplayString: " + s["c_JiraLink"]["DisplayString"] + " JiraLink's LinkID: " + s["c_JiraLink"]["LinkID"]);
DynamicJsonObject toUpdate = new DynamicJsonObject();
DynamicJsonObject myLink = new DynamicJsonObject();
myLink["LinkID"] = "NM-3";
myLink["DisplayString"] = "";
toUpdate["c_JiraLink"] = myLink;
OperationResult updateResult = restApi.Update(s["_ref"], toUpdate);
}
}
}
}
Note that this is not different from a more general example of setting LinkID of a WebLink type of filed using a browser's REST client.
Method: POST
URL:
https://rally1.rallydev.com/slm/webservice/v2.0/defect/3807704995?key=abc123...
Request Body:
{
"defect":
{
"c_JiraLink":{
"DisplayString":"",
"LinkID":"NM-2"
}
}
}
I am trying to add a simple log in with Facebook button to my ASP.NET (C#) website. All I need is on the server side to retrieve the Facebook user's email address once they have logged in.
I was trying to use this example but it seems that the cookie "fbs_appid" is no longer used and instead there is one called "fbsr_appid".
How can I change the sample to use the different cookie? Alternately does anyone have a working example of retrieving the logged in Facebook user's email address.
I know there is an SDK I can use but I want to keep things simple. The above example would be perfect if it worked.
I managed to get the information needed using the fbsr cookie. I created the following class which does all of the work to confirm the user logged in with Facebook and then retrieves the user's details:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Script.Serialization;
namespace HarlequinShared
{
public class FacebookLogin
{
protected static string _appId = null;
protected static string AppId
{
get
{
if (_appId == null)
_appId = ConfigurationManager.AppSettings["FacebookAppId"] ?? null;
return _appId;
}
}
protected static string _appSecret = null;
protected static string AppSecret
{
get
{
if (_appSecret == null)
_appSecret = ConfigurationManager.AppSettings["FacebookAppSecret"] ?? null;
return _appSecret;
}
}
public static FacebookUser CheckLogin()
{
string fbsr = HttpContext.Current.Request.Cookies["fbsr_" + AppId].Value;
int separator = fbsr.IndexOf(".");
if (separator == -1)
{
return null;
}
string encodedSig = fbsr.Substring(0, separator);
string payload = fbsr.Substring(separator + 1);
string sig = Base64Decode(encodedSig);
var serializer = new JavaScriptSerializer();
Dictionary<string, string> data = serializer.Deserialize<Dictionary<string, string>>(Base64Decode(payload));
if (data["algorithm"].ToUpper() != "HMAC-SHA256")
{
return null;
}
HMACSHA256 crypt = new HMACSHA256(Encoding.ASCII.GetBytes(AppSecret));
crypt.ComputeHash(Encoding.UTF8.GetBytes(payload));
string expectedSig = Encoding.UTF8.GetString(crypt.Hash);
if (sig != expectedSig)
{
return null;
}
string accessTokenResponse = FileGetContents("https://graph.facebook.com/oauth/access_token?client_id=" + AppId + "&redirect_uri=&client_secret=" + AppSecret + "&code=" + data["code"]);
NameValueCollection options = HttpUtility.ParseQueryString(accessTokenResponse);
string userResponse = FileGetContents("https://graph.facebook.com/me?access_token=" + options["access_token"]);
userResponse = Regex.Replace(userResponse, #"\\u([\dA-Fa-f]{4})", v => ((char)Convert.ToInt32(v.Groups[1].Value, 16)).ToString());
FacebookUser user = new FacebookUser();
Regex getValues = new Regex("(?<=\"email\":\")(.+?)(?=\")");
Match infoMatch = getValues.Match(userResponse);
user.Email = infoMatch.Value;
getValues = new Regex("(?<=\"first_name\":\")(.+?)(?=\")");
infoMatch = getValues.Match(userResponse);
user.FirstName = infoMatch.Value;
getValues = new Regex("(?<=\"last_name\":\")(.+?)(?=\")");
infoMatch = getValues.Match(userResponse);
user.LastName = infoMatch.Value;
return user;
}
protected static string FileGetContents(string url)
{
string result;
WebResponse response;
WebRequest request = HttpWebRequest.Create(url);
response = request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
result = sr.ReadToEnd();
sr.Close();
}
return result;
}
protected static string Base64Decode(string input)
{
UTF8Encoding encoding = new UTF8Encoding();
string encoded = input.Replace("=", string.Empty).Replace('-', '+').Replace('_', '/');
var decoded = Convert.FromBase64String(encoded.PadRight(encoded.Length + (4 - encoded.Length % 4) % 4, '='));
var result = encoding.GetString(decoded);
return result;
}
}
public class FacebookUser
{
public string UID { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
And then I can use this in my login page:
FacebookUser user = FacebookLogin.CheckLogin();
if (user != null)
{
Response.Write("<p>" + user.Email);
Response.Write("<p>" + user.FirstName);
Response.Write("<p>" + user.LastName);
}
This is further explained here.
I believe that this method is secure and does the job as simply as possible. Please comment if there is any concerns.
This is not at all how you should be doing things, especially if you are trying to do it on the server side. You should not use the cookie.
The different sdks make things simpler, they (especially the official ones) stay up to date with facebook changes, unlike the example you provided that just stopped working when the cookie name was changed.
I'm not a C# developer, and never tried to use facebook from that environment so I can't tell you exactly how to do it with C#, but this is the main concept:
When you send the user for authentication (assumed Server Side Flow) you need to ask the user for permissions for anything but basic and public user information.
The email field is filed under "extended permission" and you need to ask for it specifically, something like this:
https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_url=YOUR_REDIRECT_URI&scope=email
(You can read more about permissions here)
Once the user authorized your app and granted you with the permissions, you can then ask for that data.
You can do it in the server side by asking for the "/me" graph path (or just the email: "me?fields=email").
You can also do it in the client side with the javascript sdk:
FB.api("/me", function(response) {
console.log(response);
});