C# .NET 4 Self-Contained Oracle Driver Deployment - c#

I want to write and deploy some software without the need to install Oracle drivers on the users' workstations, rather, I would like to copy everything needed to a network location, and for the users to launch from there.
I have successfully established an OracleConnection by deploying the following to the application's folder. (I took these from instantclient-basiclite-nt-11.2.0.3.0 provided by Oracle )
oci.dll
ociw32.dll
orannzsbb11.dll
oraocci11.dll
oraociicus11.dll
I have found this works with an LDAP Query to establish the DataSource= part of the connection string. The code I use is as follows - I hope this is useful to someone.
List<string> LDAPServers
string OracleInstance
private string GetOracleNetDescriptor()
{
string result = null;
foreach (string ldapserver in LDAPServers)
{
try
{
DirectoryEntry de = new DirectoryEntry(String.Format("LDAP://{0}", ldapserver), null, null, AuthenticationTypes.Anonymous);
DirectorySearcher ds = new DirectorySearcher(de);
ds.PropertiesToLoad.Add("orclnetdescstring");
ds.SearchScope = SearchScope.Subtree;
ds.Filter = String.Format("(CN={0})", OracleInstance);
SearchResult sr = ds.FindOne();
if (sr != null)
{
result = Encoding.Default.GetString(sr.Properties["orclnetdescstring"][0] as byte[]);
break;
}
}
catch
{
result = null;
}
}
if (result == null)
throw new Exception(String.Format("Failed To Identify Oracle Instance '{0}' From LDAP", OracleInstance));
else
return result;
}
So far so good, however ...
The solution is currently dependent on a reference to the deprecated System.Data.OracleClient which I really need/want to avoid
I would like to get access to some advanced features, such as Entity Framework.
Can the panel advise ?
I would also appreciate any comments on the ldap code above as this is all work-in-progress.

