Custom MSBuild Task and capturing Command Line Output

I recently had a need to “capture the output” of a command line tool, but within a MSBuild Custom Task (context).

While I know there are “msbuild’ish” ways to accomplish the below example (getting a list of directories from a “dir” command), the below is an ~~example~~ of how to capture the output of a command line call.

What am I talking about?

Well, for example, if run the command (from a command line prompt):

dir “c:\”

You would see (in the command line window) something like the below:

C:\>dir “c:\”

Volume in drive C has no label.
Volume Serial Number is JER-33_3

Directory of c:\

10/15/2012 01:20 PM <DIR> Program Files
11/08/2012 02:29 PM <DIR> Program Files (x86)
08/16/2012 02:23 PM <DIR> Users
11/08/2012 02:08 PM <DIR> Windows

0 File(s) 0 bytes
4 Dir(s) 33,490,378,752 bytes free

C:\>

So how can I “capture” the output while inside of a custom MSBuild Task?

First.  I didn’t figure this out on my own.  I googled and bing searched by rumpus off.  And then I came across an example.

https://msbuildextensionpack.svn.codeplex.com/svn/Solutions/Main3.5/Framework/Framework/CommandLine.cs

namespace MSBuild.ExtensionPack.Framework
{
[HelpUrl("http://www.msbuildextensionpack.com/help/3.5.12.0/html/324b0e31-5ff0-baac-40ae-bf26297e5821.htm")]
public class CommandLine : Task
{
//not seen stuff here
}
}

And I totally ripped their code.

But what I did do is package this up in a smaller (more digestable?) example as seen below.
And I remember when I first started with writing my own custom MSBuild tasks that what is obvious to me now, was not obvious when I first started.

So, in a nutshell:
“CollectedOutput” will be the property that has all the contents for the ‘dir “c:\”‘.

The CSharp code will need to be put into a “Class Library” DotNet csproj of course.
If you want the example to run “out of the box”, then name the new csproj with the name “GranadaCoder.Framework.CrossDomain.MSBuild” (If you don’t do this, you’ll need to adjust the name of the .dll mentioned in the .msbuild file).
After compiling, place the .dll in the same folder as the .msbuild and .bat file.

//START CSharp Code//

namespace GranadaCoder.Framework.CrossDomain.MSBuild.Tasks.Temp
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.ComponentModel;
using System.Reflection;

using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.Diagnostics;

