Thursday, June 23, 2005

Role Based Security Notes

  • Impersontaing a User: 1) Setup the Platform Invoke DLL Function you'll be calling in step #2:
    [System.Runtime.InteropServices.DllImport("advapi32.dll")]
    public static extern bool LogonUser(
         String strUsername, 
         String strDomain, 
         String strPassword, 
         int intLogonType, 
         int intLogonProvider, 
         out IntPtr phToken);
    
    2) Retrieve the OS token for the user you want to impersonate:
    IntPtr phToken = IntPtr.Zero;
    LogonUser("username", "Domain", "password", 3, 0, out phToken);
    
    3) Create a new WindowsIdentity object and pass it the impersonation token:
    WindowsIdentity ImpersonatedIdentity = new WindowsIdentity(phToken);
    
    4) Impersonate the identity:
    WindowsImpersonationContext MyImpersonation = ImpersonatedIdentity.Impersonate();
    
    4) When impersonation is no longer needed, end it:
    MyImpersonation.Undo();
    

Monday, June 13, 2005

C# Notes

  • Operator Overloading: the operator must be defined in the same type on which it performs the operation.
    private void btnStart_Click(object sender, System.EventArgs e)
    {
     MyClass myClass1 = new MyClass();
     MyClass myClass2 = new MyClass();
     myClass1.MyStringField = "Hello ";
     myClass2.MyStringField = "World!";
     // Displays: Hello World!
     MessageBox.Show((myClass1 + myClass2).MyStringField);
     // Displays: Hello 1
     MessageBox.Show((myClass1 + 1).MyStringField);
    }
    
    
    public class MyClass 
    {
     public String MyStringField = "";
     public static MyClass operator + (MyClass myClass1, MyClass myClass2) 
     {
      MyClass myClass = new MyClass();
      myClass.MyStringField = myClass1.MyStringField + myClass2.MyStringField;
      return myClass;
     }
     public static MyClass operator + (MyClass myClass, int Number) 
     {
      myClass.MyStringField += Number.ToString();
      return myClass;
     }
    }
    
  • Making sure only an interface type can access an interface's members:
      private void btnStart_Click(object sender, System.EventArgs e)
      {
       // MyMethod() not accessible from mc:
       MyClass mc = new MyClass();
       // MyMethod() is accessible from imc:
       IMyClass imc = new MyClass();
       imc.MyMethod();
      }
    
      private interface IMyClass 
      {
       void MyMethod();
      }
    
      private class MyClass : IMyClass
      {
       // This method only accessible via a IMyClass type:
       void IMyClass.MyMethod() {}
      }
    

Tuesday, June 07, 2005

Setup and Deployment Notes

  • Installing New Instances of an Application via the Microsoft Installer (MSI): reset the ProductCode property of the Setup Project and then recompile the project.

Monday, June 06, 2005

Windows Forms Controls Notes

  • Properly Updating Data Grid Bound Values: If you ever run into a situation where you have a Data Grid bound to a Data Source and another control, such as a TextBox or ComboBox, and it's bound to the same Data Source and modifying the bound value of that control does not immediately cause the modified value to appear in the Data Grid, I have the reason why: validation. Normally, when a situation like this occurs, simply tabbing through the controls causes the modified value to appear in the bound Data Grid. That's because the validation events are raised which in return call the control's Refresh() method. Refresh() causes the bound control(s) to invalidate their area and redraw. This includes, in this example, any data within a Data Grid. In order for you to properly update a bound Data Grid's data whenever a bound control modifies it's values, you need to 1) first place the Focus() on the container that holds the Data Grid (a Form, GroupBox, etc.), 2) secondly, place the Focus() on the Data Grid itself and then 3) thirdly, call the Data Grid's Refresh() method.
  • Properly Handling KeyPress, KeyDown and KeyUp Events: always set the KeyPressEventArgs's Handled property to true whenever you want to AVOID the key pressed being sent to the application. For example, if you're checking a TextBox control to determine whether or not a user has entered a non-digit character and don't want that character to end up in the TextBox accordingly then set the Handled property to true:
      private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
      {
       if (! Char.IsDigit(e.KeyChar)) 
       {
        MessageBox.Show("No non-digit characters allowed.");
        this.textBox1.Undo();
        e.Handled = true; // Key press will not be sent!
       }
       else
        e.Handled = false;
      }
  • Tab Order: To enable this feature, 1) first select the form on which you want to set a tab order structure, 2) click on View and then Tab Order from the VS.NET menu and then 3) click on each control in the order in which you want tabbing flow to follow.

Saturday, June 04, 2005

Windows Forms User Assistance Notes

  • Providing Windows Forms Help via the HelpProvider: 1) Drag and drop the HelpProvider control onto the form in which you want to provide help, 2) in the properties tab of the HelpProvider control, click on the ... button for HelpNamespace and enter the path to a Help File (*.htm, *.chm, *.col), 3) select a control on the form for which you want to provide help and then set the ShowHelp property to true, 4) set the HelpKeyword property for the control to the name of the HTML page that pertains to this topic and then 5) set the HelpNavigator property to Topic. Note: after doing the preceding steps, select the control for which you want to see help and then press the F1 key. Set the HelpKeyword property to a keyword in the help file's Index and then set the HelpNavigator property to KeywordIndex to have the Index lookup the keyword specified. What you enter in HelpKeyword depends ENTIRELY on what you set the HelpNavigator property to.
  • Providing Windows Forms Pop-up Help via the HelpProvider: 1) Drag and drop the HelpProvider control onto the form in which you want to provide pop-up help, 2) select a control on the form for which you want to provide pop-up help and then set the ShowHelp property to true, 3) set the HelpString property to whatever you want the pop-up message to display, 4) select the parent form and then set HelpButton property to true and then 5) set the form's MaximizeBox and MinimizeBox to false. Note: after doing the preceding steps, an extra button on the control area of the title bar of the form with a question mark in it should appear: click on this button and then click on the control for which you want to display the associated pop-up help for.

