Showing posts with label Security. Show all posts
Showing posts with label Security. Show all posts

Tuesday, January 15, 2013

Event receiver with Elevated Privileged

Scenario:
Client wanted to run the event receiver code with elevated access.

Solution:
Using Elevated Privileged is one option but better options was using System User Token
Code:

/// 
        /// An item is being added.
        /// 
        public override void ItemAdding(SPItemEventProperties properties)
        {
            SPSite site = properties.Web.Site;
            SPUserToken sysToken = site.SystemAccount.UserToken;
            site.Dispose();

            using (var systemSite = new SPSite(properties.SiteId, sysToken))
            {
                using (SPWeb sysWeb = systemSite.OpenWeb(properties.Web.ID))
                {
                       // Code goes here 
                }
            }

Tuesday, February 28, 2012

Powershell and SharePoint Permissions

Scenario: SharePoint provide options to have security at different level, here some related functions which you can use.
Code:

#Load SharePoint Snap In
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
function Create-SPGroupInWeb
{
 param ($Url, $GroupName, $PermissionLevel, $Description)
 $web = Get-SPWeb -Identity $Url
 if ($web.SiteGroups[$GroupName] -ne $null)
 {
  Write-Host "Group $GroupName already exists!" -foregroundcolor Red
 }
 else
 {
  $web.SiteGroups.Add($GroupName, $web.Site.Owner, $web.Site.Owner, $Description)
  $group = $web.SiteGroups[$GroupName]
  $roleAssignment = new-object Microsoft.SharePoint.SPRoleAssignment($group)
  $roleDefinition = $web.Site.RootWeb.RoleDefinitions[$PermissionLevel]
  $roleAssignment.RoleDefinitionBindings.Add($roleDefinition)
  $web.RoleAssignments.Add($roleAssignment)
  $web.Update()
  Write-Host "Group $GroupName created successfully" -foregroundcolor Green
 }

 $web.Dispose()
}
function Remove-SPPermisssionFromListGroup
{
 param ($Url, $ListName, $GroupName, $PermissionLevel)
 $web = Get-SPWeb -Identity $Url
 $list = $web.Lists.TryGetList($ListName)
 if ($list -ne $null)
 {
  if ($list.HasUniqueRoleAssignments -eq $False)
  {
   $list.BreakRoleInheritance($True)
  }
  else
  {
   if ($web.SiteGroups[$GroupName] -ne $null)
   {
    $group = $web.SiteGroups[$GroupName]
    $roleAssign = $list.RoleAssignments.GetAssignmentByPrincipal($group);
    $roleDefinition = $web.RoleDefinitions[$PermissionLevel];
    $roleAssign.RoleDefinitionBindings.Remove($roleDefinition);
    $roleAssign.Update();
    $list.Update();
    Write-Host "Successfully removed $PermissionLevel permission from $GroupName group in $ListName list." -foregroundcolor Green
   }
   else
   {
    Write-Host "Group $GroupName does not exist." -foregroundcolor Red
   }
  }
 }
 else
 {
  Write-Host "List $ListName does not exist!" -foregroundcolor Red
 }

 $web.Dispose()
}
function Add-SPPermissionToListGroup
{
 param ($Url, $ListName, $GroupName, $PermissionLevel)
 $web = Get-SPWeb -Identity $Url
 $list = $web.Lists.TryGetList($ListName)
 if ($list -ne $null)
 {
  if ($list.HasUniqueRoleAssignments -eq $False)
  {
   $list.BreakRoleInheritance($True)
  }
  else
  {
   if ($web.SiteGroups[$GroupName] -ne $null)
   {
    $group = $web.SiteGroups[$GroupName]
    $roleAssignment = new-object Microsoft.SharePoint.SPRoleAssignment($group)
    $roleDefinition = $web.RoleDefinitions[$PermissionLevel];
    $roleAssignment.RoleDefinitionBindings.Add($roleDefinition);
    $list.RoleAssignments.Add($roleAssignment)
    $list.Update();
    Write-Host "Successfully added $PermissionLevel permission to $GroupName group in $ListName list. " -foregroundcolor Green
   }
   else
   {
    Write-Host "Group $GroupName does not exist." -foregroundcolor Red
   }
  }
 }

 $web.Dispose()
}
function Remove-SPPermisssionFromListItemGroupSpecific
{
 param ($Url, $ListName, $GroupName, $PermissionLevel)
 $web = Get-SPWeb -Identity $Url
 $list = $web.Lists.TryGetList($ListName)
 if ($list -ne $null)
 {
  foreach ($item in $list.Items) 
  {
   if ($item.HasUniqueRoleAssignments -eq $False)
   {
    $item.BreakRoleInheritance($True)
   }
   else
   {
    if ($web.SiteGroups[$GroupName] -ne $null)
    {
     $group = $web.SiteGroups[$GroupName]
     $roleAssign = $item.RoleAssignments.GetAssignmentByPrincipal($group);
     $roleDefinition = $web.RoleDefinitions[$PermissionLevel];
     $roleAssign.RoleDefinitionBindings.Remove($roleDefinition);
     $roleAssign.Update();
     $item.SystemUpdate();                    
     Write-Host "Successfully removed $PermissionLevel permission from $GroupName group in $ListName list." -foregroundcolor Green
    }
    else
    {
     Write-Host "Group $GroupName does not exist." -foregroundcolor Red
    }
   }
  }
 }
 else
 {
  Write-Host "List $ListName does not exist!" -foregroundcolor Red
 }

 $web.Dispose()
}
function Remove-SPPermisssionFromListItemGroupAll
{
 param ($Url, $ListName, $GroupName)
 $web = Get-SPWeb -Identity $Url
 $list = $web.Lists.TryGetList($ListName)
 if ($list -ne $null)
 {
  foreach ($item in $list.Items) 
  {
   if ($item.HasUniqueRoleAssignments -eq $False)
   {
    $item.BreakRoleInheritance($True)
   }
   else
   {
    if ($web.SiteGroups[$GroupName] -ne $null)
    {
     $group = $web.SiteGroups[$GroupName]
                    $item.RoleAssignments.Remove($group)
     $item.SystemUpdate();                    
     Write-Host "Successfully removed $PermissionLevel permission from $GroupName group in $ListName list." -foregroundcolor Green
    }
    else
    {
     Write-Host "Group $GroupName does not exist." -foregroundcolor Red
    }
   }
  }
 }
 else
 {
  Write-Host "List $ListName does not exist!" -foregroundcolor Red
 }

 $web.Dispose()
}
function Add-SPPermissionToListItemGroup
{
 param ($Url, $ListName, $GroupName, $PermissionLevel)
 $web = Get-SPWeb -Identity $Url
 $list = $web.Lists.TryGetList($ListName)
 if ($list -ne $null)
 {
  foreach ($item in $list.Items) 
  {
   if ($item.HasUniqueRoleAssignments -eq $False)
   {
    $item.BreakRoleInheritance($True)
   }
   else
   {
    if ($web.SiteGroups[$GroupName] -ne $null)
    {
     $group = $web.SiteGroups[$GroupName]
     $roleAssignment = new-object Microsoft.SharePoint.SPRoleAssignment($group)
     $roleDefinition = $web.RoleDefinitions[$PermissionLevel];
     $roleAssignment.RoleDefinitionBindings.Add($roleDefinition);
     $item.RoleAssignments.Add($roleAssignment)
     $item.SystemUpdate();
     Write-Host "Successfully added $PermissionLevel permission to $GroupName group in $ListName list. " -foregroundcolor Green
    }
    else
    {
     Write-Host "Group $GroupName does not exist." -foregroundcolor Red
    }
   }
  }
 }

 $web.Dispose()
}
function Add-SPPermissionToListItemGroupConditional
{
 param ($Url, $ListName, $Caml, $GroupName, $PermissionLevel)
 $web = Get-SPWeb -Identity $Url
 $list = $web.Lists.TryGetList($ListName)
 if ($list -ne $null)
 {
        $spQuery = New-Object Microsoft.SharePoint.SPQuery        
        $spQuery.Query = $Caml
        $spQuery.RowLimit = 10000
        $listItems = $list.GetItems($spQuery)        
        $listItems.Count
  foreach ($item in $listItems) 
  {
   if ($item.HasUniqueRoleAssignments -eq $False)
   {
    $item.BreakRoleInheritance($True)
   }
   else
   {
    if ($web.SiteGroups[$GroupName] -ne $null)
    {
     $group = $web.SiteGroups[$GroupName]
     $roleAssignment = new-object Microsoft.SharePoint.SPRoleAssignment($group)
     $roleDefinition = $web.RoleDefinitions[$PermissionLevel];
     $roleAssignment.RoleDefinitionBindings.Add($roleDefinition);
     $item.RoleAssignments.Add($roleAssignment)
     $item.SystemUpdate();
     Write-Host "Successfully added $PermissionLevel permission to $GroupName group in $ListName list. " -foregroundcolor Green
    }
    else
    {
     Write-Host "Group $GroupName does not exist." -foregroundcolor Red
    }
   }
  }
 }

 $web.Dispose()
}
$Url=Read-Host "Enter site url"

Remove-SPPermisssionFromListItemGroupSpecific $Url "Shared Documents" "Team Visitors" "Read"
Remove-SPPermisssionFromListItemGroupAll $Url "Shared Documents" "Team Visitors"
Add-SPPermissionToListItemGroup $Url "Shared Documents" "Team Visitors" "Contribute"
Add-SPPermissionToListItemGroupConditional $Url "Shared Documents" "<Where><Eq><FieldRef Name='Create' /><Value Type='Boolean'>1</Value></Eq></Where>" "Team Visitors" "Contribute"

Thursday, November 5, 2009

Sharepoint login Sign In button icon

Scenario:
Client wanted to have a better Sign In image. Out of box Sign In link sucks.

Solution:
Small java script did it the trick.

signin

Code:

<script type="text/javascript" >

function newSignInImage()
{

var div = document.getElementById('ctl00_PlaceHolderGlobalNavigation_IdWelcome_ExplicitLogin');

div.parentNode.innerHTML = "<A id='a' style='DISPLAY: block;padding-right:5px' href='/_layouts/Authenticate.aspx?Source=/' ><img border=0 src='/images/Nav_SignIn.gif' alt='signinimage'/>";

}

_spBodyOnLoadFunctionNames.push("newSignInImage");

</script>

Wednesday, November 4, 2009

Restricting accessing view for admins only

Scenario:
One of the requirement we had was something like this.

Users can submit Feedback forms and should not be able to see other feedback.
But Owners should be able to go to Feedback List and see all the feedbacks.

Solution:
Custom Web Part was the solution. We gave contributor rights to all the users on this list and to avoid them visiting the complete list we added a webpart on top of the AllItems.aspx List.

Code:

using System;
using System.Runtime.InteropServices;

using Microsoft.SharePoint;

namespace ReDirectWebPart
{
public class ReDirectWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
public ReDirectWebPart()
{
}

private const string const_permissionMask = "9223372036854775807";
private string m_permissionMask = const_permissionMask;

// Configuration List Name property.
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[Category("Custom Properties")]
[WebDisplayName("Permission Mask")]
[Description("Permission Mask (i.e. FullMask = 9223372036854775807")]
[DefaultValue(const_permissionMask)]
public string PermissionMask
{
get
{
return m_permissionMask;
}

set
{
m_permissionMask = value;
}
}

private const string const_urlToRedirect = "/";
private string m_urlToRedirect = const_urlToRedirect;

// Configuration Url to Redirect property.
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(true)]
[Category("Custom Properties")]
[WebDisplayName("Url to Redirect")]
[Description("Url to Redirect")]
[DefaultValue(const_urlToRedirect)]
public string UrlToRedirect
{
get
{
return m_urlToRedirect;
}

set
{
m_urlToRedirect = value;
}
}

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);