public class CommandLineDirectoryTask : ToolTask
{

private static readonly string WINDIR_ENVIRONMENT_VARIABLE = “windir”;
private static readonly string CMD_RUNTIME_EXE = “cmd.exe”;
private static readonly string COMMAND_LINE_SWITCH_C_DIR = “/c dir”;

public CommandLineDirectoryTask()
{
base.ToolExe = CMD_RUNTIME_EXE; // see http://msdn.microsoft.com/en-us/library/microsoft.build.utilities.tooltask.toolexe.aspx
this.ToolPath = this.ReadSystem32EnvironmentVariable();
}

[Required]
public string DirectoryNameToList { get; set; }

/// <summary>
/// Gets or sets the collected output from the command-line.
/// </summary>
///
[Output]
public string CollectedOutput { get; set; }

/// <summary>
/// Gets or sets the StdErr stream encoding. Specifies the encoding of the captured task standard error stream.
/// The default is the current console output encoding.
/// </summary>
/// <remarks>Exec Equivalent: StdErrEncoding</remarks>
[Output]
public string StdErrEncoding { get; set; }

/// <summary>
/// Gets or sets the StdOut stream encoding. Specifies the encoding of the captured task standard output stream.
/// The default is the current console output encoding.
/// </summary>
/// <remarks>Exec Equivalent: StdOutEncoding</remarks>
[Output]
public string StdOutEncoding { get; set; }

/// <summary>
/// Gets the name of the tool.
/// </summary>
/// <value>The name of the tool.</value>
protected override string ToolName
{
get { return base.ToolExe; }
}

/// <summary>
/// Generates the full path to tool.
/// </summary>
/// <returns></returns>
protected override string GenerateFullPathToTool()
{
return Path.Combine(ToolPath, ToolName);
}

/// <summary>
/// Executes this instance.
/// </summary>
/// <returns></returns>
public override bool Execute()
{
//return base.Execute();
return InternalExecute();
}

private bool InternalExecute()
{

string command = this.GenerateFullPathToTool();
string commandLineArguments = GenerateSvnExportCommandLineArguments();

this.Log.LogMessage(“Execute: {0} {1}”, command, commandLineArguments);
ProcessStartInfo startInfo = this.GetCommandLine(command, commandLineArguments, this.DirectoryNameToList, this.StdErrEncoding, this.StdOutEncoding);
using (Process process = Process.Start(startInfo))
{
//this.Log.LogMessage(“Collect Standard Output Stream”);

while (!process.StandardOutput.EndOfStream || !process.HasExited)
{
this.CollectOutputLine(process.StandardOutput.ReadLine());
}

//this.Log.LogMessage(“Collect Standard Error Stream”);
while (!process.StandardError.EndOfStream)
{
this.CollectOutputLine(process.StandardError.ReadLine());
}

}

return true;

}

/// <summary>
/// Gets a command process object with the command specified.
/// </summary>
/// <param name=”command”>The command to execute</param>
/// <param name=”workingDirectory”>The command working directory</param>
/// <param name=”standardErrorEncoding”>The standard error stream encoding</param>
/// <param name=”standardOutputEncoding”>The standard output stream encoding</param>
/// <returns>Returns a command prompt start information that is ready to start</returns>
/// <remarks>StdErr and StdOut are always redirected.</remarks>
private ProcessStartInfo GetCommandLine(string command, string commandLineArguments, string workingDirectory, string standardErrorEncoding, string standardOutputEncoding)
{
var process = new ProcessStartInfo
{
FileName = command,
Arguments = commandLineArguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
ErrorDialog = false
};

if (!string.IsNullOrEmpty(standardErrorEncoding))
{
try
{
process.StandardErrorEncoding = Encoding.GetEncoding(standardErrorEncoding);
}
catch (ArgumentException ex)
{
this.Log.LogMessage(“Non-fatal exception caught: invalid encoding specified for standard error stream: {0}”, standardErrorEncoding);
this.Log.LogWarningFromException(ex);
}
}

if (!string.IsNullOrEmpty(standardOutputEncoding))
{
try
{
process.StandardOutputEncoding = Encoding.GetEncoding(standardOutputEncoding);
}
catch (ArgumentException ex)
{
this.Log.LogMessage(“Non-fatal exception caught: invalid encoding specified for standard output stream: {0}”, standardOutputEncoding);
this.Log.LogWarningFromException(ex);
}
}

if (!string.IsNullOrEmpty(workingDirectory))
{
if (Directory.Exists(workingDirectory))
{
process.WorkingDirectory = workingDirectory;
}
else
{
this.Log.LogWarning(“Non-fatal input error: provided working directory does not exist: {0}”, workingDirectory);
}
}

return process;
}

/// <summary>
/// Generates the command line commands.
/// </summary>
/// <returns></returns>
protected string GenerateSvnExportCommandLineArguments()
{
StringBuilder builder = new StringBuilder();
AppendIfPresent(builder, CommandLineDirectoryTask.COMMAND_LINE_SWITCH_C_DIR, QuoteItUp(this.DirectoryNameToList));
Log.LogCommandLine(builder.ToString());
return builder.ToString();
}

/// <summary>
/// Collects a line of output.
/// </summary>
/// <param name=”text”>The text line</param>
private string CollectOutputLine(string text)
{
text = string.IsNullOrEmpty(text) ? null : text.Trim();
if (!string.IsNullOrEmpty(text))
{
this.CollectedOutput += Environment.NewLine + text;
this.Log.LogMessage(text);
}

return text;
}

/// <summary>
/// Append the command and argument only if the argument exists. Repped from the internet at http://www.zorched.net/2009/01/08/msbuild-task-for-partcover/
/// </summary>
/// <param name=”builder”>The containing string builder.</param>
/// <param name=”cmdArg”>The command argument switch.</param>
/// <param name=”value”>The value of the command argument.</param>
protected static void AppendIfPresent(StringBuilder builder, string cmdArg, string value)
{
if (!String.IsNullOrEmpty(value))
{
builder.AppendFormat(“{0} {1} “, cmdArg, value);
}
}

/// <summary>
/// Wrap quotes around a string and escape charcters if needed.
/// </summary>
/// <param name=”builder”>The containing string builder.</param>
protected static string QuoteItUp(string args)
{
if (String.IsNullOrEmpty(args))
{
return args;
}

bool alreadyHasQuoteBookEnds = false;

if (args.StartsWith(“\””) && args.EndsWith(“\””))
{
alreadyHasQuoteBookEnds = true;
}

// Escape internal quotes if any
if (args.Contains(“\””))
{
args = args.Replace(“\””, “\\\””);
}

if (!alreadyHasQuoteBookEnds)
{
// quote string
args = String.Format(“\”{0}\””, args);
}
return args;
}

private string ReadSystem32EnvironmentVariable()
{
string returnValue = string.Empty;

if (!String.IsNullOrEmpty(System.Environment.GetEnvironmentVariable(WINDIR_ENVIRONMENT_VARIABLE)))
{
returnValue = System.Environment.GetEnvironmentVariable(WINDIR_ENVIRONMENT_VARIABLE);
returnValue = Path.Combine(returnValue, “System32″);
}
else
{
base.Log.LogWarning(string.Format(“The ‘{0}’ Environment Variable was not available. You will need to manually set the ToolPath value”, WINDIR_ENVIRONMENT_VARIABLE));
}

return returnValue;
}
}
}

