Following up from this question:
designing application classes
What is wrong (from a design point of view) with this class:
I'm trying to refactor this class and it's abstract base class (Logon) and the fact it's actually horrible design. I wrote it myself (pretty much when I was a newbie). I'm finding it hard to refactor and want some input on it?
class NewUserLogon : Logon, ILogonNewUser, IDisposable
{
#region Member Variables
System.Windows.Forms.Form _frm = new MainWindow();
SQLDatabase.SQLDynamicDatabase sql;
SQLDatabase.DatabaseLogin dblogin;
LogonData lgndata;
System.Security.SecureString securepassword;
PasswordEncrypt.Collections.CreatedItems items;
LogonEventArgs e = new LogonEventArgs();
#endregion
#region Constructors
// for DI
public NewUserLogon(PasswordEncrypt.Collections.CreatedItems items) : base (items)
{
this.items = items;
}
#endregion
#region Public Methods
public new void Dispose()
{
}
public bool? ReadFromRegistry(HashedUsername username, HashedPassword hashedpassword)
{
return RegistryEdit.ReadFromRegistry(username, hashedpassword);
}
public bool WriteToRegistry(HashedUsername username, HashedPassword hashedpassword)
{
return RegistryEdit.WriteToRegistry(username, hashedpassword);
}
public override void Login(TextBox username, TextBox password)
{
base.Login(username, password);
Login(username.Text, password.Text);
}
#endregion
#region Protected Methods
protected override void Login(string username, string password) // IS INSECURE!!! ONLY USE HASHES
{
base.Login(username, password);
if (_user is NewUserLogon) // new user
{
sql = new PasswordEncrypt.SQLDatabase.SQLDynamicDatabase();
dblogin = new SQLDatabase.DatabaseLogin();
lgndata = base._logondata;
securepassword = new System.Security.SecureString();
// Set Object for eventhandler
items.SetDatabaseLogin = dblogin;
items.SetSQLDynamicDatabase = sql; // recreates L
items.Items = items;
string generatedusername;
// write new logondata to registry
if (this.WriteToRegistry(lgndata.HahsedUserName, lgndata.HashedPsw))
{
try
{
// Generate DB Password...
dblogin.GenerateDBPassword();
// get generated password into securestring
securepassword = dblogin.Password;
//generate database username
generatedusername = dblogin.GenerateDBUserName(username);
if (generatedusername == "Already Exists")
{
throw new Exception("Username Already Exists");
}
//create SQL Server database
try
{
sql.CreateSQLDatabase(dblogin, username);
}
catch (Exception ex)
{
//System.Windows.Forms.MessageBox.Show(ex.Message);
e.ErrorMessage = ex.Message;
e.Success = false;
OnError(this, e);
}
}
catch (Exception exc)
{
e.Success = false;
e.ErrorMessage = exc.Message;
OnError(this, e);
}
OnNewUserLoggedIn(this, e); // tell UI class to start loading...
}
else
{
System.Windows.Forms.MessageBox.Show("Unable to write to Registry!", "Registry Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation);
}
}
else if (_user is ExistingUserLogon) // exising user
{
bool? compare = base._regRead;
lgndata = base._logondata;
if (compare == true)
{
//Tell GUI to quit the 'busydialog' thread
OnMessage(this, e);
LogonFrm frm = LogonFrm.LogonFormInstance;
// tell user he already exists and just needs to login
// ask if user wants to logon straight away
System.Windows.Forms.DialogResult dlgres;
dlgres = System.Windows.Forms.MessageBox.Show("Your login already exists, do you wan to login now?", "Login Exists", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question);
if (dlgres == System.Windows.Forms.DialogResult.Yes)
{
ExistingUserLogon existinguser = new ExistingUserLogon(compare, lgndata);
existinguser.Error += new ErrorStatus(frm._newuser_Error);
existinguser.loginname = username;
existinguser.LoginNewUser();
///TELL GUI THAT USER LOGIN SUCCEEDED, THROUGH EVENT
e.Success = true;
OnNewUserLoggedIn(this, e);
}
else
{
e.Success = false;
e.ErrorMessage = "Failed";
OnError(this, e);
}
}
}
}
#endregion
}
Your class tries to do too many things. Try to separate different responsibilities into separate classes (eg database access and UI stuff).
And why are you instantiating a new Form at the beginning of your class and don't seem to use it further on?
Your protected Login is way too long.
Security should be a cross cutting concern, not a base class. I don't know if you have aspect oriented programming techniques available to you, but extending a base class with security built into it seems like an abuse of inheritance to me.
Related
So i want to create a new user and give it admin rights but i also need it to change the PasswordNeverExpires option to true.
class Program
{
public static string Name;
public static string Pass;
static void Main(string[] args)
{
Name = "Test1";
Pass = "Test2";
createUser(Name, Pass);
}
public static void createUser(string Name, string Password)
{
try
{
using (PrincipalContext ctx = new PrincipalContext(ContextType.Machine))
{
// create new newUser
UserPrincipal newUser = new UserPrincipal(ctx);
// set some properties
newUser.SamAccountName = Name;
newUser.SetPassword(Password);
// define new newUser to be enabled and password never expires
newUser.Enabled = true;
newUser.PasswordNeverExpires = true;
// save new newUser
newUser.Save();
Console.WriteLine("User has been created");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
I use UserPrinicipal here because when i use DirectoryEntry i can give the created user Admin rights but i havent found a way to set the PasswordNeverExpires option to true. With UserPrinicipal i can set the PasswordNeverExpires option to true, but i havent found a way to give it Admin rights.
If anyone knows how this is done, or know a way to do this better i would love to know how.
I want to know what is the correct way of doing this: lets say I have a login method that receives username and password, and log ins the user or return invalid username/password or not enough permissions. What is the correct way of doing this?
Way #1: throwing exception and handling in the user interface to display the error
public void Login(string username, string password)
{
if (SessionService.GetSession.IsLoggedIn)
{
throw new Exception("User is already logged in");
}
var user = GetByUsername(username);
if (user == null)
{
throw new LoginException(LoginResultEnum.InvalidUsername);
}
var hashPass = EncryptionService.Hash(password);
if (hashPass != user.password)
{
throw new LoginException(LoginResultEnum.InvalidPassword);
}
if (!user.HasPermission(PermissionTypeEnum.CanLogIn))
{
throw new MissingPermissionException(TipoPermisoEnum.CanLogIn);
}
SessionService.GetSession.Login(user);
}
Way #2: returning boolean true/false and handle the error in the UI (success or fail)
public bool Login(string username, string password)
{
if (SessionService.GetSession.IsLoggedIn)
{
return false;
}
var user = GetByUsername(username);
if (user == null)
{
return false;
}
var hashPass = EncryptionService.Hash(password);
if (hashPass != user.password)
{
return false;
}
if (!user.HasPermission(PermissionTypeEnum.CanLogIn))
{
return false;
}
SessionService.GetSession.Login(user);
return true;
}
Way #3: returning a LoginResult enum and handle in the UI
public LoginResult Login(string username, string password)
{
if (SessionService.GetSession.IsLoggedIn)
{
return LoginResult.AlreadyLoggedIn;
}
var user = GetByUsername(username);
if (user == null)
{
return LoginResult.InvalidUsername;
}
var hashPass = EncryptionService.Hash(password);
if (hashPass != user.password)
{
return LoginResult.InvalidPassword;
}
if (!user.HasPermission(PermissionTypeEnum.CanLogIn))
{
return LoginResult.Forbidden;
}
SessionService.GetSession.Login(user);
return LoginResult.OK;
}
In my view it better to create some dto if it is eligible for your case. So this dto will have the following properties:
public class LoginResponseDto
{
public bool Success { get; set; }
public string Error { get; set; }
}
And then you will return your response something like this:
public LoginResponseDto Login(string username, string password)
{
if (SessionService.GetSession.IsLoggedIn)
{
return new LoginResponseDto { Error = "User is already logged in" };
}
var user = GetByUsername(username);
if (user == null)
{
return new LoginResponseDto { Error = "There is no such user" };
}
var hashPass = EncryptionService.Hash(password);
if (hashPass != user.password)
{
return new LoginResponseDto { Error = "Incorrect password or username" };
}
if (!user.HasPermission(PermissionTypeEnum.CanLogIn))
{
return new LoginResponseDto { Error = "There is no permission to log in" };
}
SessionService.GetSession.Login(user);
return new LoginResponseDto { Success = true };
}
It is possible to see this tutorial "Create a secure ASP.NET MVC 5 web app with log in, email confirmation and password reset". Author of article use ViewBag in this article to send errors from controller and Succeeded to check whether login is okay.
In addition, try to avoid to show message about what is exactly wrong username or password.
I would say #3 is the best way.
#1 you are using Exception for non-exceptional circumstances. The control path is expected, so don't use Exceptions.
#2 By using a bool you are discarding information, is it InvalidPassword or Forbidden?
#3 Returns all information, allowing the UI to surface that information to the User.
How can i sign-out and redirect to the login page from a public static class?
I have tried the following but it does not stop page execution..
public static DatabaseNameEntities CreateEntitiesForSpecificDatabaseName(bool contextOwnsConnection = true)
{
string database_name = "";
try
{
database_name = System.Web.HttpContext.Current.Application["DB_NAME"].ToString();
}
catch (NullReferenceException)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
//Initialize the SqlConnectionStringBuilder
//Initialize the EntityConnectionStringBuilder
//Create entity connection
EntityConnection connection = new EntityConnection(entityBuilder.ConnectionString);
return new DatabaseNameEntities(connection);
}
I have tried the following but it does not stop page execution..
That's because it's simply not the MVC way. It's also breaks the Single Responsibility Principle, that is, why would a method named CreateEntitiesForSpecificDatabaseName() know anything about MVC or logging out a user. The code you posted generally breaks this principle multiple times (application state, signing out a user).
Additionally, catching an exception you can prevent is also poor practice (or as the Lead Developer for the C# Compiler team called it, Boneheaded Exceptions).
Consider the following code.
public static ControllerBaseExtensions
{
private const string DBNAME = "DB_NAME";
public static bool TryGetDatabaseName(this ControllerBase instance,
out string DbName)
{
DbName = null;
var app = GetApp(instance);
var result = app.Any(k => k == DBNAME);
if (result)
{
DbName = instance.Application[DBNAME] as string;
result = DbName != null;
}
return result;
}
public static void SetDatabaseName(this ControllerBase instance,
string DbName)
{
var app = GetApp(instance);
app[DBNAME] = DbName;
}
private static HttpApplication GetApp(ControllerBase instance)
{
return instance.ControllerContext.HttpContext.Application;
}
}
public ActionResult MyMethod()
{
string DbName;
if (!this.TryGetDatabaseName(out DbName))
{
FormsAuthentication.SignOut();
// http://stackoverflow.com/questions/30509980
RedirectToAction("Login", "Account");
}
CreateEntitiesForSpecificDatabaseName(Dbname);
}
public static DatabaseNameEntities CreateEntitiesForSpecificDatabaseName(
string dbName,
bool contextOwnsConnection = true)
{
//Initialize the SqlConnectionStringBuilder
//Initialize the EntityConnectionStringBuilder
//Create entity connection
EntityConnection connection = new
EntityConnection(entityBuilder.ConnectionString);
return new DatabaseNameEntities(connection);
}
Does simply ie, no try or catch work as expected
public static DatabaseNameEntities CreateEntitiesForSpecificDatabaseName(bool contextOwnsConnection = true)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
I'm using the Microsoft XRM SDK to programmatically add an entity. However, each time I run a .Create() command I get the following error:
Required member 'LogicalName' missing for field 'Target'
First time using this service and similar resources in our company are scarce, so not sure what this error means or how to investigate/solve it.
Below is the class I created to handle the XRM communication. I instantiate each of the connection properties in the construtor. Then, in this case, call CreateAgency(AgentTransmission agt). The exception is being thrown in the CreateAgency() method on the .Create(account) method call.
class DynamicsCommunication
{
private Uri OrganizationUri = new Uri("http://devhildy03/xRMDRMu01/XRMServices/2011/Organization.svc");
private ClientCredentials credentials;
private OrganizationServiceProxy servicePoxy;
private Guid accountId;
private Entity account;
public DynamicsCommunication()
{
credentials = new ClientCredentials();
credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
servicePoxy = new OrganizationServiceProxy(OrganizationUri, null, credentials, null);
accountId = Guid.Empty;
}
public string UpdateDynamics(AgentTransmission agt)
{
switch (DeterminAction(agt))
{
case DynamicsAction.Create:
return CreateAgency(agt);
case DynamicsAction.Update:
return UpdateAgency(agt);
default:
return string.Empty;
}
}
private string CreateAgency(AgentTransmission agt)
{
try
{
//Exception is thrown after this command
accountId = servicePoxy.Create(CreateAccount(agt));
if (accountId != Guid.Empty)
{
return string.Empty;
}
else
{
return "error creating agency";
}
}
catch (ODataException oEx)
{
string s = oEx.Message;
throw;
}
catch (Exception ex)
{
string s = ex.Message;
throw;
}
}
private Entity CreateAccount(AgentTransmission agt)
{
account = new Entity();
account.Attributes.Add("LogicalName", "something");
account.Attributes.Add("name", agt.AgencyName);
account.Attributes.Add("telephone1", agt.BusinessPhone.Replace("(","").Replace(")", "").Replace("-", ""));
account.Attributes.Add("address1_line1", agt.MailingStreet1);
account.Attributes.Add("address1_city", agt.MailingCity);
account.Attributes.Add("address1_postalcode", agt.MailingZip);
account.Attributes.Add("neu_address1stateprovince", 1); //1 for Mailing
account.Attributes.Add("neu_channelid", LookupChannelId(agt.Channel));
account.Attributes.Add("neu_appointementstatus", "279660000");
account.Attributes.Add("customertypecode", LookupCustomerCode(agt.RelationshipType));
account.Attributes.Add("neu_taxid", UnobfuscateRef(agt.ReferenceNumber));
return account;
}
}
Set the name of the CRM entity on the LogicalName property of the Entity object rather than adding it to the attribute collection
account = new Entity("your_entity_name");
or
account = new Entity();
account.LogicalName = "your_entity_name";
I have this databases: table<User>(UserID,Name,Surname,Username,Password,Email), table<Role>(RoleID,RoleName,Description), and table<UsersInRole>(UserID,RoleID). I create a login authentication with username and password to access to the application (with Linq ToSql to store data), and it is right.
Now I wish to create a role for each user, but I don't know how work out it; I saw some features about it but it refers to web.app.
This is the code of the procedure that applies to login:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
public bool ValidateApplicationUser(string userName, string password)
{
{
var AuthContext = new DataClasses1DataContext();
var query = from c in AuthContext.Users
where (c.Username == userName.ToLower() && c.Password == password.ToLower())
select c;
if(query.Count() != 0 )
{
return true;
}
return false;
}
}
private void mahhh(object sender, RoutedEventArgs e)
{
bool authenticated = true;
{
if (usernameTextBox.Text !="" && passwordTextBox.Text != "")
{
authenticated = ValidateApplicationUser(usernameTextBox.Text , passwordTextBox.Text);
}
}
if (!authenticated)
{
MessageBox.Show("Invalid login. Try again.");
}
else
{
MessageBox.Show("Congradulations! You're a valid user!");
Window3 c = new Window3();
c.ShowDialog();
this.Close();
}
}
}
I don't know how to implement a method to assign a role to the user.
Do you have any idea or suggest to make it right?
First, try not to store passwords in the database; it is better to store a hash. I'm not quite sure what you mean "assign a role to the user" - are you having difficulty getting the role from the db? Or are you unsure what to do with it afterwards? If the latter, the "principal" is the way to go; at the simplest level:
string username = ...
string[] roles = ...
Thread.CurrentPrincipal = new GenericPrincipal(
new GenericIdentity(username), roles);
Now you can use role-based security, either declarative or imperative.
Declarative:
[PrincipalPermission(SecurityAction.Demand, Role="ADMIN")]
public void Foo()
{ // validated automatically by the .NET runtime ;-p
}
Imperative:
static bool IsInRole(string role)
{
IPrincipal principal = Thread.CurrentPrincipal;
return principal != null && principal.IsInRole(role);
}
...
bool isAdmin = IsInRole("ADMIN");