SPList list = SPContext.Current.List;

if (!string.IsNullOrEmpty(PermissionMask))
{
if (!list.DoesUserHavePermissions((SPBasePermissions)Enum.Parse(typeof(SPBasePermissions),PermissionMask)))
{
if (string.IsNullOrEmpty(UrlToRedirect))
{
this.Page.Response.Redirect(SPContext.Current.Web.Url);
}
else
{
this.Page.Response.Redirect(UrlToRedirect);
}
}
}
else
{
if (!list.DoesUserHavePermissions(SPBasePermissions.FullMask))
{
if (string.IsNullOrEmpty(UrlToRedirect))
{
this.Page.Response.Redirect(SPContext.Current.Web.Url);
}
else
{
this.Page.Response.Redirect(UrlToRedirect);
}
}
}
}

public override void RenderControl(System.Web.UI.HtmlTextWriter writer)
{
if (string.IsNullOrEmpty(UrlToRedirect) || string.IsNullOrEmpty(PermissionMask))
{
writer.Write("Please configure the webpart with proper custom property values");
}
base.RenderControl(writer);
}
}
}
Enhacements:
1. You can make it generic with what custom properties for what type of permission and the url to redirect. ( Code Updated to include it )

Sunday, September 13, 2009

Create web application link missing

Scenario:
You have successfully installed the WSS/MOSS on the Window Server 2003/2008 or Vista, however you don't see any link for creating or extending a web application.