//END CSHARP Code

—————————————

//Start file “CommandLineDirectoryTaskTest.msbuild”

<?xml version=”1.0″ encoding=”utf-8″?>
<Project DefaultTargets=”AllTargetsWrapper” xmlns=”http://schemas.microsoft.com/developer/msbuild/2003″&gt;
<!– –>
<PropertyGroup>
<WorkingDirectory>.</WorkingDirectory>
</PropertyGroup>
<!– –>
<PropertyGroup>
<GranadaCoderMSBuildFileName Condition=”Exists(‘$(WorkingDirectory)\MSBuildHelpers\GranadaCoder.Framework.CrossDomain.MSBuild.dll’)”>$(WorkingDirectory)\MSBuildHelpers\GranadaCoder.Framework.CrossDomain.MSBuild.dll</GranadaCoderMSBuildFileName>
<GranadaCoderMSBuildFileName Condition=”Exists(‘$(WorkingDirectory)\GranadaCoder.Framework.CrossDomain.MSBuild.dll’)”>$(WorkingDirectory)\GranadaCoder.Framework.CrossDomain.MSBuild.dll</GranadaCoderMSBuildFileName>
<GranadaCoderMSBuildFileName Condition=”$(GranadaCoderMSBuildFileName)==””>CannotFind_GranadaCoder.Framework.CrossDomain.MSBuild.dll</GranadaCoderMSBuildFileName>
</PropertyGroup>
<!– –>
<!– –>
<UsingTask AssemblyFile=”$(GranadaCoderMSBuildFileName)” TaskName=”CommandLineDirectoryTask”/>
<!– –>
<!– –>
<Target Name=”AllTargetsWrapper”>
<CallTarget Targets=”CommandLineDirectoryTask1″ />
</Target>
<!– –>
<!– –>
<Target Name=”CommandLineDirectoryTask1″>

<CommandLineDirectoryTask DirectoryNameToList=”C:\wutemp\”>
<Output TaskParameter=”CollectedOutput” PropertyName=”MyCollectedOutput1″/>
</CommandLineDirectoryTask>

<Message Text=”The CollectedOutput1 is $(MyCollectedOutput1)”/>
<Message Text=” “/>
<Message Text=” “/>
<Message Text=” “/>
<Message Text=” “/>

<CommandLineDirectoryTask ContinueOnError=”true” DirectoryNameToList=”C:\work3\” >
<Output TaskParameter=”ExitCode” PropertyName=”MyErrorCode”/>
<Output TaskParameter=”CollectedOutput” PropertyName=”MyCollectedOutput2″/>
</CommandLineDirectoryTask>

<Message Text=”The CollectedOutput2 is $(MyCollectedOutput1)”/>
<Message Text=” “/>
<Message Text=” “/>
<Message Text=”The exit code (MyErrorCode) is $(MyErrorCode)”/>

</Target>

</Project>

//End file “CommandLineDirectoryTaskTest.msbuild”

—————————————

//Start CommandLineDirectoryTaskTest.bat file
set msBuildDir=%WINDIR%\Microsoft.NET\Framework\v3.5
set msBuildDir=%WINDIR%\Microsoft.NET\Framework\v2.0.50727
set msBuildDir=%WINDIR%\Microsoft.NET\Framework\v3.5

