Wednesday, May 8, 2013

Useful Knockout.js Debugging Nugget

A really short post, but this little template insert for knockout is incredibly useful when you’re not sure what’s going on inside your view model:

<div class="clear"><pre data-bind="text: ko.toJSON($data, null, 2)"></pre></div>

This snippet essentially outputs the contents of your view model as text on to your page.


What I like to do with things like this is add them to a Resharper live template – for me, whenever I’m inside my cshtml file, I just type “kodebug” and I get the above snippet.


Sometimes the simplest things are the most useful!!

Tuesday, April 30, 2013

.NET Membership Database – Hashing Plain Passwords in SQL

I had a requirement this week to take a load of plain passwords in the aspnet membership database, and hash them all so that they would continue to work with the membership provider.

My first iteration was a C# console app that ran over the membership provider and made it hash the passwords – this was slow and ran into to issues around password rules.

I then tried using a simple sql compute hash comand in sql server but this also failed – it turns out that the membership provider isn’t storing the salt as a string, but as a base 64 encoded string. It also seems that the hashed password is stored in base 64 encoded format. I finally got this sorted with a few functions and a single stored proc.

First a couple of utility functions to encode and decode base 64 strings:

CREATE FUNCTION [dbo].[fnBase64Decode] (@Base64Text VARCHAR(max)) RETURNS VARBINARY(max)
WITH SCHEMABINDING, RETURNS NULL ON NULL INPUT
BEGIN
DECLARE @x XML;
SET @x = @Base64Text
RETURN @x.value('(/)[1]', 'VARBINARY(max)')
END

GO

CREATE FUNCTION [dbo].[fnBase64Encode] (@Data VARBINARY(max)) RETURNS VARCHAR(max)
WITH SCHEMABINDING, RETURNS NULL ON NULL INPUT
BEGIN
RETURN ( SELECT [text()] = @Data FOR XML PATH('') )
END

GO

Next the function to create the hashed and salted password:


CREATE FUNCTION [dbo].[fnGetHashedPassword] 
(
@Password nvarchar(50),
@PasswordSalt NVARCHAR(128)
)
RETURNS NVARCHAR(150)
AS
BEGIN
DECLARE @Hash NVARCHAR(150)

DECLARE @EncodedSalt VARBINARY(MAX);
SET @EncodedSalt = Core.fnBase64Decode(@PasswordSalt);

DECLARE @EncodedPassword varbinary(max);
SET @EncodedPassword = CAST(@Password as VARBINARY(MAX));

SET @Hash = Core.fnBase64Encode(HASHBYTES('SHA1',@EncodedSalt+@EncodedPassword));

RETURN @Hash

END

And finally the stored procedure to generate new salts, and hash+salt every plain password in the membership database:


CREATE PROCEDURE [dbo].[spEncodePlainPasswords]

AS
BEGIN
SET NOCOUNT ON;

UPDATE dbo.aspnet_Membership
SET PasswordSalt = [dbo].fnBase64Encode(CONVERT(VARBINARY, CONVERT(NVARCHAR(100), NEWID())))
WHERE dbo.aspnet_Membership.PasswordFormat = 0

UPDATE dbo.aspnet_Membership
SET [Password] = [dbo].fnGetHashedPassword([Password], dbo.aspnet_Membership.PasswordSalt),
Passwordformat = 1
WHERE dbo.aspnet_Membership.PasswordFormat = 0

END

Friday, April 19, 2013

Deploying SQL CLR Assemblies as Part of Continuous Integration / Deployment

I’ve had to amend a continuous integration / deployment build process recently to include the deployment of SQL CLR assemblies. This was interesting to say the least – there are a few gotchas:

  • You cant ALTER a clr assembly if any functions (in that assembly) signatures change or are removed
  • You cant really drop and recreate an assembly that has schema bound dependencies (like computed columns)
  • Using SQL CREATE / ALTER with a file path is difficult due to permissions (I have to qualify this – it works very well, but for me I don’t want to be messing around with file system permissions whenever I spin up a new deployment environment)
  • You need to be careful if you have have multiple files linked to your assembly in SQL server

One thing I will say is that as much as you might read about the benefits of continuous integration (etc), there are a lot of development teams that don’t source control their databases and are not used to applying best practice in this area. When working with procedural code we tend to apply a lot of constraints to the way we work, but sql databases seem like a free for all! What this leads me to is that in order to have a successful CI setup with sql you must