You can navigate to Shared Service Administration section but not able to create a new SSP. Getting Access Denied or permission issues.

Solution:
0. Make sure you close all the instances of IE

1. You have to start IE with "Run as Administrator..." option

2. The IE should be in the Intranet zone.

Sunday, August 30, 2009

ASP.Net Impersonation

Scenario:
ASP.Net provides impersonation options, but I still find few people not very sure about what is it and how to use it in ASP.net.

Details:
Impersonation is important security feature which enables the ability to control the identity under which code is executed. Impersonation is when ASP.NET executes code in the context of an authenticated and authorized client. By default, ASP.NET does not use impersonation and instead executes all code using the same user account as the ASP.NET process, which is typically the ASPNET account.

This is contrary to the default behavior of ASP and SharePoint, which uses impersonation by default.

Enabling Impersonation from Web Config:

<identity impersonate="true" />
Impersonation enabled for a specific identity:
<identity impersonate="true" userName="domain\user" password="password" />
Disabling Impersonation from Web Config fully:
<identity impersonate="false" />
Disabling Impersonation from Code for sometime:
<identity impersonate="true" />
using System.Web.Hosting; //Add Reference to System.Web.dll

//Any code here will runs as the application pool user

using (HostingEnvironment.Impersonate()) {
//Code here will runs as logged on user
}