Had the same problem with the deprecated provider. I've made good expiriences with Devart dotConnect for Oracle:
http://www.devart.com/dotconnect/oracle/
It is not free of charge (and I'm not related to this company nor would I benefit in any kind).
It works with ADO.Net's DataTable and DataSet. As some years passed since my last usage I don't know how well it integrates with EF.

Related

DirectorySearcher returns ERROR_MORE_DATA

I have developed an application that talks to the Directory Server and gets user information.
This application is a generic one and can talk to Active Directory or Any other Directory Services.
In one case where i use this application to read data from Radiant One VDS, the application fails with the ERROR_MORE_DATA. Following is the code that returns this error:
try
{
using (DirectoryEntry de = new DirectoryEntry("LDAP://" + server + "/" + basedn, username, pwd,AuthenticationTypes.None))
{
using (DirectorySearcher Searcher = new DirectorySearcher(de))
{
Searcher.Filter = "(&(objectClass=user))";
Searcher.ReferralChasing = ReferralChasingOption.All;
Searcher.PropertiesToLoad.Add("cn");
Searcher.PropertiesToLoad.Add("memberof");
Searcher.PageSize = 1000;
using (SearchResultCollection allUsers = Searcher.FindAll())
{
foreach (SearchResult user in allGroups)
{
.
.
.
.
}
}
}
}
}
catch(System.Exception ex)
{
}
In the above code, Searcher.FindAll() returns ERROR_MORE_DATA. When i searched i found the this article.
But, this article talks about .NET 1.0 and my application runs with .NET 3.5
Can you anyone please help me here? Is there any way to fix this without going for DirectoryServices.Protocols?
Generally, this issue comes in the following situation:-
If one or more entries are found in Network and the buffer size is not enough to hold it.
see ERROR_MORE_DATA
You just need to specify the size of buffer.
see: specify the size of buffer in network call

Different result with DirectorySearcher

I wrote a small app to check AD group members. When I execute the following code on my pc, It works well, the SearchResult contains the "member" property, however when I run the same exe on the server or on an another computer the "member" property is missing. The usnchanged and usncreated will be different also. I run the exe with the same user on every pc. What can cause this?
...
using (DirectorySearcher searcher = new DirectorySearcher())
{
searcher.CacheResults = false;
searcher.Filter = "(&(objectClass=group)(cn=" + ADName + "))";
searcher.SizeLimit = int.MaxValue;
searcher.PageSize = int.MaxValue;
if (!DirectoryEntry.Exists(ADPath))
{
return null;
}
searcher.SearchRoot = new DirectoryEntry(ADPath);
using (SearchResultCollection collection = searcher.FindAll())
{
if (collection.Count == 1)
{
return collection[0];
}
}
}
...
The group membership data is not replicated to the global catalog. The query might work sometimes, if you happen to connect to the domain controller with the actual membership data. On other machines, you probably connect to other domain controllers, of different domains, where the information is not available.
You might want to connect to a domain controller in the actual domain, not to the global catalog.

Cannot get LDAP connection working - always "Unknown error"

I'm researching what I need to build an Active Directory search tool which ultimately will be used in a C# ASP.NET web app.
I know very little about AD (and don't particularly want to know any more than necessary) so I've asked our tech ops to set up a dummy instance on a server. This they've done giving it the domain dummy.local
I now need to work out my LDAP connection string. Here I'm completely stuck. I'm using a user account which is a member of Domain Admins. After loads of hunting round the web I've tried all sorts of things to work out the various components of the LDAP connection string. For example, if I run the following in cmd.exe for that domain admin user on the server...
dsquery user -samid "username" | dsget user -memberof -expand
...this gives me the following information:
"CN=Domain Admins,CN=Users,DC=dummy,DC=local"
"CN=Remote Desktop Users,CN=Builtin,DC=dummy,DC=local"
"CN=Users,CN=Builtin,DC=dummy,DC=local"
"CN=Administrators,CN=Builtin,DC=dummy,DC=local"
"CN=Domain Users,CN=Users,DC=dummy,DC=local"
"CN=Denied RODC Password Replication Group,CN=Users,DC=dummy,DC=local"
I've also run the following in a C# console app...
using (var context = new PrincipalContext(ContextType.Domain))
using (var comp = ComputerPrincipal.FindByIdentity(context, Environment.MachineName))
{
Console.WriteLine(String.Join(",", comp.DistinguishedName.Split(',').SkipWhile(s => !s.StartsWith("OU=")).ToArray()));
}
...and this gives me the following:
OU=Domain Controllers,DC=dummy,DC=local
I thought I therefore had all the properties I needed to build my LDAP connection string. I ended up with this:
LDAP://COMSECWEBDEV.dummy.local/ou=Domain Controllers,/cn=Domain Admins,/cn=Users,/dc=dummy,/dc=local
I've tried using this connection string, with the username and password of the domain admin which I know is correct, but everything I try always gives me the same error:
System.Runtime.InteropServices.COMException (0x80005000): Unknown error (0x80005000)
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
Since this error gives me no detail I have no idea what I'm doing wrong. I'm sure I'm just not getting the connection string right, but I've no idea how to work out the correct string.
For completeness, here is the console code which I'm testing with:
static void Main(string[] args)
{
var connString = ConfigurationSettings.AppSettings["lc"];
var username = ConfigurationSettings.AppSettings["lu"];
var password = ConfigurationSettings.AppSettings["lpw"];
using (DirectoryEntry de = new DirectoryEntry(connString, username, password))
{
DirectorySearcher search = new DirectorySearcher(de);
search.PageSize = 1001;// To Pull up more than 100 records.
DirectorySearcher directorySearcher = new DirectorySearcher(de);
directorySearcher.Filter = string.Format("(&(objectClass=user)(objectCategory=user) (sAMAccountName={0}))", username);
directorySearcher.PropertiesToLoad.Add("msRTCSIP-PrimaryUserAddress");
try
{
var result = directorySearcher.FindOne();
var found = false;
if (result != null)
{
if (result.Properties["msRTCSIP-PrimaryUserAddress"] != null)
{
found = true;
Console.WriteLine("Found: " + result.Properties["msRTCSIP-PrimaryUserAddress"][0]);
}
}
if (!found)
{
Console.WriteLine("Sad face");
}
}
catch (Exception x)
{
Console.WriteLine(x.Message);
}
Console.WriteLine("------------");
}
}
I was trying to figure out how to properly format a LDAP connection string last week, and found this entry over on serverfault:
How can I figure out my LDAP connection string?
I noticed you had a "/" between each OU or DC entry - I didn't include those in mine, and I don't see them included in the above example either.
I'm far from an expert (obviously) but just figured I would throw it out there.

Visual-SVN Server C# code not properly adding user permissions (version 2.7.2)

Story time! One of our more disgruntled employees decided to upgrade visual svn and modify our web interface a day before his last day. We had the old authentication set up, and it was all working fine. Give this particular applications footprint in the grand scheme we followed the 'if it ain't broke, don't fix it' mantra.
It wasn't broke, and he fixed it...
SO here we are. I found This Question regarding interfacing with Visual SVN with C#, and it looks like he had just copied and pasted the code verbatim from there.
The interface we have is very simple. There is an input box that the user types in the name of there desired repo. Below that is a text area where he/she can add users to have access to the repo. The user lookup is done based off of email address and it hits our active directory. The end result of this is I have the name of the repo I need to create, and the users/SIDs of the people I need to give read/write access to.
Testing this code he pasted in, it seems like the repositories are getting created fine (they show up when I log into the server as an admin). Here is the repo creation code:
static public bool CreateRepository(repository r)
{
ManagementClass repoClass = new ManagementClass("root\\VisualSVN", "VisualSVN_Repository", null);
// Obtain in-parameters for the method
ManagementBaseObject inParams = repoClass.GetMethodParameters("Create");
// Add the input parameters.
inParams["Name"] = r.name;
// Execute the method and obtain the return values.
ManagementBaseObject outParams =
repoClass.InvokeMethod("Create", inParams, null);
return true;
}
'repository r' in the method parameters, repository is a class with the following properties:
private int _id;
private string _name;
private System.Nullable<System.DateTime> _deleteAfter;
private EntitySet<UserRepositoryRight> _UserRepositoryRights;
with all the public getters and setters you would expect from a linq to sql generated file.
UserRepositoryRight is a table that holds the one (repo) to many (users) relationships.
Like I said, I think this code is fine, since I am seeing the repositories being created.
The next copypasta code is the UpdatePermissions method
static public void UpdatePermissions(string sid, string repository, AccessLevel level, bool isAdmin = false)
{
//Update SVN
ManagementClass userClass = new ManagementClass("root\\VisualSVN", "VisualSVN_WindowsAccount", null);
ManagementClass permClass = new ManagementClass("root\\VisualSVN", "VisualSVN_PermissionEntry", null);
ManagementClass repoClass = new ManagementClass("root\\VisualSVN", "VisualSVN_Repository", null);
ManagementObject userObject = userClass.CreateInstance();
userObject.SetPropertyValue("SID", sid);
ManagementObject permObject = permClass.CreateInstance();
permObject.SetPropertyValue("Account", userObject);
permObject.SetPropertyValue("AccessLevel", level);
ManagementObject repoObject = repoClass.CreateInstance();
repoObject.SetPropertyValue("Name", repository);
ManagementBaseObject inParams =
repoClass.GetMethodParameters("SetSecurity");
inParams["Path"] = "/trunk";
inParams["Permissions"] = new object[] { permObject };
ManagementBaseObject outParams =
repoObject.InvokeMethod("SetSecurity", inParams, null);
//Update in DB
var db = new DataMapSVNServiceDataContext();
if (level == AccessLevel.NoAccess) //If we are removing the user
{
var output = (db.repositories.Single(r => r.name == repository)).UserRepositoryRights.Single(u => u.User.Sid == sid);
if (output.isAdmin != null && !((bool)output.isAdmin)) //making sure DB owner isn't ever removed
db.UserRepositoryRights.DeleteOnSubmit(output);
}
if (level == AccessLevel.ReadWrite) //if we are adding the user
{
var add = new UserRepositoryRight
{
isAdmin = isAdmin,
User = db.Users.Single(u => u.Sid == sid),
repository = db.repositories.Single(r => r.name == repository)
};
db.UserRepositoryRights.InsertOnSubmit(add);
}
db.SubmitChanges();
}
Here everything looks ok, but it does not seem to be carrying over to the repo and adding the user to have ReadWrite (key value is 2) permissions on the created repo. The tail end of the method just saves the data to our websites databases to allow us to interface with it.
So, the root problem is if I create a repo via the web interface here, I get a 403 Forbidden error when trying to access it, but NO errors when creating and deleting it. Can anyone point me in the right direction here?
You should use VisualSVN Server PowerShell cmdlets instead of using the server's WMI provider. The WMI provider of VisualSVN Server can be considered as an internal API. It is not documented and is not intended to be used to build custom applications.
Old answer
If you don't get any errors when running the code, I guess that the access rule is set on path <repo>/trunk which simply does not exist in the youngest revision. You can try the script on some fresh testing repository which contains "/trunk" in it's root. Otherwise, you can simply change change the code string inParams["Path"] = "/trunk"; to inParams["Path"] = "/";.
VisualSVN Server allows you to setup path-based authorization rules on items (files and folders) that don't exist in youngest revision because these items can exist in earlier and newer revisions.

How do I get a real name stored in Active Directory from an username with C#?

I want to create a quick application for people to resolve the name of a user stored in Active Directory from a set of credentials. Some applications only provide the user id and it is too much to expect an end user to fire up the Active Directory Users and Groups MMC snap-in.
Input would be something like "MYCORP\a_user" and output would be "Dave Smith" if that is what is stored in AD.
I want this to be able to run in my test domain and also in a multi-forest environment.
Can someone provide a sample that does this? Does retrieval of other attributes from AD such as telephone number follow the same pattern?
Target platform: .NET 2.0 and above.
Here's the code I use, taken from my authentication class:
string[] strUserName = username.Split("\\".ToCharArray());
using (var entry = new DirectoryEntry("LDAP://" + ADServer, ADServiceDomain + "\\" + ADServiceAccount, ADServicePassword))
using (var ds = new DirectorySearcher(entry, "sAMAccountName=" + strUserName[1])) {
ds.SearchScope = SearchScope.Subtree;
SearchResult result = ds.FindOne();
string fullname = result.Properties["displayName"][0].ToString();
}
System.DirectoryServices sucks. As you can see, it takes a ridiculous amount of code to do even the most basic things. I'd like to see a user authentication method that didn't require using exceptions for flow control.
Working with Active Directory is a bit painfull in C#, sure 3.5 adds some new classes to help, but for pure productivity I like to use Powershell and Quest's free PowerShell Commands for Active Directory
in which case the code looks something like
get-qaduser userid | select PhoneNumber,DisplayName
if you need this to run as part of your C# program, you can do that too
public static IEnumerable<PSObject> Invoke(string script, params object[] input)
{
IList errors = null;
using (var run = new RunspaceInvoke())
{
var psResults = run.Invoke(script, input, out errors);
if (errors != null && errors.Count > 0)
Debug.WriteLine(errors.Count);
foreach (PSObject res in psResults)
yield return res;
}
}
PSObject psUser = POSHelp.Invoke(
#"add-pssnapin Quest.ActiveRoles.ADManagement
($userid) = $input | % { $_ }
get-qaduser $userid", "auserid").Single();
Debug.WriteLine(psUser.Properties["DisplayName"].Value);
add a ref to Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll
See DirectorySearcher, loading the property "DisplayName".

Categories