call %msBuildDir%\msbuild /target:AllTargetsWrapper “CommandLineDirectoryTaskTest.msbuild” /p:Configuration=Release;FavoriteFood=Popeyes /l:FileLogger,Microsoft.Build.Engine;logfile=CommandLineDirectoryTaskTest_AllTargetsWrapped.log

set msBuildDir=

//End CommandLineDirectoryTaskTest.bat file

Posted in Software Development | Leave a comment

How to find References in a C# Project File (.csproj) using LINQ (Xml)

I was asked to “Generate a report of all our c# projects and their dependencies.

:<

So instead of going through 333+ csprojs manually, it was time to write a helper routine to look through the .csproj xml.

And instead of old-school XPath and SelectNodes() (boooo), it was time to write some Linqy-Dinq.

The below code will query a csproj file and report back all all project references. 

It will report back:

(1) If the reference is an already compiled dll.  Not in the GAC.
(2) If the reference is an already compiled dll.  In the GAC.
(3) If the reference is a reference “by project”.

Feel free to post any suggestions and/or improvements.

The biggest trick was adding the namespace to the conditional clauses. ( ns + “SomeElementName” ).
The other trick was figuring out how to ignore Xml-Element(s) if there was no value.
Oh, it is so simple when it is done.  But took a while to figure out the nuances.

//using System.Xml.Linq;

string fileName = @”C:\MyFolder\MyProjectFile.csproj”;

XDocument xDoc = XDocument.Load(fileName);