//Any code here will runs as the application pool user
Articles:
ASP.Net impersonation

Saturday, August 29, 2009

Checking if user belongs to a group

Scenario:
Often we need to do some action depending upon as if user is part of certain sharepoint group or not.

Solution:
Simple function did the trick

Code:

public bool DoesUserExistInGroup(SPWeb web, SPUser user,string groupName)
{
//Retrieving all the user groups in the site/web
SPGroupCollection userGroups = user.Groups;

foreach (SPGroup group in userGroups)
{
//Checking the group
if (group.Name.Contains(groupName))
{
return true;
}
}
return false;
}

Saturday, August 22, 2009

Securing sharepoint pages / views

Scenario:
Sharepoint has a great security model build but client always need something different. i.e. X user should be able to acess the following Url etc etc.

Solution:
This is the starting point for a good solution which can be scaled to any level. Look at the possible enhancement section.

I have created a very simple HTTPModule which will act as gate keeper for blocking/allowing users.

Steps:
1. Create a list name 'Blocked' of type 'Generic List'

2. Add columns for Url ( Text ) , BlockedFor ( People and Group, Allow multiple and display AccountName as field value ), Message ( Multi-line , with no rick text support )

3. Add a test entry for a user as shown here
blocked entry

4. Now login to the site using new user account details and try navigating to the Url you have blocked for this user. Here's what you get.
blocked error

HTTPModule Code:

using System;
using System.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace SPSecureHttpModule
{
public class SPSecureHttpModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostAuthenticateRequest += ContextPostAuthenticateRequest;
}

static void ContextPostAuthenticateRequest(object sender, EventArgs e)
{
var app = sender as HttpApplication;
if (app != null)
{
string requesturl = app.Request.Url.ToString();
string message = CheckBlockage(requesturl, app.Request.LogonUserIdentity.Name);
if (message.Length >0 )
{
SPUtility.TransferToErrorPage(message);
//SPUtility.SendAccessDeniedHeader(new Exception("You dont have access"));
}
}
}
public static string CheckBlockage(string url, string userName)
{
const string rootSite = "http://localhost";
const string listName = "Blocked";
string message = string.Empty ;

SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (var site = new SPSite(rootSite))
{
using (SPWeb web = site.RootWeb)
{
SPList list = web.Lists[listName];

SPQuery query = new SPQuery()
{
ViewFields = "<FieldRef Name='Message'/>",
RowLimit = 100,
Query = string.Format(@"<Where>" +
"<And>" +
"<Eq>"+
"<FieldRef Name='Url' />"+
"<Value Type='Text'>{0}</Value>"+
"</Eq>"+
"<Eq>"+
"<FieldRef Name='BlockedFor' />"+
"<Value Type='UserMulti'>{1}</Value>"+
"</Eq>"+
"</And>"+
"</Where>",url,userName)

};

SPListItemCollection listItemCollection = list.GetItems(query);
if (listItemCollection.Count > 0)
message = listItemCollection[0]["Message"].ToString();
}
}
});

