I am required to create separate windows service accounts for each
environment (dev, acceptance, and production) that my desktop
application uses to connect to one of our internal databases.
A global group has been added to these accounts to provide access
thereby requiring access by windows authentication using impersonation.
The connection string data is encrypted and stored
on a network, accessed by class library for security.
If I don't impersonate, and use the base constructor for the DbContext base class that accepts a connection string, it works because my personal account is assigned to the same global group. But when I encapsulate the instantiation of the DbContext object to impersonate, it fails with an internal exception stating catastrophic failure while the outer exception states
The provider did not return a ProviderManifest instance.
For example:
Console.WriteLine(Environment.UserName); //This shows me! So no impersonation yet!
using (new Impersonator("AppUser", "mydomain", "notapassword"))
{
Console.WriteLine(Environment.UserName); //This shows as AppUSER! So it works!
using (BillMarkContext dbContext = new BillMarkContext())
{
//Read each bill mark object
foreach (BillMarkCode code in dbContext.BillMarkCodes.AsEnumerable<BillMarkCode>())
Console.WriteLine(code.Code);
}
}
public partial class BillMarkContext : DbContext
{
private static string _connection = "Integrated Security=True;Persist Security Info=True;Initial Catalog=MyDB;Data Source=DBServer";
public BillMarkContext()
: base(_connection)
{}
public virtual DbSet<BillMarkCode> BillMarkCodes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{}
}
Then I tried to hard-code the connection information by creating my own DbConfiguration object, but that results in an error where it's evidently trying to do more than establish a readable connection. It's trying to create the database instead to which I do not have the rights.
Example:
[DbConfigurationType(typeof(MyDbConfiguration))]
public partial class BillMarkContext : DbContext
{
public BillMarkContext()
{}
public virtual DbSet<BillMarkCode> BillMarkCodes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{}
}
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices("System.Data.SqlClient", SqlProviderServices.Instance);
SetDefaultConnectionFactory(new SqlConnectionFactory("Integrated Security=True;Persist Security Info=True;Initial Catalog=MyDB;Data Source=DBServer"));
}
}
This is Code-First and I can only find very simple statements and super-high level examples using DbConfiguration. And with regards to a runtime definition of connection/provider information, info always seems to be directed at a model-based approach or neglects the provider all the way around.
How do I programmatically configure EF Code-First approach to accessing a database while impersonating an application's windows service account and not get these errors?
So, I think I have finally got this worked out. It's been quite an adventure through and around many rabbit holes reading a ton of MSDN articles and many individual blogs. And in the process of working this out, I've also discovered issues in how our DB access roles were layed out. This part only goes to show that it's equally important to make sure and verify that your roles are setup correctly. Don't just take their word for it. We've got them fixed now, but probably have more controls to put in place and make sure they remain managed in the most appropriate manner. I wanted to provide my example to help someone else in the same situation as well as offer something for the veterans to comment on if they see some improvement opportunities. The sample below is still a bit naive in the since that I could probably do more around SecureString, but I figure that's not the core of the issue. So, here goes...
Consumer:
[STAThread]
static void Main(string[] args)
{
try
{
string domain = "myDomain";
string userName = "myUserName";
string password = "NotAPassword"
//Using NEW_CREDENTIALS is the same as RunAs with the /netonly switch set. Local computer login is based on the
//current user. While the impersonated account will be used for remote access to resources on the network.
//Therefore authentication across the domain.
//Per MSDN, NEW_CREDENTIALS should only work with the WINNT50 provider type. However, I have verified this to work with Default.
//I'm just not sure of the long-term implications since MS made a point to specify this.
using (Impersonator.LogonUser(domain, userName, password, LogonType.NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50))
{
//This will show the currently logged on user (machine), because NEW_CREDENTIALS doesn't alter this, only remote access
Console.WriteLine("Current user...{0}", WindowsIdentity.GetCurrent().Name);
using (BillMarkContext dbContext = new BillMarkContext())
{
//Read each bill mark object
foreach (BillMarkCode code in dbContext.BillMarkCodes.AsEnumerable<BillMarkCode>())
{
Console.WriteLine(code.Code);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}
Context:
Obviously a real-world implementation will not store the connection string in a static field.
public partial class BillMarkContext : DbContext
{
private static string _connection4 = "Integrated Security=True;Persist Security Info=True;Initial Catalog=MyDB;Data Source=MyServer";
public BillMarkContext()
: base(_connection4)
{
//Since we're read-only
Database.SetInitializer<BillMarkContext>(null);
}
//View property setup since we're read-only
protected virtual DbSet<BillMarkCode> _billMarkCodes { get; set; }
public DbQuery<BillMarkCode> BillMarkCodes
{
get { return Set<BillMarkCode>().AsNoTracking(); }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ }
}
Impersonator & supporting classes/enums:
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
internal sealed class Impersonator : IDisposable
{
#region Properties
private SafeTokenHandle _handle;
private WindowsImpersonationContext _context;
private bool _isDisposed;
public bool IsDisposed
{
get { return _isDisposed; }
private set { _isDisposed = value; }
}
#endregion
#region Constructors / Factory Methods
private Impersonator(string domain, string userName, string password, LogonType logonType, LogonProvider provider)
{
bool gotTokenHandle = NativeLoginMethods.LogonUser(userName, domain, password, (int)logonType, (int)provider, out _handle);
if (!gotTokenHandle || _handle.IsInvalid)
{
int errorCode = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(errorCode);
}
}
public static Impersonator LogonUser(string domain, string userName, string password, LogonType logonType, LogonProvider provider)
{
Impersonator impersonator = new Impersonator(domain, userName, password, logonType, provider);
impersonator._context = WindowsIdentity.Impersonate(impersonator._handle.DangerousGetHandle());
return impersonator;
}
#endregion
#region Dispose Pattern Methods
private void Dispose(bool disposing)
{
//Allow the Dispose() to be called more than once
if (this.IsDisposed)
return;
if (disposing)
{
// Cleanup managed wrappers
if (_context != null)
_context.Dispose();
if (_handle != null && !_handle.IsClosed)
_handle.Dispose();
//Suppress future calls if successful
this.IsDisposed = true;
}
}
public void Dispose()
{
//Dispose the resource
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
internal class NativeLoginMethods
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(IntPtr handle);
}
internal sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
#region Constructors
internal SafeTokenHandle()
: base(true)
{ }
#endregion
#region Support Methods
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
protected override bool ReleaseHandle()
{
return NativeLoginMethods.CloseHandle(base.handle);
}
#endregion
}
/// <summary>
/// Logon Type enum
/// </summary>
internal enum LogonType : int
{
/// <summary>
/// This logon type is intended for users who will be interactively using the computer, such as a user being logged on by a terminal server, remote shell, or similar process. This logon type has the additional expense of caching logon information for disconnected operations; therefore, it is inappropriate for some client/server applications, such as a mail server.
/// </summary>
INTERACTIVE = 2,
/// <summary>
/// This logon type is intended for high performance servers to authenticate plaintext passwords. The LogonUser function does not cache credentials for this logon type.
/// </summary>
NETWORK = 3,
/// <summary>
/// This logon type is intended for batch servers, where processes may be executing on behalf of a user without their direct intervention. This type is also for higher performance servers that process many plaintext authentication attempts at a time, such as mail or web servers.
/// </summary>
BATCH = 4,
/// <summary>
/// Indicates a service-type logon. The account provided must have the service privilege enabled.
/// </summary>
SERVICE = 5,
/// <summary>
/// GINAs are no longer supported. Windows Server 2003 and Windows XP: This logon type is for GINA DLLs that log on users who will be interactively using the computer. This logon type can generate a unique audit record that shows when the workstation was unlocked.
/// </summary>
UNLOCK = 7,
/// <summary>
/// This logon type preserves the name and password in the authentication package, which allows the server to make connections to other network servers while impersonating the client. A server can accept plaintext credentials from a client, call LogonUser, verify that the user can access the system across the network, and still communicate with other servers.
/// </summary>
NETWORK_CLEARTEXT = 8,
/// <summary>
/// This logon type allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identifier but uses different credentials for other network connections. This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
/// </summary>
NEW_CREDENTIALS = 9
}
internal enum LogonProvider : int
{
/// <summary>
/// Use the standard logon provider for the system. The default security provider is negotiate, unless you pass NULL for the domain name and the user name is not in UPN format. In this case, the default provider is NTLM.
/// </summary>
LOGON32_PROVIDER_DEFAULT = 0,
/// <summary>
/// Use the Windows NT 3.5 logon provider.
/// </summary>
LOGON32_PROVIDER_WINNT35 = 1,
/// <summary>
/// Use the NTLM logon provider.
/// </summary>
LOGON32_PROVIDER_WINNT40 = 2,
/// <summary>
/// Use the negotiate logon provider.
/// </summary>
LOGON32_PROVIDER_WINNT50 = 3
}
Related
In my company there is a console app running on a windows environment serve/pc. My problem is when this Server shuts down or restarted by other people this app will be closed and have to restart the app manually for this I have to issue commands on it to start running.
and another problem is I would not know if the server state just restarted or shuts down.
I have this idea that i will build an app that would send me a sms message to my phone and alert me that this server is down or just restarted in .net vb/c#. honestly, I don't know where to start I tried to search it on the internet but found nothing. If you can help me where to start I'll appreciate it much and i will post here the development stage of this app.
thanks.
Sorry for the delay on an answer. Anyway, I have found out that there is no way to differentiate between a system shut down and a system restart. But in any case, I think your best approach is to the use the SystemEvents.SessionEnding and/or SystemEvents.SessionEnded events to capture the system/server's shutdown. The easiest way to do this would be to use a Windows Service and register these events, like so:
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
/* Choose one or both of these to register for */
SystemEvents.SessionEnding += OnSessionEnding; // Register with session ending event
SystemEvents.SessionEnded += OnSessionEnded; // Register with session ended event
}
protected override void OnStop()
{
/* Static events, so MUST deregister from them */
SystemEvents.SessionEnding -= OnSessionEnding;
SystemEvents.SessionEnded -= OnSessionEnded;
}
protected static void OnSessionEnding(Object sender, SessionEndingEventArgs e)
{
/* I suggest using SchwabenCode.EasySmtp as it is very easy to use and implements the IDisposable interface. If that is not an option, than simply use SmtpClient class */
if (e.Reason == SessionEndReasons.SystemShutdown)
{
// Send SMS message to yourself notifying shutdown is occurring on server
}
}
protected static void OnSessionEnded(Object sender, SessionEndedEventArgs e)
{
/* I suggest using SchwabenCode.EasySmtp as it is very easy to use and implements the IDisposable interface. If that is not an option, than simply use SmtpClient class */
if (e.Reason == SessionEndReasons.SystemShutdown)
{
// Send SMS message to yourself notifying shutdown is occurring on server
}
}
}
I hope that helps you get things started! Here is a enum and its extensions that I have used in the past for sending SMS messages:
/// <summary> Values that represent various carriers. </summary>
[Serializable]
public enum Carrier
{
None = 0,
Alltel = 1,
Att = 2,
BoostMobile = 3,
Sprint = 4,
Tmobile = 5,
UsCellular = 6,
Verizon = 7,
VirginMobile = 8
}
/// <summary> Carrier extensions. </summary>
public static class CarrierExtensions
{
/// <summary> Gets the email to SMS gateway for the specified carrier. </summary>
/// <param name="carrier"> The carrier to get the gateway for.</param>
/// <returns> The email to SMS gateway. </returns>
public static String GetGateway(this Carrier carrier)
{
switch (carrier)
{
case Carrier.Alltel:
return "#message.alltel.com";
case Carrier.Att:
return "#txt.att.net";
case Carrier.BoostMobile:
return "#myboostmobile.com";
case Carrier.Sprint:
return "#messaging.sprintpcs.com";
case Carrier.Tmobile:
return "#tmomail.net";
case Carrier.UsCellular:
return "#email.uscc.net";
case Carrier.Verizon:
return "#vtext.com";
case Carrier.VirginMobile:
return "#vmobl.com";
}
return String.Empty;
}
/// <summary> Formats the phone number with the appropriate email to SMS gateway. </summary>
/// <param name="carrier"> The carrier to get the gateway for.</param>
/// <param name="phoneNumber"> The phone number.</param>
/// <returns> The formatted phone number. </returns>
public static String FormatPhoneNumber(this Carrier carrier, String phoneNumber)
{
return String.Format("{0}{1}", phoneNumber, carrier.GetGateway());
}
}
Easiest would be to place the app in the startup folder:
for individual users: C:\Users[username]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
for all users: C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
But a better solution is to use the windows Task Scheduler and create a task to run the application on startup. Here is a link to an example using the scheduler.
I have a custom MembershipProvider class that inherits from MembershipProvider that takes two parameters:
public class CustomMembershipProvider : MembershipProvider
{
private readonly ISecurityRepository _securityRepository;
private readonly IUserRepository _userRepository;
public CustomMembershipProvider(ISecurityRepository securityRepository, IUserRepository userRepository)
{
...
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
...
}
... etc
}
The config file for this looks similar to this:
<membership defaultProvider="CustomMembershipProvider">
<providers>
<clear />
<add name="CustomMembershipProvider" type="Library.Membership.CustomMembershipProvider" />
</providers>
</membership>
This works fine mostly throughout my web application for logging in and logging out. I am using Unity for DI and have the necessary classes setup in my Boostrapper.cs class.
However I recently ran into an issue when I wanted to create a custom User class and called the Membership.GetUser method. I get the following exception when I do:
{"No parameterless constructor defined for this object. (C:\\*app path*\\web.config line 43)"}
Line 43 in my config file points to the custom membership provider that I posted above. I think that elsewhere the app is using Unity to resolve those parameters but when using the Membership class it doesn't.
Is there any way I can tell the application how to resolve those dependencies or if not is there a way of adding those dependencies to my membership provider without using the concrete implementation?
EDIT 1:
Here is the custom User class:
public class User : MembershipUser
{
public int UserId { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public DateTime LastLoggedOnDate { get; set; }
...
}
EDIT 2:
In my custom membership provider class this is what the GetUser method looks like:
public override MembershipUser GetUser(string username, bool userIsOnline)
{
return _userRepository.GetUser(username);
}
The problem is that you can't inject into the Membership provider via constructor. Refer to this question
I implemented a Custom Membership Provider using Ninject and I used the ServiceLocator to get the instance of the service.
public class AccountMembershipProvider : MembershipProvider
{
private readonly IUsers _users;
public AccountMembershipProvider()
{
_users = ServiceLocator.Current.GetInstance<IUsers>();
}
public override bool ValidateUser(string username, string password)
{
return _users.IsValidLogin(username, password);
}
...
}
In your case, you need to get the IUserRepository and ISecurityRepository.
When you wire your Interfaces/services
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUsers>().To<UsersService>();
kernel.Bind<IRoles>().To<RolesService>();
kernel.Bind<MembershipProvider>().To<AccountMembershipProvider>().InRequestScope();
kernel.Bind<RoleProvider>().To<AccountRoleProvider>().InRequestScope();
}
You can check a complete example of it working (using Ninject but you can adapt it to Unity) here: https://github.com/lopezbertoni/SampleApp
Hope this helps,
I also use Unity and implemented a custom membership provider, but used a slightly different approach. Check the code sample:
/// <summary>
/// Defines the custom membership provider class.
/// </summary>
public class SsoMembershipProvider : MembershipProvider
{
private IApplicationsRepository _appsRepo;
private IUsersRepository _usersRepo;
private IMembershipsRepository _membershipsRepo;
/// <summary>
/// Initializes a new instance of the <see cref="SsoMembershipProvider"/> class
/// using injectionConstructor attribute in order to get the repositories needed.
/// </summary>
/// <param name="appsRepo">The apps repo.</param>
/// <param name="usersRepo">The users repo.</param>
/// <param name="membershipsRepo">The memberships repo.</param>
[InjectionConstructor]
public SsoMembershipProvider(IApplicationsRepository appsRepo, IUsersRepository usersRepo, IMembershipsRepository membershipsRepo)
{
_appsRepo = appsRepo;
_usersRepo = usersRepo;
_membershipsRepo = membershipsRepo;
}
/// <summary>
/// Initializes a new instance of the <see cref="SsoMembershipProvider"/> class.
/// which calls the internal contructor.
/// </summary>
/// <remarks>This is happening due to the fact that membership provider needs a
/// parametless constructor to be initialized</remarks>
public SsoMembershipProvider()
: this(DependencyResolver.Current.GetService<IApplicationsRepository>(),
DependencyResolver.Current.GetService<IUsersRepository>(),
DependencyResolver.Current.GetService<IMembershipsRepository>())
{ }
}
This worked for me
public ICustomerRepository CustomerRepository {
get { return DependencyResolver.Current.GetService<ICustomerRepository>(); }
}
and then use
public override bool ValidateUser(string username, string password)
{
var abc = CustomerRepository.ValidateCustomer(username, password);
}
I have a somewhat simple web app, that uses an ASMX web service as its sole data access. All the information is gotten from it, and saved to it. It works fine so thats out of the way.
I just updated to VS2012, and it complained about the class implementing the service reference, does not inherit from IDisposeable.
After some reading, i am more confused as some solutions are really elaborate, some are simple. Short version is, after understanding so little, it seems like i cant adapt it to how my app is made.
I have several data access classes, all focusing on methods for an area. For example, one dataaccess for customer related calls, one for product related calls etc.
But since they are all using the same service, they all derive from a base data access class that holds the reference.
This is the base data access class:
public class BaseDataAccess
{
private dk.odknet.webudv.WebService1 _service;
private string _systemBrugerID, _systemPassword;
public BaseDataAccess()
{
//Gets the system user and password that is stored in the webconfig file. This means you only have to change
//the username and password in one place without having to change the code = its not hardcoded.
_systemBrugerID = System.Configuration.ConfigurationManager.AppSettings["SystemBrugerID"].ToString();
_systemPassword = System.Configuration.ConfigurationManager.AppSettings["SystemPassword"].ToString();
_service = new dk.odknet.webudv.WebService1();
}
/// <summary>
/// Gets an instance of the webservice.
/// </summary>
protected dk.odknet.webudv.WebService1 Service
{
get { return _service; }
}
/// <summary>
/// Gets the system user id, used for certain methods in the webservice.
/// </summary>
protected string SystemBrugerID
{
get { return _systemBrugerID; }
}
/// <summary>
/// Gets the system user password, used for certain methods in the webservice.
/// </summary>
protected string SystemPassword
{
get { return _systemPassword; }
}
}
And here is how a derived class utilizes the service reference from the base class:
public class CustomerDataAccess : BaseDataAccess
{
public CustomerDataAccess() {}
/// <summary>
/// Get's a single customer by their ID, as the type "Kunde".
/// </summary>
/// <param name="userId">The user's username.</param>
/// <param name="customerId">Customer's "fkKundeNr".</param>
/// <returns>Returns a single customer based on their ID, as the type "Kunde".</returns>
public dk.odknet.webudv.Kunde GetCustomerById(string userId, string customerId)
{
try
{
return Service.GetKunde(SystemBrugerID, SystemPassword, userId, customerId);
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}}
So how on earth do i implement IDisposable in this situation? I just cant wrap my head around it.
EDIT
I have fiddled with the service reference, and come up with this:
/// <summary>
/// Gets an instance of the webservice.
/// </summary>
protected dk.odknet.webudv.WebService1 Service
{
get
{
try
{
using (_service = new dk.odknet.webudv.WebService1())
{
return _service;
}
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}
}
Yes the exception handling isnt great, i will get to that (advice is appreciated), but VS2012 does not complain about the lack of IDisposable anymore.
Instantiation of the service has been removed from the constructor. The app works fine without any further modifications.
Will this suffice?
First of all I wanted to thank all of you for your continuous contributions to the Stack Overflow community! I've been a member of Stack Overflow for years and have come to rely on your input more so than any other source online. Though I try to participate and answer members' questions whenever I can, every once in a while I find myself stuck and in need of help.
Speaking of which I have an unusual code problem. I am writing an API library in C# that needs to be able to be called from WPF/Windows Forms application, but also from within Unit Test code.
The issue is that I need to be able to report (in Excel) on whether each method of the library executed properly when the API is called from within a WPF/windows forms application, along some other metadata and optionally a return type.
When the code is consumed within Unit Tests I don't really care about the reporting, but I do need to be able to produce an Assert on whether the API call executed properly or not.
For instance, if in a Unit Test we have an Test Initialize portion, one of the API calls may be to create a Domain User for the test method to use. Another one may also create a Domain Group, so that the user has proper group membership.
To accomodate the consumption of the API from WPF/WinForms, I've been rewriting every function in the API to return a OperationStep type, with the hopes that when all API calls have executed I would have an IEnumerable<OperationStep> which I can write to a CSV file.
So the question is is there an easier way of achieving what I have done so far? The reporting is extremely tedious and time consuming to code, considering that the API library consists of hundreds of similar methods. Samples are described bellow:
OperationStep<PrincipalContext> createDomainConnectionStep = DomainContext.Current.GetPrincipalContext(settings.DomainInfo);
OperationStep<UserPrincipal> createDomainUserStep = DomainContext.Current.CreateUser(createDomainConnectionStep.Context, settings.TestAccountInfo.Username, settings.TestAccountInfo.Password);
OperationStep<GroupPrincipal> createDomainGroupStep = DomainContext.Current.CreateGroup(createDomainConnectionStep.Context, settings.TestAccountInfo.UserGrupName);
Where the DomainContext is a singleton object whose functionality is to connect to the domain controller and create a user, group, and associate the user to a group.
Note that both the second and the third method call require the output of the first, and therefore warranting the need for having the public T Context within the OperationResult object as described bellow.
The OperationStep object consists of the following properties which are inherited by the IOperation interface with the exception of the public T Context.
public class OperationStep<T> : IOperation
{
/// <summary>
/// Denotes the Logical Name of the current operation
/// </summary>
public string Name { get; set; }
/// <summary>
/// Denotes the stage of execution of the current operation: Setup, Execution, Validation, Cleanup
/// </summary>
public OperationStage Stage { get; set; }
/// <summary>
/// Denotes whether the test step completed properly or failed.
/// </summary>
public OperationResult Result { get; set; }
/// <summary>
/// Denotes the return type of the test method.
/// </summary>
public T Context { get; set; }
/// <summary>
/// Denotes any other relevant information about the test step
/// </summary>
public string Description { get; set; }
/// <summary>
/// If the test step result is failed, this should have the stack trace and the error message.
/// </summary>
public string Error { get; set; }
}
The method calls themselves are a bit bloated and tedious but here is a sample.
public class DomainContext
{
private static volatile DomainContext currentContext;
private static object synchronizationToken = new object();
/// <summary>
/// default ctor.
/// </summary>
private DomainContext() { }
/// <summary>
/// Retrieves the Current DomainContext instance.
/// </summary>
public static DomainContext Current
{
get
{
if (currentContext == null)
{
lock (synchronizationToken)
{
if (currentContext == null)
{
currentContext = new DomainContext();
}
}
}
return currentContext;
}
}
/// <summary>
/// Establishes a connection to the domain.
/// </summary>
/// <param name="domainInfo"></param>
/// <returns></returns>
public OperationStep<PrincipalContext> GetPrincipalContext(DomainInfo domainInfo)
{
OperationStep<PrincipalContext> result = new OperationStep<PrincipalContext>();
result.Name = "Establish Connection to Active Directory";
result.Result = OperationResult.Success;
result.Stage = OperationStage.Setup;
result.Description = string.Format("Domain Name: {0}, Default Containter: {1}", domainInfo.FQDN, domainInfo.Container);
try
{
ContextType contextType = this.GetContextType(domainInfo.DomainType);
PrincipalContext principalContext;
try
{
principalContext = new PrincipalContext(contextType, domainInfo.FQDN, domainInfo.Container);
}
catch
{
throw new Exception("Unable to establish connection to Active Directory with the specified connection options.");
}
if (principalContext != null)
{
bool authenticationResult = principalContext.ValidateCredentials(domainInfo.Username, domainInfo.Password);
if (!authenticationResult)
{
throw new Exception("Unable to authenticate domain admin user to Active Directory.");
}
result.Context = principalContext;
result.Result = OperationResult.Success;
}
}
catch(Exception ex)
{
result.Error = ex.Message;
result.Result = OperationResult.Failure;
}
return result;
}
}
When all method calls have executed theoreticaly I should have an IEnumerable<IOperation> which in the case of a win form I can write in a csv file (to be viewed in MS Excel) or in the case of a unit test I can simply omit the extra info and ignore (other than the method executed successively and the T Context property).
If I understood you correctly - all that OperationSteps are here only for logging. Then why not enable simple .NET logging? Log needed info where it is convenient for you. You can use TraceSource with DelimetedTraceListener to write to .csv file. More than that. You can move logging logic to Strategy class and override its logging methods in your unit test so that instead of logging you call Assert methods.
In VS2010 I have two MVC 2 based web apps within the same solution that also share a common Setup project. One app is a configuration utility for setting up users and variables in the opposing app. Upon installation, the two IIS directories look like this on the user's browser:
App1: http://localhost/App1/Auth/Login
App2: http://localhost/App1/App2/Auth/Login
The problem I'm having is when the user has both apps open at the same time, and logs out of one of them they are also logged out of the opposing app. This is a minor issue, but I've been tasked with correcting it.
From what I can tell, the two apps must be sharing the same Session object, because the logout command method in each controller invokes Session.Abandon() .
Only two controllers have the ability to log out a user; here's the constructor's from those controller's:
App1: namespace App1.Controllers
/// <summary>
/// Functionality related to Assets
/// </summary>
public class AssetsController : Controller
{
private IConfig _config = null;
private IProfileRepository _profiles = null;
private IInspectionRepository _inspections = null;
private ICustomLabelsFactory _labels = null;
private IValidateRepository _validator = null;
/// <summary>
/// Create an instance of the AssetsController which uses the db.
/// </summary>
public AssetsController() : this(Config.Current, new ProfileRepository(Config.Current), new InspectionRepository(), new CustomLabelFactory(), new ValidateRepository()) { }
/// <summary>
/// Create an instance of the AssetsController with the given
/// IInspectionRepository implementation.
/// </summary>
/// <param name="inspections">IInspectionRepository implementation.</param>
public AssetsController(IConfig config, IProfileRepository profiles, IInspectionRepository inspections, ICustomLabelsFactory labels, IValidateRepository validator)
: base()
{
ViewData["_Module"] = "Assets";
_config = config;
_profiles = profiles;
_profiles.ModelState = ModelState;
_inspections = inspections;
_inspections.ModelState = ModelState;
_labels = labels;
_labels.ModelState = ModelState;
_validator = validator;
_validator.CustomLabels = _labels.Assets;
_validator.ModelState = ModelState;
}
App2: namespace App1.App2.Controllers
/// <summary>
/// Handles login/logout functionality
/// </summary>
public class AuthController : Controller
{
private ILoginService _login;
private IUtilityRepository _utility;
/// <summary>
/// Creates the Auth controller using the default User Repository which
/// uses the database.
/// </summary>
public AuthController() : this(new LoginService(), new UtilityRepository()) { }
/// <summary>
/// Creates the Auth controller with the given User Repository.
/// </summary>
/// <param name="userRepository">IUserRepository implementation.</param>
public AuthController(ILoginService loginService, IUtilityRepository utility)
: base()
{
ViewData["_Module"] = "Login";
_login = loginService;
_login.ModelState = ModelState;
_utility = utility;
_utility.ModelState = ModelState;
}
I might be barking up the wrong tree on where to start looking at the code, but I'm hoping someone can see something obvious here that I can't. Or, maybe someone can tell me how to do this differently so there is not a shared Session object involved. I've been working on this on and off for the better part of this week, so any help offered will be greatly appreciated.
You could configure each application to use a different session database in your web.config
EDIT: something like
<sessionState mode="SQLServer" sqlConnectionString="Data Source=.\SQLEXPRESS;User Id=test;Password=test;Application Name=AppName" />
<machineKey
validationKey="SOMEKEY"
validation="SHA1" decryption="AES"
/>
Where somekey is different for each application
A simple, lazy, IIS settings avoiding solution is to open each in a different browser.