XNamespace ns = XNamespace.Get(http://schemas.microsoft.com/developer/msbuild/2003&#8243;);


//References “By DLL (file)”

var list1 = from list in xDoc.Descendants(ns + “ItemGroup”)
from item in list.Elements(ns + “Reference”)

/* where item.Element(ns + “HintPath”) != null */ /* optional */

select new
{
CsProjFileName = fileName,
ReferenceInclude = item.Attribute(“Include”).Value,
RefType = (item.Element(ns + “HintPath”) == null) ? “CompiledDLLInGac” : “CompiledDLL”,
 HintPath = (item.Element(ns + “HintPath”) == null) ? string.Empty : item.Element(ns + “HintPath”).Value};

foreach  (var v in list1)
{
    Console.WriteLine(v.ToString());
}

//References “By Project”
var list2 = from list in xDoc.Descendants(ns + “ItemGroup”)
from item in list.Elements(ns + “ProjectReference”)

where
item.Element(ns + “Project”) != null

select new
{
CsProjFileName = fileName,
ReferenceInclude = item.Attribute(“Include”).Value,
RefType = “ProjectReference”,
ProjectGuid = item.Element(ns + “Project”).Value
};

foreach (var v in list2)
{
    Console.WriteLine(v.ToString());
}

Posts that helped me:

http://stackoverflow.com/questions/2338512/understanding-linq-to-xml-descendants-return-no-results

http://stackoverflow.com/questions/2630192/c-sharp-check-an-element-exists-while-using-linq-to-xml

Bonus Code:

string startFolder = @”C:\DotNetCodeBase\”;

System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(startFolder);

IEnumerable<System.IO.FileInfo> fileList = dir.GetFiles(“*.csproj”, System.IO.SearchOption.AllDirectories);

IEnumerable<System.IO.FileInfo> fileQuery = from file in fileList
    where file.Extension == “.csproj”
    orderby file.Name
select file;

 

Posted in Software Development | Leave a comment

Create a COM+ Application with Powershell

$comAdmin = New-Object -comobject COMAdmin.COMAdminCatalog
$apps = $comAdmin.GetCollection(“Applications”)
$apps.Populate();

$newComPackageName = “MyFirstCOMPackage”

$appExistCheckApp = $apps | Where-Object {$_.Name -eq $newComPackageName}

if($appExistCheckApp)
{
$appExistCheckAppName = $appExistCheckApp.Value(“Name”)
“This COM+ Application already exists : $appExistCheckAppName”
}
Else
{
$newApp1 = $apps.Add()
$newApp1.Value(“Name”) = $newComPackageName
$newApp1.Value(“ApplicationAccessChecksEnabled”) = 0 <# Security Tab, Authorization Panel, “Enforce access checks for this application #>

<# See http://msdn.microsoft.com/en-us/library/ms686107(v=VS.85).aspx#identity for full documentation. #>

<# Optional (to set to a specific Identify) #>
$newApp1.Value(“Identity”) = “MyDomain\myUserName”
$newApp1.Value(“Password”) = “myPassword”

$saveChangesResult = $apps.SaveChanges()
“Results of the SaveChanges operation : $saveChangesResult”
}

Full documentation of the properties here.

http://msdn.microsoft.com/en-us/library/ms686107(v=VS.85).aspx#applicationaccesschecksenabled

I’m a C# developer.  But Powershell rocks the suburbs for alot of tasks!

Posted in Software Development | Leave a comment

Hello world!

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!

Posted in Uncategorized | 1 Comment

Bug in Documentation : Microsoft Access Database Engine 2010 Redistributable

Microsoft Access
Database Engine 2010 Redistributable

http://www.microsoft.com/downloads/en/details.aspx?familyid=C06B8369-60DD-4B64-A44B-84B371EDE16D&displaylang=en

There is a bug in the documentation at the download page.

The documentation says:
1.If you are the user of an application, consult your application
documentation for details on how to use the appropriate driver.

2.If
you are an application developer using OLEDB, set the Provider argument
of the ConnectionString property to “Microsoft.ACE.OLEDB.12.0”
    If
you are connecting to Microsoft Office Excel data, add “Excel 14.0” to
the Extended Properties of the OLEDB connection string.

The "Excel 14.0" is the issue.

It should be "Excel 12.0".

Her are a few connection strings to provide full context.

// Old School Jet, been around for a while
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=’C:\MyOldSchoolFile.xls’;Extended Properties=’Excel 8.0;HDR=NO;IMEX=1;’;"

//Newer version with xslx
"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=’C:\MyXlsXFile.xlsx’;Extended Properties=’Excel 12.0 Xml;HDR=NO;IMEX=1;’;"

//Newer version, any xls file
"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=’C:\AlmostAnyExcelVersionFileRunningUnder64BitOS.xls’;Extended Properties=’Excel 12.0;HDR=NO;IMEX=1;’;"

Don’t take the above as absolute truth.  But it should draw attention to the issue (if you’re experiencing it), and help with some more google (errr. bing) searches.

Here are some other phrases which might lead you here.

The OLE DB provider "Microsoft.Jet.OLEDB.4.0" has not been registered
    ( Trying to read an excel file on a 64 bit O/S?  The purple string above should work for you under a 64 bit O/S. )

"Could not find installable ISAM"     
    ( This might show up because of the "14.0" vs "12.0" bug mentioned above )

See also:
http://stackoverflow.com/questions/2075054/upload-an-excel-file-in-classic-asp-on-windows-2003-x64-using-office-2010-drivers/

http://social.msdn.microsoft.com/Forums/en/adodotnetdataproviders/thread/686d8ebb-0da3-4f0c-bf16-9c650f8dcb32

http://www.connectionstrings.com/excel-2007

Posted in Software Development | 6 Comments

RANT : Hard Coded Security Roles

:::::::::::sigh:::::::::::::

If I come across one more hard coded security roles brownfield application, I think I’m gonna throw my chair out the window.

Today, I came across an application that does type-checking to determine security.

The code goes something like this:

                        AbstractUser user = SomeGetUserMethod("myname", "mypassword");
                       /* the above method returns 1 of a few concrete classes which implement AbstractUser */

                        // type-checking time!!
                        if (user.GetType() == typeof(BAL.Domain.AdminUser))
                        {
                            MyWebPage.btnDeleteAllEmployees.Visible = true;
                            MyWebPage.btnUpdateMyOwnProfile.Visible = true;
                            MyWebPage.btnLogOut.Visible = true;
                        }

                        if (user.GetType() ==
typeof(BAL.Domain.NormalUser))
                        {
                            MyWebPage.btnDeleteAllEmployees.Visible = false;
                            MyWebPage.btnUpdateMyOwnProfile.Visible =
true;
                            MyWebPage.btnLogOut.Visible = true;
                        }

or maybe you have seen (bool representing 1 of a few hard coded roles)

                        bool isAdmin = SomeMethodToFigureOutHardCodedRoles();
                        bool isNormalUser =
SomeMethodToFigureOutHardCodedRoles();
                        if (isAdmin)
                        {
                            MyWebPage.btnDeleteAllEmployees.Visible =
true;
                            MyWebPage.btnUpdateMyOwnProfile.Visible =
true;
                            MyWebPage.btnLogOut.Visible = true;
                        }
                        if (isNormalUser)

                        {

                            MyWebPage.btnDeleteAllEmployees.Visible =false ; // NormalUser cannot do this! So hide the button.

                            MyWebPage.btnUpdateMyOwnProfile.Visible =
true;

                            MyWebPage.btnLogOut.Visible = true;

                        }

You know the drill. 

And the pre-project-starts-to-be-constructed discussion goes something like this:
"Today, we have 3 roles, lets base all our security off those 3 (hard coded) roles.  Those roles will ~~never~~ change.

And my thoughts on a finite set of hard coded Role(s).
That’s fine for your kid’s soccer club fan page.
That is NOT fine for a professional DotNet developer creating a business application.

I’ve written an example on how NOT do it (so you can see the pattern clearly).

The problem will always end up being that the 3 (N) number of roles will never be sufficient.
A DAY WILL ARRIVE WHEN THE BUSINESS OWNERS WANT A SLIGHTLY DIFFERENT ROLE.
They’ll say that want to "tweak" an existing role, but what they really mean is that they want a new role that is very close to an existing role.
But "a tad bit different" is still different.
But you (or your "architect" if you want to blame someone else) did not account for this at the beginning of the project.

You can check out:
http://www.lhotka.net/weblog/CommentView,guid,9efcafc7-68a2-4f8f-bc64-66174453adfd.aspx
for a discussion.
I can’t blame just the developer(s).  Microsoft and its "super easy p-easy" ".IsInRole()" method helped propagate this ugliness.
And then this kind of code:
[PrincipalPermission(SecurityAction.Demand,Role="Teller")]

And I completely agree with the article above and its assertion "At runtime, when the user is actually using the application, the roles are entirely meaningless".
Listen people (aka all you developers) ..  software cares about permissions (or "rights").  Stop coding to roles, start coding to permissions (or rights).

The article above tackles the issue using the existing (available) objects in DotNet.

Here is my custom IPrincipal solution, if the above workaround rubs you the wrong way.
While I have "Role" methods, I never use them ***, except for the AllRoles collection, which I use to show humans what role they are in.
However, I never show just the Roles, I show the Permissions/Rights, because that is more important.

    public interface IRolesAndRightsPrincipal : System.Security.Principal.IPrincipal
    {

        bool IsInRole(System.Guid role);

        bool IsInAnyRole(System.Guid[] roles);

        bool IsInAllRoles(System.Guid[] roles);

        bool HasRight(System.Guid right);

        bool HasAnyRight(System.Guid[] rights);

        bool HasAllRights(System.Guid[] rights);
       
        ISecurityRoleCollection AllRoles
        {
            get;
        }

        ISecurityRightCollection AllRights
        {
            get;
        }
    }

(*** The one place I might use them (though I never have) is backwards compatibility when refactoring an existing application.  If the current application is "all roled up" then that would be a stepping stone to getting to permissions/rights based security.)

You might be saying "What is the HasAnyRight method all about?"

Well, take for instance that you have a menu link called "Manage Employees".
This link will take you to a separate page that allows you to ADDNEW, UPDATE, and/or DELETE an employee.
(These are 3 distinct permission(s)/rights(s)).

So how do you decide if you should show this menu link called "Manage Employees", since its not based on a single permission/right.

And there ya go:  Use the HasAnyRight() method
menuItemManageEmployees.Visible = customPrinc.HasAnyRight ( /* throw the Guids here which represent ADDNEW, UPDATE, DELETE */ );
You show the link if the use has one (any) of the 3 permissions/rights.
Then when you get to that new page, you show buttons/links/etc based on the individual permissions/rights.

Side note, my concrete IRolesAndRightsPrincipal ( adaptly named "RolesAndRightsPrincipal" takes all your roles,rights in its constructor, and then becomes a look-up holder from that point on.  Obviously, if you do on the fly permissions/rights changes, you’ll have to refresh it.
Currently, I just go with a "You gotta re-login to get your fresh permissions/rights", since the project (almost) never takes away any permissions/rights and seldom changes them.  But you’ll have to conquer that design decision on your own.

:::::::::Call to all Developers::::::::::::

If you’re developing a new project, and starting out with N number of hard coded roles, please stop.  Please stop.  I’m begging you, please stop.

If you’re in a brownfield application, then ask your manager for some time to re-factor the security.
If your manager is fair, then he/she will find some time for you that does not involve late nights or weekends.
If your manager thinks re-factoring (without scope change) is a waste of time since "it works, so its OK", then find a new job.

This oldie-but-goodie article has the basic meat of a good solution.
http://www.codeproject.com/KB/security/cgsecurity.aspx
I would take his DDL, update it to your standards, and "framework up" his "Managers"(.cs code).

It is OK to make a Role, but ONLY in the sense to logically group a set of permissions/rights.
You should be checking permission(s)/right(s) when you actually are interested in the question
                 "Can this IIdentity perform this certain thing?"

Please stop coding hard-coded-roles into your application(s). 
The developers who have to maintain your code after you’ve left will thank you.
..

I have also posted an "Anti Example" C# code example here.  (<<Right-Click , "Save As" usually works best).

//End Rant

Posted in Software Development | 2 Comments

CruiseControl.NET / header.xsl / and DiskSpace

I had a nice situation today.
After a code check-in to SVN, CruiseControl.NET reported an error.

The culprit??

No more disk space.

The error is seen below.  "Out of space" isn’t mentioned, thus why I am posting this.
But it makes sense, the xsl transformation didn’t have enough disk space to handle the transform output.

There
was an exception trying to carry out your request.

Exception Message

Unable to execute transform: C:\Program Files
(x86)\CruiseControl.NET\webdashboard\xsl\header.xsl
(or)
Unable to execute transform: C:\Program Files\CruiseControl.NET\webdashboard\xsl\header.xsl

Exception Full Details

ThoughtWorks.CruiseControl.Core.CruiseControlException:
Unable to execute transform: C:\Program Files
(x86)\CruiseControl.NET\webdashboard\xsl\header.xsl —>
System.Xml.XmlException: Unexpected end of file while parsing CDATA has
occurred. Line 384, position 106.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.ParseCDataOrComment(XmlNodeType type,
Int32& outStartPos, Int32& outEndPos)
at System.Xml.XmlTextReaderImpl.ParseCDataOrComment(XmlNodeType type)
at System.Xml.XmlTextReaderImpl.ParseElementContent()
at System.Xml.XPath.XPathDocument.LoadFromReader(XmlReader reader,
XmlSpace space)
at System.Xml.XPath.XPathDocument..ctor(TextReader textReader)
at
ThoughtWorks.CruiseControl.Core.Util.XslTransformer.Transform(String
input, String xslFilename, Hashtable xsltArgs)
— End of inner exception stack trace —
at
ThoughtWorks.CruiseControl.Core.Util.XslTransformer.Transform(String
input, String xslFilename, Hashtable xsltArgs)
at
ThoughtWorks.CruiseControl.Core.Util.HtmlAwareMultiTransformer.Transform(String
input, String[] transformerFileNames, Hashtable xsltArgs)
at
ThoughtWorks.CruiseControl.WebDashboard.Dashboard.PathMappingMultiTransformer.Transform(String
input, String[] transformerFileNames, Hashtable xsltArgs)
at
ThoughtWorks.CruiseControl.WebDashboard.Dashboard.BuildRequestTransformer.Transform(IBuildSpecifier
buildSpecifier, String[] transformerFileNames, Hashtable xsltArgs)
at
ThoughtWorks.CruiseControl.WebDashboard.Dashboard.Actions.MultipleXslReportBuildAction.Execute(ICruiseRequest
cruiseRequest)
at
ThoughtWorks.CruiseControl.WebDashboard.MVC.Cruise.ServerCheckingProxyAction.Execute(ICruiseRequest
cruiseRequest)
at
ThoughtWorks.CruiseControl.WebDashboard.MVC.Cruise.BuildCheckingProxyAction.Execute(ICruiseRequest
cruiseRequest)
at
ThoughtWorks.CruiseControl.WebDashboard.MVC.Cruise.ProjectCheckingProxyAction.Execute(ICruiseRequest
cruiseRequest)
at
ThoughtWorks.CruiseControl.WebDashboard.MVC.Cruise.CruiseActionProxyAction.Execute(IRequest
request)
at
ThoughtWorks.CruiseControl.WebDashboard.MVC.Cruise.CachingActionProxy.Execute(IRequest
request)
at
ThoughtWorks.CruiseControl.WebDashboard.MVC.Cruise.ExceptionCatchingActionProxy.Execute(IRequest
request)
Posted in Software Development | 2 Comments