return message;
}

public void Dispose() { }
}
}
Web Config entry:
    <httpModules>
<clear />

.... other entries ( remove for readability ) .....

<add name="SPSecureHttpModule" type="SPSecureHttpModule.SPSecureHttpModule, SPSecureHttpModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=898712a2c58c5c10" />

</httpModules>
Enhancements possible:
1. To make it more generic for Groups / Multiple Users
2. Wildcard support for Url

Thoughts ??

Restricting adding new item for contibutors

Scenario:
So many time I have been asked for a way where Contributors are allowed to reply or edit the existing item but should not be able to add any new entry.

User should be able reply to the discussion but not able to create a new one.

Workaround:
This is a no code workaround as users can still find a way to bring the New form back. Using DataView webpart we will hide the form for all the users except Administrators.

You should enable Content Approval to be able to control it fully.

restricting

Steps:
1. Create a new file with the name 'Information.webpart'

2. Paste the WebPart definition into the file and save it.

3. Now navigate to Site Action > Site Settings > WebPart Gallery

4. Upload the new webpart file here. ( There is a reason why you will need to do it this way, as you will not able to import this webpart using import option )

5. Now go to the list you want with restricted new item.

6. Click on new item and enter into Edit mode
( I know Edit page is missing but you need paste following command javascript:MSOLayout_ToggleLayoutMode(); into your browser address bar and hit enter to get the edit mode )Check Srini's post on how to toggle

7. Add the new webpart from the WebPart picker popup as 2nd Webpart on the page. Make sure new webpart is the 2nd webpart.Otherwise it will not work.

8. Exit Edit mode and now test it using visitor account.

WebPart Definition File:

<webParts>
<webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
<metaData>
<type name="Microsoft.SharePoint.WebPartPages.DataFormWebPart, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
<importErrorMessage>Cannot import this Web Part.</importErrorMessage>
</metaData>
<data>
<properties>
<property name="MissingAssembly" type="string">Cannot import this Web Part.</property>
<property name="FireInitialRow" type="bool">True</property>
<property name="TitleIconImageUrl" type="string" />
<property name="HelpMode" type="helpmode">Modeless</property>
<property name="CacheXslStorage" type="bool">True</property>
<property name="ViewContentTypeId" type="string" />
<property name="Description" type="string" />
<property name="DataSourcesString" type="string" />
<property name="AllowZoneChange" type="bool">True</property>
<property name="ParameterBindings" type="string" null="true" />
<property name="PageSize" type="int">-1</property>
<property name="TitleUrl" type="string" />
<property name="ViewFlag" type="string" />
<property name="Xsl" type="string">

<xsl:stylesheet version="1.0" exclude-result-prefixes="rs z o s ddwrt dt msxsl" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:o="urn:schemas-microsoft-com:office" xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema" xmlns:ddwrt2="urn:frontpage:internal"><xsl:output method="html" indent="no" /><xsl:decimal-format NaN="" />

<xsl:template match="/" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:asp="http://schemas.microsoft.com/ASPNET/20">

<xsl:if test="not(ddwrt:IfHasRights(9223372036854775807))">

<script type="text/javascript" >
var div2 = document.getElementById('WebPartWPQ2');
div2.style.display = 'none';
</script>

You dont have permissions to create a new announcement.

</xsl:if>

</xsl:template>
</xsl:stylesheet>

</property>
<property name="NoDefaultStyle" type="string" null="true" />
<property name="Direction" type="direction">NotSet</property>
<property name="UseSQLDataSourcePaging" type="bool">True</property>
<property name="ListName" type="string" null="true" />
<property name="Hidden" type="bool">False</property>
<property name="DisplayName" type="string" />
<property name="SampleData" type="string" null="true" />
<property name="HelpUrl" type="string" />
<property name="ChromeType" type="chrometype">Default</property>
<property name="CatalogIconImageUrl" type="string" />
<property name="Height" type="string" />
<property name="DataFields" type="string" />
<property name="Default" type="string" />
<property name="ChromeState" type="chromestate">Normal</property>
<property name="DataSourceID" type="string" />
<property name="AllowClose" type="bool">True</property>
<property name="CacheXslTimeOut" type="int">86400</property>
<property name="AllowMinimize" type="bool">True</property>
<property name="AllowEdit" type="bool">True</property>
<property name="XslLink" type="string" null="true" />
<property name="Title" type="string">Information</property>
<property name="Width" type="string" />
<property name="ShowWithSampleData" type="bool">False</property>
<property name="ExportMode" type="exportmode">All</property>
<property name="AllowHide" type="bool">True</property>
<property name="AllowConnect" type="bool">True</property>
</properties>
</data>
</webPart>
</webParts>
Configure:
1. You can edit the webpart to change the message user gets when he don't have access.