Thursday, June 02, 2005

ADO.NET Notes

  • Accessing a Deleted DataRow's Data: can be accessed by retrieving it's DataRowVersion.Original values; typically, access to data is retrieved through the DataRow using it's DataRowVersion.Current values. Added, Modified and Unchanged DataRows can have their values accessed via their DataRowVersion.Current versions.

Wednesday, June 01, 2005

Code Access Security Notes

  • Licensing Classes Using LicFileLicenseProvider: 1) Add a LicenseProviderAttribute to the class (only classes are permitted) being protected by the file-based license:
    [System.ComponentModel.LicenseProviderAttribute(typeof(System.ComponentModel.LicFileLicenseProvider))]
    2) In the constructor of the class you want to protect, call the LicenseManager's Validate method:
    System.ComponentModel.LicenseManager.Validate(typeof(MyClass));
    3) Create a license file with the name of the format Namespace.Class.lic. Where Namespace.Class is the fully qualified class to be protected by the license. 4) In the license file, make sure the first line reads the following format: "Namespace.Class is a licensed component." (without the double-quotes). Care must be taken to ensure the exact sentence is written - even omitting the preiod at the end will cause the LicenseManager to throw an exception if it's not formatted perfectly.
  • SecurityAction.RequestOptional: isn't what it sounds like; requests the specified permission from the CLR and ALL others are implicitly refused; the specified permission is simply requested and not demanded (RequestMinimum). So, when running a Windows Forms application, [assembly: FileDialogPermission(SecurityAction.RequestOptional, Unrestricted=true)] will prevent the CLR from allowing the application to execute as ALL other permissions, including UIPermission, will be denied permission grants.
  • SecurityAction.RequestMinimum: demands that the CLR grant the specified permission to the application. If the run-time security policy disallows the specified permission, the application will not execute.
  • SecurityAction.RequestRefuse: the application lets the CLR know that the specified permission MUST NOT BE GRANTED ACCESS. If the CLR detected that the application is using the specified resource, it will now allow the application to execute.
  • SecurityAction.RequestOptional, SecurityAction.RequestMinimum and SecurityAction.RequestRefuse can ONLY be used at the assembly scope.
  • All permissions declaratively stated at the assembly scope are stored in the assembly's manifest.
  • Repeated Role-based Validation: Use the AppDomain.CurrentDomain's SetPrincipalPolicy method instead of the Thread's CurrentPrincipal property when consistent validation against the current principal is present. Supposedly, there's less overhead involved. The default is for the AppDomain to use the PrincipalPolicy.UnauthenticatedPrincipal policy.
    String strUsername = ""; AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); strUserName = System.Threading.Thread.CurrentPrincipal.Identity.Name); // strUsername will be 'DomainName\Username' AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.NoPrincipal); strUserName = System.Threading.Thread.CurrentPrincipal.Identity.Name); // A null reference exception will occur as no // principal object was automatically created AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.UnauthenticatedPrincipal); strUserName = System.Threading.Thread.CurrentPrincipal.Identity.Name); // strUsername will be '' (empty) even though an // IPrincipal object was automatically created
  • Single-use Role-based Validation: When you're only going to validate a user one time, don't use the AppDomain's SetPrincipalPolicy method. Instead, set the thread's CurrentPrincipal to an instance of an IPrincipal object..
    GenericPrincipal gp = new GenericPrincipal(new GenericIdentity("mikeg"), new String[] {"Developer" }); System.Threading.Thread.CurrentPrincipal = gp;
  • Replacing the IPrincipal Object: You will not have a problem replacing the thread's current principal with trusted code. However, semi-trusted code (Internet, Intranet zones, etc.) will cause a problem. In such a cases, make it known to the run-time that this is the permission you need for the semi-trusted code (the need to change the principal object from unmanaged code) by implementing the SecurityPermissionAttribute:..
    [assembly: System.Security.Permissions.SecurityPermissionAttribute(SecurityAction.RequestMinimum, ControlPrincipal = true)]
  • Validating the Current User: Use the PrincipalPermission object to validate the current thread's IPrincipal object/user.:
    //Declaratively: [System.Security.Permissions.PrincipalPermission(SecurityAction.Demand, Name = "Username", Role = "Role1")] // Imperatively: System.Security.Permissions.PrincipalPermission pp = new System.Security.Permissions.PrincipalPermission("Username", "Role"); pp.Demand();
  • Combining PrincipalPermission Objects: You can do this by creating two PrincipalPermission objects and joining them by using of the object's Union methods. The CLR uses an OR condition to determine whether a test succeeds. For example,.:
    // Current user/principal must be part of either of // the following two roles/groups: PrincipalPermission pp1 = new PrincipalPermission(null, "Administrators"); PrincipalPermission pp2 = new PrincipalPermission(null, "Domain Admins"); // Set the current user: GenericPrincipal gp1 = new GenericPrincipal(new GenericIdentity("MikeG"), new String[] { "Administrators" }); System.Threading.Thread.CurrentPrincipal = gp1; // User must be part of the Administrators group: pp1.Union(pp2).Demand(); // The above statement will succeed as the current // principal is part of the Administrators group.