Platform is Windows 7 and above.
I am having problems with BITS.
I don't even know if this is feasible to do.
I would like to initiate a BITS job under the System account running as a service. I would like to pass in credentials such as NT domain creds domain\Username. I would like the job to transfer (download) from a network share but not while that user is logged on.
So just to re-iterate, Service to download from a network share using BITS but the will need to domain cred to access the share.
I have tried so many different things from impersonating the NT domain account (user isn't logged on and therefore the Job wont start).
Pass credentials into the BITS job but that has little success.
Attempted to add the network share in Credential manager but fails with error 1312
Update
Some code here for you - I am using the SharpBits as the BITS wrapper. http://sharpbits.codeplex.com/
I have tried to minimise the class to make it as simple as possible. Apologies on posting the code its not as simple as I expected.
using SharpBits.Base;
public class BitsFetchFiles : IDisposable
{
BitsCredentials _BitsCredentials;
BitsManager mManager = new BitsManager();
List<string> _JobIds = new List<string>();
DirectoryInfo _Source; //Set by constructor
DirectoryInfo _Cache; //Set by constructor
//Impersonation class to wrap credentials in
InfoDllImpersonation _Impersonation = null;
public void GetJob(string Domain, string Username, string Password)
{
_BitsCredentials = new BitsCredentials();
_BitsCredentials.UserName = Domain + "\\" + Username;
_BitsCredentials.Password = Password;
_BitsCredentials.AuthenticationTarget = AuthenticationTarget.Server;
_BitsCredentials.AuthenticationScheme = AuthenticationScheme.Negotiate;
_Impersonation = new InfoDllImpersonation(Username, Password, Domain);
//Just a method to the impersonated user access to the destination directory
GiveAllUserAccessToTheFolder(DirectoryDestination);
BitsJob Bjob = mManager.CreateJob("Service Download", JobType.Download);
Bjob.OnJobError += Bjob_OnJobError;
Bjob.OnJobTransferred += Bjob_OnJobTransferred;
Bjob.Priority = JobPriorityForDownload;
Bjob.OnJobModified += Bjob_OnJobModified;
_JobIds.Add(Bjob.JobId.ToString());
JobInformation.JobId = Bjob.JobId.ToString();
Bjob.TakeOwnership();
using (_Impersonation.ImpersonatedUser)
{
using (new NetworkConnection(_Source.FullName, new System.Net.NetworkCredential(
_Impersonation.UserName, _Impersonation.Password, _Impersonation.Domain)))
{
Bjob.AddCredentials(_BitsCredentials);
StartIncludingSubs(remoteFiles, Bjob, ref totalFileBytes);
}
}
//Enumerate all jobs listed and resume - e.g Bjob.Resume();
StartAllJobs();
}
void StartIncludingSubs(FileInfo[] remoteFiles, BitsJob Bjob, ref long totalFileBytes)
{
_LogFile.Log(string.Format("Using network credentials user : {0}",
_Impersonation.UserName), LogFile.LogLevel.Debug);
remoteFiles = _Source.GetFiles("*.*", SearchOption.AllDirectories);
JobInformation.TotalNumberOfFiles = remoteFiles.Length;
//Collected all remote files...
JobInformation.StatusInformation =
"Source Files have been collected and read with a total of "
+ remoteFiles.Length.ToString() + " files";
foreach (var fi in remoteFiles)
{
string newCacheFile = _Cache + fi.FullName.
Substring(_Source.FullName.Length);
string direct = newCacheFile.Substring(0, newCacheFile.LastIndexOf("\\"));
Directory.CreateDirectory(direct);
Bjob.AddFile(fi.FullName, newCacheFile);
if (_CancelTheJobAddingFiles)
{
return;
}
//Console.WriteLine("remote file {0} and Local is {1}", _Source.FullName + "\\" + fi.Name, newCacheFile);
totalFileBytes += fi.Length;
}
}
}
Related
I am working on a project that uses Silverlight, where I want to show PDFS files of a server path, but when I start debugging my code I find the following exception:
where I generate the flow in the following code:
System.Windows.Browser.HtmlElement myFrame = System.Windows.Browser.HtmlPage.Document.GetElementById("_sl_historyFrame");
if (myFrame != null)
{
DirectoryInfo folderPath = new DirectoryInfo(#"\\192.168.1.216\UploadFileMobilePDF\" + transfer.IdTransfer);
foreach (var file in folderPath.EnumerateFiles("*.pdf", SearchOption.AllDirectories))
{
myFrame.SetStyleAttribute("width", "1024");
myFrame.SetStyleAttribute("height", "768");
Uri uri = new Uri(folderPath + file.FullName);
string path = uri.AbsoluteUri.ToString();
myFrame.SetAttribute("src", path);
myFrame.SetStyleAttribute("left", "0");
myFrame.SetStyleAttribute("top", "50");
myFrame.SetStyleAttribute("visibility", "visible");
}
}
The error marks me when instantiating the DirectoryInfo class folderPath = new DirectoryInfo ()
I don't know if silverlight can't have permissions to server addresses
Your application likely doesn't have permission to access the files on the server you're trying to access.
Look into WindowsImpersonationContext for the most likely way around this. https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.windowsimpersonationcontext?view=netframework-4.8
You'll want a class (say, "MyImpersonator") that uses WindowsImpersonationContext to log onto the server using valid credentials. There are too many details to present an entire solution, but using the class (defined elsewhere) to get a single file might look something like this:
using (var impersonator = new MyImpersonator())
{
string name = ConfigurationManager.AppSettings["name"];
string password = ConfigurationManager.AppSettings["pass"];
if (impersonator.LogOnCrossDomain(account, pass))
{
if (File.Exists(filepath))
{
byte[] content = File.ReadAllBytes(filepath);
}
}
}
I am using google API for google drive, V3 with C# dot net. When trying to change the ownership from my service account to a 'regular' drive account (so it is within the same domain) of files other than docs, sheets and slides (like .zip or even .pdf) I get an error saying that:
Error: Bad Request. User message: "You can't yet change the owner of this item. (We're working on it.).
I guess this has something to do with the fact that docs, sheets and slides are not taken into account in the storage quota.
(1) Does this have a workaround? (Trying to change the file name to .doc before uploading it causes auto file conversion of the file and it is useless after that).
(2) Does this also happen on a paid account?
(3) Is Google team really 'working on it' as it states in the error message?
UPDATE:
This is the code I am using:
public string UploadFileToDrive(string FilePath, string ParentID)
{
try
{
Google.Apis.Drive.v3.Data.File body = new Google.Apis.Drive.v3.Data.File();
string fileNameNoPath = System.IO.Path.GetFileName(FilePath);
body.Name = "NewFile.ASC"; // some file names such as zip are not acceptable by google drive api
//body.MimeType = GoogleDriveMimeTypes.GetGenericMimeTypeString();
if (ParentID != null)
{
body.Parents = new List<string>();
body.Parents.Add(ParentID);
}
byte[] byteArray = System.IO.File.ReadAllBytes(FilePath);
System.IO.MemoryStream Ustream = new System.IO.MemoryStream(byteArray);
var requestU = _CurrentDriveService.Files.Create(body, Ustream, "");
requestU.Upload();
var uploadedFileID = requestU.ResponseBody.Id;
body.Name = fileNameNoPath;
//body.MimeType = GoogleDriveMimeTypes.GetGenericMimeTypeString();
FilesResource.CopyRequest cr = new FilesResource.CopyRequest(_CurrentDriveService, body, uploadedFileID);
var newFile = cr.Execute();
var NewFileNameID = newFile.Id;
DeleteFileFromDrive(uploadedFileID);
{
Permission p = new Permission();
p.Role = "reader";
p.Type = "anyone";
PermissionsResource.CreateRequest cc = new PermissionsResource.CreateRequest(_CurrentDriveService, p, NewFileNameID);
cc.Execute();
}
// you can comment out the next block if using Auth client
//
{
// make main account the owner in order to take its size quota in main account not google service.
Permission p = new Permission();
p.Role = "owner";
p.Type = "user";
p.EmailAddress = "vizfilesender#gmail.com";
PermissionsResource.CreateRequest cc = new PermissionsResource.CreateRequest(_CurrentDriveService, p, NewFileNameID);
cc.TransferOwnership = true; // acknowledge transfer of ownership - must be set to "true" in order for role to change to "owner"
cc.Execute();
}
return NewFileNameID;
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
return "";
}
}
With this code I can upload all files, change permissions for sharing, but I can't change ownership back to the google drive account.
I finally found the answer. I need to impersonate to another user.
var initializer = new ServiceAccountCredential.Initializer("blablablabla#blabla.iam.gserviceaccount.com")
{
Scopes = scope,
User = "emailToImpersonate#domain"
};
var credential = new ServiceAccountCredential(initializer.FromPrivateKey("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----\n"));
var driveService = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName
});
Also, make sure you give the google service domain wide delegation as shown here:
https://developers.google.com/drive/v2/web/delegation
and let up to 10 minutes for the change to take effect.
This is the workaround I have been searching for.
Given a service name, I would like to retrieve the username that it runs under (i.e. the username shown in the 'Log On' tab of a service's properties window).
There doesn't appear to be anything in the ServiceController class to retrieve this basic information. Nothing else in System.ServiceProcess looks like it exposes this information either.
Is there a managed solution to this, or am I going to have to drop down into something lower-level?
Using WMI, with the System.Management you can try the following code:
using System;
namespace WindowsServiceTest
{
class Program
{
static void Main(string[] args)
{
System.Management.SelectQuery sQuery = new System.Management.SelectQuery(string.Format("select name, startname from Win32_Service")); // where name = '{0}'", "MCShield.exe"));
using (System.Management.ManagementObjectSearcher mgmtSearcher = new System.Management.ManagementObjectSearcher(sQuery))
{
foreach (System.Management.ManagementObject service in mgmtSearcher.Get())
{
string servicelogondetails =
string.Format("Name: {0} , Logon : {1} ", service["Name"].ToString(), service["startname"]).ToString();
Console.WriteLine(servicelogondetails);
}
}
Console.ReadLine();
}
}
}
You can then later substitute the commented code with your service name, and it should only return the instances of your service process that is running.
WMI is your friend. Look at Win32_Service, specifically the StartName property. You can access WMI from C# via the System.Management.ManagementClass.
If you've not used WMI before, this article seems to be quite a good tutorial.
You can find this using the Windows Registry, reading the following string value, replacing [SERVICE_NAME] with the name of the Windows Service:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\[SERVICE_NAME]\ObjectName
Try this:
System.Security.Principal.WindowsIdentity.GetCurrent();
but the most obvious you will get LOCAL SYSTEM or NETWORK. The reason that you cannot show this user - that service can manage multiple users (shared by desktop, attached to current windows session, using shared resource ...)
System starts service, but any user can use it.
This solution works fine for me:
ManagementObject wmiService = new ManagementObject("Win32_Service.Name='" + this.ServiceName + "'");
wmiService.Get();
string user = wmiService["startname"].ToString();
public String getUsername() {
string username = null;
try {
ManagementScope ms = new ManagementScope("\\\\.\\root\\cimv2");
ms.Connect();
ObjectQuery query = new ObjectQuery
("SELECT * FROM Win32_ComputerSystem");
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(ms, query);
foreach (ManagementObject mo in searcher.Get()) {
username = mo["UserName"].ToString();
}
string[] usernameParts = username.Split('\\');
username = usernameParts[usernameParts.Length - 1];
} catch (Exception) {
username = "SYSTEM";
}
return username;
}
I would like to have a clean C# class that authenticates from Active Directory.
It should be pretty simple, it just has to ask for credentials and check if it matches what AD is expecting.
I am responsible for a number of C# applications, and I would like all of them to use the same class.
Could someone please provide a clean code sample of such a class? It should have good error handling, be well commented, and specifically ask for credentials rather than try to read if a user is already logged in to AD for another application. (This is a security requirement because some applications are used in areas with shared computers: People with multiple roles and different permission levels may use the same computer and forget to log out between sessions)
http://support.microsoft.com/kb/316748
public bool IsAuthenticated(String domain, String username, String pwd)
{
String domainAndUsername = domain + "\\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
try
{ //Bind to the native AdsObject to force authentication.
Object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if(null == result)
{
return false;
}
//Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (String)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
}
Admittedly I have no experience programming against AD, but the link below seems it might address your problem.
http://www.codeproject.com/KB/system/everythingInAD.aspx#35
There's some reason you can't use Windows integrated authentication, and not bother users with entering their names and passwords? That's simultaneously the most usable and secure solution when possible.
Currently I have an application that receives an uploaded file from my web application. I now need to transfer that file to a file server which happens to be located on the same network (however this might not always be the case).
I was attempting to use the webclient class in C# .NET.
string filePath = "C:\\test\\564.flv";
try
{
WebClient client = new WebClient();
NetworkCredential nc = new NetworkCredential(uName, password);
Uri addy = new Uri("\\\\192.168.1.28\\Files\\test.flv");
client.Credentials = nc;
byte[] arrReturn = client.UploadFile(addy, filePath);
Console.WriteLine(arrReturn.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
The machine located at 192.168.1.28 is a file server and has a share c:\Files.
As of right now I am receiving an error of Login failed bad user name or password, but I can open explorer and type in that path login successfully. I can also login using remote desktop, so I know the user account works.
Any ideas on this error?
Is it possible to transfer a file directly like that? With the webclient class or maybe some other class?
Just use
File.Copy(filepath, "\\\\192.168.1.28\\Files");
A windows fileshare exposed via a UNC path is treated as part of the file system, and has nothing to do with the web.
The credentials used will be that of the ASP.NET worker process, or any impersonation you've enabled. If you can tweak those to get it right, this can be done.
You may run into problems because you are using the IP address instead of the server name (windows trust settings prevent leaving the domain - by using IP you are hiding any domain details). If at all possible, use the server name!
If this is not on the same windows domain, and you are trying to use a different domain account, you will need to specify the username as "[domain_or_machine]\[username]"
If you need to specify explicit credentials, you'll need to look into coding an impersonation solution.
namespace FileUpload
{
public partial class Form1 : Form
{
string fileName = "";
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string path = "";
OpenFileDialog fDialog = new OpenFileDialog();
fDialog.Title = "Attach customer proposal document";
fDialog.Filter = "Doc Files|*.doc|Docx File|*.docx|PDF doc|*.pdf";
fDialog.InitialDirectory = #"C:\";
if (fDialog.ShowDialog() == DialogResult.OK)
{
fileName = System.IO.Path.GetFileName(fDialog.FileName);
path = Path.GetDirectoryName(fDialog.FileName);
textBox1.Text = path + "\\" + fileName;
}
}
private void button2_Click(object sender, EventArgs e)
{
try
{
WebClient client = new WebClient();
NetworkCredential nc = new NetworkCredential("erandika1986", "123");
Uri addy = new Uri(#"\\192.168.2.4\UploadDocs\"+fileName);
client.Credentials = nc;
byte[] arrReturn = client.UploadFile(addy, textBox1.Text);
MessageBox.Show(arrReturn.ToString());
}
catch (Exception ex1)
{
MessageBox.Show(ex1.Message);
}
}
}
}
when you manually open the IP address (via the RUN command or mapping a network drive), your PC will send your credentials over the pipe and the file server will receive authorization from the DC.
When ASP.Net tries, then it is going to try to use the IIS worker user (unless impersonation is turned on which will list a few other issues). Traditionally, the IIS worker user does not have authorization to work across servers (or even in other folders on the web server).