Articles:
Permission Values

Friday, August 21, 2009

Encrypting sensitive information in web config

Scenario:
Web Config is another spot where usually people store important information in form of some AppSettings or Connection String information. Even though its no more a new topic, still most of the developer take it light. Its not that difficult to encrypt it any section of the Web.config

Solution:
Keep in mind, encrypting will not the change how you read the settings from C# code.

Code:

using System.Configuration;
using System.Web.Configuration;

Configuration config = WebConfigurationManager.OpenWebConfiguration(Context.Request.ApplicationPath);
ConfigurationSection sect = config.GetSection("appSettings");
if (!sect.SectionInformation.IsProtected) {
sect.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
config.Save();
}

Configuration config = WebConfigurationManager.OpenWebConfiguration("/");
ConfigurationSection sect = config.GetSection("appSettings");
if (sect.SectionInformation.IsProtected) {
sect.SectionInformation.UnprotectSection();
config.Save();
}
Web.Config:
<appSettings>
<add key="BlogAuthor" value="Sandeep" />
</appSettings>

<appSettings configProtectionProvider="RsaProtectedConfigurationProvider">
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCM…dsadasdsaEWRSDFDS</CipherValue>
</CipherData>
</EncryptedData>
</appSettings>

Encrypting sensitive information

Scenario:
One of the common requirements from client is to encrypt sensitive information.I do it all the time, but never posted anything here.

Solution:
.Net framework has supporting classes for encryption.

Code:

using System.Text;
using System.Security.Cryptography;

string strSSNValueInitial = "000-0-0000-000";

//Here's how you encrypt
byte[] arrSecret = Encoding.Unicode.GetBytes(strSSNValue);
byte[] arrKey = {0, 1, 2};
byte[] arrEncryptedData = ProtectedData.Protect(arrSecret, arrKey,DataProtectionScope.LocalMachine);


//Here's how you decrypt it back
byte[] ssnValueArray = ProtectedData.Unprotect(arrEncryptedData, arrKey,DataProtectionScope.LocalMachine);
string strSSNValueFinal = Encoding.Unicode.GetString(ssnValueArray);

Another option:

You can also use SecureString class , the value of SecureString is automatically encrypted.
using System.Security;
using System.Runtime.InteropServices;

string strSSNValue = "000-000-000";

//Securing data in secure string
SecureString strSecure = new SecureString();
char[] charValue = Encoding.Unicode.GetChars(Encoding.Unicode.GetBytes(strSSNValue));
for (int i = 0; i < charValue.Length; i++)
{
strSecure.AppendChar(charValue[i]);
}

//Reading it back
IntPtr objPointer = Marshal.SecureStringToBSTR(strSecure);
string strSSNValue = Marshal.PtrToStringUni(objPointer);

Sunday, July 19, 2009

Anonymous access Webpart and List security

Scenario:
I was reading one of the article below from Mike Walsh and was also concerned about our Web part security displacing sensitive data.

Whats the concern :
In simple words , if you have a site with Anonymous Access enabled and have one more list / document library with inheritance broken. Users can still access the list data/documents because of Anonymous Access setting on web.

Is it Bug ?:
I don't think so, its kinda same behavior as we have for Nested folders on Windows, where if you open door for the top level folder then all sub folders become open to all.

Solution:
1. You can fix it from UI
a. Navigate the list / document library you want to restrict the permissions.
b. Settings > List Settings > Permission for this list
c. Settings > Anonymous Access
d. Remove the check box from View Items and save.

2. If you have written a custom web part to access the list data/documents, then you need to perform additional check before displaying data to the user as shown below.

Code:

if (!SPContext.Current.Web.AllowAnonymousAccess )
{
// Code goes here
}

Thursday, July 9, 2009

CAPTCHA or HIP Control

Scenario:
I worked on sometime back and still few friends of mine keep asking about me.
CAPTCHA is a way to ensure that requester is a human.

Solution:
Small ASP.Net, with 2 pages only.
Login.aspx and Default.aspx

CAPTCHA