APPLY CONSTRAINTS TO DATABASE USAGE

What I mean by this is apply some sensible constraints to say, never schema bind anything to CLR assembly functions, or always use synonyms for cross database activity. These are just a couple of ideas, but there doesn’t seem to be any conventional wisdom out there that I can find.

Deploying the Assembly

So the best way I can find to deploy an assembly in this situation is by hex encoding the file and passing it directly into a sql function. I found this GetHexString function on stack overflow which will get the hex string of an assembly:

static string GetHexString(string assemblyPath)
{
if (!Path.IsPathRooted(assemblyPath))
assemblyPath = Path.Combine(System.Environment.CurrentDirectory, assemblyPath);

var builder = new StringBuilder();
builder.Append("0x");

using (var stream = new FileStream(assemblyPath,
FileMode.Open, FileAccess.Read, FileShare.Read))
{
int currentByte = stream.ReadByte();
while (currentByte > -1)
{
builder.Append(currentByte.ToString("X2", CultureInfo.InvariantCulture));
currentByte = stream.ReadByte();
}
}

return builder.ToString();
}

Using this function, you can get the hex string you need and pass it to some sql to deploy your assembly. There are a few things that you need to be careful to drop any other files associated with your assembly (like pdb files that may have been introduced). I’ve also found that altering the assembly causes the least problems, but doesn’t always work. For this reason I use a function that looks for any associated pdb files and drops them if they exist, then tries to alter the assembly – if it fails it then tries to drop and recreate:


DECLARE @PdbName NVARCHAR(500) = @ClrAssemblyName + '.pdb'
DECLARE @DropFileSQL NVARCHAR(500) = 'ALTER ASSEMBLY [' + @ClrAssemblyName + '] DROP FILE ''' + @PdbName + ''''
DECLARE @AlterAssemblySQL NVARCHAR(MAX) = 'ALTER ASSEMBLY ['+ @ClrAssemblyName + '] FROM ' + @ClrAssemblyHex + ' WITH PERMISSION_SET = UNSAFE, UNCHECKED DATA'
DECLARE @DropAssemblySQL NVARCHAR(MAX) = 'DROP ASSEMBLY [' + @ClrAssemblyName + ']'
DECLARE @CreateAssemblySQL NVARCHAR(MAX) = 'CREATE ASSEMBLY [' + @ClrAssemblyName + '] FROM ' + @ClrAssemblyHex + ' WITH PERMISSION_SET = UNSAFE, UNCHECKED DATA'

BEGIN TRY
IF EXISTS (SELECT * FROM sys.assemblies AS a WHERE a.name = @ClrAssemblyName)
BEGIN
IF EXISTS (SELECT * FROM sys.assembly_files WHERE name = @PdbName)
EXEC sp_executesql @DropFileSQL

EXEC sys.sp_executesql @AlterAssemblySQL
END
END TRY
BEGIN CATCH

IF ERROR_NUMBER() IN (6270,6271,6272)
BEGIN
EXEC sp_executesql @DropAssemblySQL
EXEC sp_executesql @CreateAssemblySQL
END

IF NOT ERROR_NUMBER() IN (6285,6270,6271,6272)
BEGIN
declare @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int;
select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);
END
END CATCH

I usually save this sql snippet into source control somewhere within the database section, and pick this up as part of the database deployment – I then feed it into a SqlCommand with the required parameters:



  • @ClrAssemblyHex – the hex from the previous function

  • @ClrAssemblyName – the name of the assembly (without the .dll extension)

This is by no means perfect and could definitely be cleaned up a bit – but it gets the job done most of the time!


Other Issues


The main other issue that I encountered was that in order to drop and recreate the assembly, you need to drop any dependent clr functions, clr stored procs etc beforehand, and then recreate them afterwards. But thats for a different post!

Tuesday, April 16, 2013

TSQL Scripting a Full Text Catalog & Index

One thing that really annoys me in sql server management studio is that when I try to script out a full text catalog, it doesn’t include the full text index within it. The scripting out of sql server objects is an amazing feature, and maybe this is a little oversight, but it is frustrating nonetheless.

Changing a full text generally requires dropping and recreating the whole thing, choosing this option in sql server generally results in the following type of code, regardless of the contents of the full text catalog:

USE [DatabaseName]
GO

IF EXISTS (SELECT * FROM sys.fulltext_indexes fti WHERE fti.object_id = OBJECT_ID(N'[Schema].[TableName]'))
ALTER FULLTEXT INDEX ON [Schema].[TableName] DISABLE
GO

IF EXISTS (SELECT * FROM sys.fulltext_indexes fti WHERE fti.object_id = OBJECT_ID(N'[Schema].[TableName]'))
DROP FULLTEXT INDEX ON [Schema].[TableName]

GO

IF EXISTS (SELECT * FROM sysfulltextcatalogs ftc WHERE ftc.name = N'FullTextCatalogName')
DROP FULLTEXT CATALOG [FullTextCatalogName]
GO

USE [DatabaseName]
GO

CREATE FULLTEXT CATALOG [FullTextCatalogName] WITH ACCENT_SENSITIVITY = ON
AUTHORIZATION [dbo]

GO

This is great, but recreating this index will miss any of the individual index members you may have added previously.


One way to overcome this is to generate the adding of the index – the only way I’ve found to do this is as part of the adding process. For example:



  1. On your newly created full text catalog (without any contents), right click and go to properties
  2. Go to tables/views and add the required index, but DONT click OK
  3. Choose the “Script” button, and “Script Action to New Query Window”

This should output something like the following:


USE [DatabaseName]
GO
CREATE FULLTEXT INDEX ON [Schema].[TableName] KEY INDEX [PK_Index] ON ([FullTextCatalogName]) WITH (CHANGE_TRACKING AUTO)
GO
ALTER FULLTEXT INDEX ON [Schema].[TableName] ADD ([ColumnName])
GO
ALTER FULLTEXT INDEX ON [Schema].[TableName] ENABLE
GO

You can now use this after the drop and recreate script to regenerate your full text index.

TSQL Restore Database Backup Nugget

Sometimes its the simple things thats really help – restoring a sql database using transact sql:

RESTORE DATABASE TargetDatabaseName
FROM DISK = 'location of backup files (.bak)'
WITH MOVE 'database logical name' TO 'target folder and filename of mdf'
, MOVE 'database log logical name' TO 'target folder and filename of ldf'

Also, to find the logical names from the backup file:


RESTORE FILELISTONLY
FROM DISK = 'path to backup file (.bak)'
GO

Thanks to Pinal Dave: http://blog.sqlauthority.com/2007/02/25/sql-server-restore-database-backup-using-sql-script-t-sql/

Friday, April 12, 2013

Android: Dealing with Multi-Select ListViews and Updating

On my current Android project I found a problem whereby I was updating the contents of my listview, and expecting it to remember which items were checked. It turns out however that I was wrong!

The Android listview maintains its knowledge of which items are checked by simple index position – so if you update the items in the listvoew and call notifyDataSetChanged on your adapter, you can get some strange behavior. In my case the solution was simple – get the selected items before the change, restore the selected items after the change:

...

List<MyObject> selectedObjects = getSelectedObjects();

listviewAdapter.notifyDataSetChanged();

for ( int i=0; i< optionsAdapter.getCount(); i++ ) {
myListView.setItemChecked(i, selectedObjects.contains(listviewAdapter.getItem(i)));
}

And my getSelectedObjects function:


public List<Option> getSelectedOptions() {
List<MyObjects> selectedObjects = new ArrayList<MyObjects>();
final SparseBooleanArray checkedItems = myListView.getCheckedItemPositions();
int checkedItemsCount = checkedItems.size();
for (int i = 0; i < checkedItemsCount; ++i) {
int position = checkedItems.keyAt(i);
if (checkedItems.valueAt(i))
selectedObjects.add((MyObjects)listviewAdapter.getItem(position));
}
return selectedObjects;
}

And once again my sanity is restored!

Wednesday, March 27, 2013

Adding a linked folder inside a Visual Studio project

Just a short post, I had a requirement to add the results of some T4 template generated code as linked files in another assembly. The difficulty was that this generated code produced a number of different classes and may produce varying numbers of files. Linking the files individually was not an option so I linked the entire folder.

I did this by amended the csproj file, adding the following section:

<ItemGroup>
<Compile Include="FolderOfSourceFiles\**\*.cs">
<Link>TargetFolderForFiles\%(FileName)</Link>
</Compile>
</ItemGroup>

All should be fairly self explanatory!