Logic is pretty simple : Login page has CAPTCHA control and use server side Validation for it.
On Button click it checks if Page.IsValid = True, if page is valid then it redirects to the Default page.

Code:

  protected void Button1_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
Response.Redirect("default.aspx");
}
}
Download:
VS 2008 Project

Monday, June 29, 2009

Restricting access to sharepoint views

Scenario:
Some one asked me how to restricting access to sharepoint views because he has a view with one column with information only meant for Contributors and not for Visitors.

Solution:
There is no out of box solution and they are many work-around for the problem.
Here's one which is simple and can be handled from sharepoint UI itself.

Steps ( Set 1 ) :

01. Go to the list you want to restrict the access.
02. Make sure AllItems.aspx , or your default view doesn't have the column you want to hide.
03. Now create a new View based on previous view, you can name it Contributors view.
04. Add the columns you want to show to Contributors View and save it.

Steps ( Set 2 ) :

05. Now go Site Actions > Create
06. Create a new Web Part Page with name "Contributors View", select the library you want to save it to.
07. Click on Add webpart option and Insert list view webpart by selecting the list name
08. Edit this webpart to point it to new View ( Contributors View )
09. Exit the editor mode

Steps ( Set 3 ) :

10. Go to library you have used in the above step.
11. Look for the page you have created.
12. From the context menu of the page > Manage Permission , Allow only Contributors

Steps ( Set 4 ) :

13. Now visit the list again ( trust me this is the last time )
14. List Settings > delete the view ( Contributors View ).

That's it , you are almost done 95%.
Missing 5% work goes to the solution you need to find out to provide some way for your Contributors to navigate to this newly created page. :-)

Wednesday, June 17, 2009

Calling WCF service from different domain

Scenario:
We had a requirement to call a WCF endpoint which was hosted in all together different domain. We had minimal trust between the domains. Creating direct proxy gave us SOAP Security Negotiation error.

This was Net TCP binding and we have Security mode Windows

Solution:
1. Create a local account on the server hosting the WCF Endpoint
2. Before opening the channel , set the client credentials as shown below.

Code:

//Connecting to WCF
var proxyClient = new MyWCFServiceClient();
proxyClient.ClientCredentials.Windows.ClientCredential.UserName = @"domain\username";
proxyClient.ClientCredentials.Windows.ClientCredential.Password = "sharepoint";
proxyClient.Open();
Follow me on Twitter

Sunday, June 14, 2009

My Sites webpart to display what all site collection you have acces to

Scenario:
Situation is very simple, Assuming there are 100s of site collections in the organization and logged in user should be able to be able to see what all site he have access to.

Solution:
Simple webpart


Code:

using System.Data;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.WebControls;

namespace SKN
{
public class MySites : System.Web.UI.WebControls.WebParts.WebPart
{
protected SPGridView _griDView;

protected override void CreateChildControls()
{
_griDView = new SPGridView {AutoGenerateColumns = false};

var hlf = new HyperLinkField
{
HeaderText = "Title",
DataNavigateUrlFields = new[] {"Url"},
DataNavigateUrlFormatString = "{0}",
DataTextField = "Title"
};

_griDView.Columns.Add(hlf);

DataTable table = CreateTable();

base.CreateChildControls();

SPSecurity.RunWithElevatedPrivileges(delegate
{

SPSite site = SPContext.Current.Site;
SPWebApplication webApplication = site.WebApplication;

foreach (SPSite siteCollection in webApplication.Sites)
{
using (SPWeb website = siteCollection.RootWeb)
{
if (website.DoesUserHavePermissions(SPBasePermissions.ViewListItems))
{
var row = table.NewRow();
row["Title"] = website.Title;
row["Url"] = siteCollection.Url;

table.Rows.Add(row);
}
}
siteCollection.Dispose();
}
}
);

Controls.Add(_griDView);

_griDView.DataSource = table.DefaultView;
_griDView.DataBind();
}

private static DataTable CreateTable()
{
var table = new DataTable("MySites");
// Declare variables for DataColumn and DataRow objects.

// Create second column.
var column1 = new DataColumn
{
DataType = System.Type.GetType("System.String"),
ColumnName = "Title",
AutoIncrement = false,
Caption = "Title",
ReadOnly = false,
Unique = false
};
// Add the column to the table.
table.Columns.Add(column1);

// Create new DataColumn, set DataType,
// ColumnName and add to DataTable.
var column2 = new DataColumn
{
DataType = System.Type.GetType("System.String"),
ColumnName = "Url",
AutoIncrement = false,
Caption = "Url",
ReadOnly = false,
Unique = false
};
// Add the column to the table.
table.Columns.Add(column2);

return table;
}
}
}
Possible improvements:
-- Please not in case you have multiple web applications then this solution will not work. You need to iterate through other Web Applications also and make sure you have proper account which can iterate all web applications.

-- If you have really hundreds of site collections. Then consider caching, that was one reason I used DataTable. ( Srini you can do it ;-) )

-- Also Url is not clickable , Pass me the code when you finish doing it for your client because I am tired :-) <-- Implemented

Follow me on Twitter

Saturday, June 6, 2009

HTTPModule to monitor File downloaded

Scenario:
Easy way to see who downloaded what.

Solution:
HTTPModule can help.

Code:


using System;
using System.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace SPCustomHTTPModule
{
public class CustomHttpModule :IHttpModule
{
public void Init(HttpApplication context)
{
context.PostAuthenticateRequest += ContextPostAuthenticateRequest;
}

static void ContextPostAuthenticateRequest(object sender , EventArgs e)
{
var app = sender as HttpApplication;
if (app != null)
{
string requesturl = app.Request.Url.ToString();
if (requesturl.EndsWith(".docx") || requesturl.EndsWith(".pdf"))
{
CreateEntry(requesturl, app.Request.LogonUserIdentity.Name);
}
}
}
public static void CreateEntry(string fileUrl,string userName)
{
const string rootSite = "http://localhost";
const string listName = "Links";


using (var site = new SPSite(rootSite))
{
using (SPWeb web = site.RootWeb)
{
web.AllowUnsafeUpdates = true;
SPList list = web.Lists[listName];

SPListItem listItem = list.Items.Add();

listItem["Title"] = "File downloaded ";
listItem["Comments"] = userName + " downloaded " + SPEncode.UrlEncodeAsUrl(fileUrl);
listItem.Update();
web.AllowUnsafeUpdates = false;
}
}
}

public void Dispose() { }
}
}
Web.Config( Add to httpModules section):
<add name="SPCustomHTTPModule" type="SPCustomHTTPModule.CustomHttpModule, SPCustomHTTPModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a56fc096c595e650" />
Articles:
HTTP Handlers and HTTP Modules Overview

Follow on Twitter

Tuesday, June 2, 2009

Impersonating User Identity

Scenario:
You might want to impersonate a specific WSS user identity before creating a new object so that WSS user is recognized as the owner of the new object.
In order to impersonate a WSS user identity, you must first create an SPUserToken object. You can do this by accessing the UserToken property of an SPUser object. Once you have the SPUserToken object, you can use it to create a new SPSite object using an overloaded version of the SPSite class constructor.

Solution:
This is a very good alternate to RunWithElevatedPrivileges.

Code:

SPSite siteCollection = SPContext.Current.Site;
SPWeb site = SPContext.Current.Web;

// get SPUser object and acquire token
SPUser targetUser = site.SiteUsers[@"LITWAREINC\BrianC"];
SPUserToken token = targetUser.UserToken;

// create new SPSite and SPWeb object to impersonate user
using (SPSite impersonatedSiteCollection =
new SPSite(siteCollection.ID, token)) {
using (SPWeb impersonatedSite =
impersonatedSiteCollection.OpenWeb(site.ID)) {
// WSS identity switched to impersonate BrianC
// Windows identity does not change
}
}
Article:http://msdn.microsoft.com/en-us/magazine/cc163287.aspx

Wednesday, May 27, 2009

Access Denied for Central Administration

Scenario:
We were configuring Form Based Authentication for our web application and after configuring we can go to CA home page easily but were getting Access Denied on all other pages. It was not accepting the Windows Credential and was prompting repeatedly.

Solution:
By mistake we added the Role Manager to CA. While configuring the CA , you need to configure only your Membership provider. You will get Access Denied when you try to access other pages if you have configured the Role Manager for CA.

Removing Role Manager solved the issue

Thursday, May 14, 2009

Site Collection Policies - Access Denied

Scenario:
I got the following error while accessing the Site Collection Policies page
"Site Collection Policies - Access Denied"

Solution:
I was using Form Authentication for this web application and so I suspected that as my first culpit and that's what it was. I browsed the same page from my extended Web Application using Windows Authentication and it worked.

So if you are getting the same error , try extending the Web Application on another port with Windows Authentication and you are all set.