Is there a way to programmatically determine if a SharePoint 2007 web application is using Forms authentication? I guess one way might be to read it from the web.config but I was wondering if there is some property exposed in the API.
Take a look at how /_admin/Authentication.aspx does it in Central Admin:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
string g = base.Request.QueryString["WebAppId"];
this.webApp = (SPWebApplication) SPConfigurationDatabase.Local.GetObject(new Guid(g));
this.zone = (SPUrlZone) Enum.Parse(typeof(SPUrlZone), base.Request.QueryString["Zone"]);
this.lb_Zone.Text = SPHttpUtility.HtmlEncode(SPAlternateUrl.GetZoneName(this.zone));
SPIisSettings iisSettings = this.webApp.IisSettings[this.zone];
// CODE ELIDED
if (AuthenticationMode.Windows != iisSettings.AuthenticationMode)
{
if (AuthenticationMode.Forms != iisSettings.AuthenticationMode)
{
// CODE ELIDED
}
else
{
this.rdo_authForms.Checked = true;
}
// CODE ELIDED
}
}
The part you are interested in is where it uses iisSettings.AuthenticationMode to determine if it is Forms Auth or not. So the trick is to correctly obtain a reference to SPIisSettings that is relevant to your webapp and zone. Getting to that point is where all the work needs to be done.
You'll need to parameterize parts of this code so that information to identify and obtain references to the webApp and Zone are passed in.
See where it assigns his.rdo_authForms.Checked? that's how you know if it's using forms auth.
Also, this implies that you need to know which Zone of the web application you are looking at to see if Forms Authentication is enabled
Using Jon Schoning's answer, I came up with the following code to determine if the current authentication mode is forms:
if (SPContext.Current.Site.WebApplication.IisSettings[SPContext.Current.Site.Zone].AuthenticationMode == System.Web.Configuration.AuthenticationMode.Forms) { ... }
Related
I am using Orchard CMS v1.9 and want to display a custom registration page to accept the usual username/password/email and an additional token (invite token). The token will be used to match the user to some to custom data on the server.
I have walked through this blog Customizing User Registation With Dynamic Forms And Workflows. But in addition to what is achieved in this blog I want to force a registering user to enter a token. The token is used to lookup data on the server and create a link to the userpart.
Adding the token to the form is not the issue - its the querying and linking the entered token to the backend data and storing it in the userpart that im finding awkward.
Is this possible using just workflows - or do i need a custom module? I did not see a custom action that allowed me to match the token and link.
Is there a custom module already available that does something
similar?
Disclaimer: This approach is currently based on Orchard 1.10 but was initially developed on the 1.9.x branch. It does not rely on Dynamic Forms and Workflows, but I think you could achieve something similar with those modules.
Okay so I ended up building an example module with our approach to extended users / activation system. I stripped out a lot of code, but also let some juicy parts, which aren't directly related to your answer, in it.
First you should check out the UsersController it has the activate actions you are searching for. You may need to extend the orchard LogOn-View and include some GET & POST Actions accordingly.
[AllowAnonymous]
[HttpGet]
public ActionResult Activate(string activationCode)
{
// validation stuff....
var viewModel = new CustomUserActivate
{
// This is the activationCode you're looking for
ActivationCode = userFromActivationCode.ActivationCode,
UserName = userFromActivationCode.User.UserName,
WelcomeText = userFromActivationCode.WelcomeText,
Email = userFromActivationCode.User.Email
};
return this.View(viewModel);
}
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Activate(CustomUserActivate input)
{
if ( input == null )
{
this.ModelState.AddModelError("_form", this.T("The argument cannot be null").Text);
}
CustomUserPart customUserPart = null;
if ( this.ModelState.IsValid )
{
customUserPart = this.myService.GetCustomUserByActivationCode(input.ActivationCode);
if ( customUserPart == null || customUserPart.User == null || customUserPart.User.UserName != input.UserName )
{
this.notifier.Add(NotifyType.Error, this.T("The activation failed"));
}
if ( string.IsNullOrEmpty(input.Email) )
{
this.ModelState.AddModelError("Email", this.T("You must specify an email address.").Text);
}
else if ( input.Email.Length >= 255 )
{
this.ModelState.AddModelError("Email", this.T("The email address you provided is too long.").Text);
}
else if ( !Regex.IsMatch(input.Email, UserPart.EmailPattern, RegexOptions.IgnoreCase) )
{
// http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx
this.ModelState.AddModelError("Email", this.T("You must specify a valid email address.").Text);
}
else if ( !this.myService.VerifyEmailUnicity(customUserPart.User.Id, input.Email) )
{
this.ModelState.AddModelError("Email", this.T("This email address is already in use.").Text);
}
}
if ( !this.ModelState.IsValid )
{
return this.View(input);
}
Debug.Assert(customUserPart != null, "customUserPart != null");
var user = customUserPart.User;
var userParams = new CreateUserParams(user.UserName, input.Password, input.Email, passwordQuestion: null, passwordAnswer: null, isApproved: true);
this.myService.ActivateCustomUser(customUserPart.Id, userParams);
this.notifier.Add(NotifyType.Information, this.T("Your account was activated. You can now log in."));
return this.RedirectToAction("LogOn", "Account", new { area = "Orchard.Users" });
}
The interesting stuff happens in MyService.cs.
We designed the activation system so that you can still leverage all the features of the Orchard.User Module like Email-Verifcation.
For this we've implemented some CustomSettings, where you can decide if your user get's completely activated when an ActivationCode is used or if you trigger the normal Orchard mechanism.
I guess it's best to checkout the module and step through the code in Visual Studio.
Here a two screenshots of our activation views.
Step 1 - Enter your activation code
Step 2 - Fill in the remaining fields
Profit!
All the additional source is to make use of the CustomUser / ActivationCode in Workflows, Events, Tokens, etc. But I leave this for you to discover.
If you want more detailed descriptions of the source on GitHub let me know.
Hope this helps!
Actually I have integrated CCAvenue JSP_Kit to my Lifreay portal 6.2. Till payment every thing is working properly that means after filling all the payment details and submitting no error is coming. But we are getting a response as null.
Below is the code of ReponseHandler.jsp.
encResp is coming null after payment or after cancel the request.
<%
String workingKey = "working key"; //32 Bit Alphanumeric Working Key should be entered here so that data can be decrypted.
String encResp= request.getParameter("encResp");
AesCryptUtil aesUtil=new AesCryptUtil(workingKey);
String decResp = aesUtil.decrypt(encResp);
StringTokenizer tokenizer = new StringTokenizer(decResp, "&");
Hashtable hs=new Hashtable();
String pair=null, pname=null, pvalue=null;
while (tokenizer.hasMoreTokens()) {
pair = (String)tokenizer.nextToken();
if(pair!=null) {
StringTokenizer strTok=new StringTokenizer(pair, "=");
pname=""; pvalue="";
if(strTok.hasMoreTokens()) {
pname=(String)strTok.nextToken();
if(strTok.hasMoreTokens())
pvalue=(String)strTok.nextToken();
hs.put(pname, pvalue);
}
}
}
%>
In the portal world you typically don't have access to the full and original HttpServletRequest. You can get access to it by calling
origRequest = PortalUtil.getOriginalHttpServletRequest(
PortalUtil.getHttpServletRequest());
This, of course, means that you're somewhat outside of the spec, however if you really need to communicate through servlet parameters, that might be your only option. I'd also recommend to add this code to a better testable/maintainable location - e.g. inside the Java Portlet class.
Here's the PortalUtil interface.
I just upgrade MvcSiteMapProvider from v3 to v4.6.3.
I see the upgrade note indicate:
In general, any reference to System.Web.SiteMap.Provider will need to be updated to MvcSiteMapProvider.SiteMaps.Current
I am trying to get the sitemap node by using:
SiteMaps.Current.FindSiteMapNode(rawUrl)
But it always return null
I looked into the code. In the sitemap it's actually calling the function:
protected virtual ISiteMapNode FindSiteMapNodeFromUrlMatch(IUrlKey urlToMatch)
{
if (this.urlTable.ContainsKey(urlToMatch))
{
return this.urlTable[urlToMatch];
}
return null;
}
It's trying to find a match in the urlTable.
I am using Default implementation of XmlSiteMapProvider .
It define var url = node.GetAttributeValue("url");
siteMapNode.Url = url;
siteMapNode.UrlResolver = node.GetAttributeValue("urlResolver");
So if I did not define url or urlResolver attribute in the .sitemap file. These variables a set to empty string, when generate the node.
And when this nodes are passed to AddNode function in SiteMap.
When adding the node
bool isMvcUrl = string.IsNullOrEmpty(node.UnresolvedUrl) && this.UsesDefaultUrlResolver(node);
this code will check if there is url or urlResolver
// Only store URLs if they are clickable and are configured using the Url
// property or provided by a custom URL resolver.
if (!isMvcUrl && node.Clickable)
{
url = this.siteMapChildStateFactory.CreateUrlKey(node);
// Check for duplicates (including matching or empty host names).
if (this.urlTable
.Where(k => string.Equals(k.Key.RootRelativeUrl, url.RootRelativeUrl, StringComparison.OrdinalIgnoreCase))
.Where(k => string.IsNullOrEmpty(k.Key.HostName) || string.IsNullOrEmpty(url.HostName) || string.Equals(k.Key.HostName, url.HostName, StringComparison.OrdinalIgnoreCase))
.Count() > 0)
{
var absoluteUrl = this.urlPath.ResolveUrl(node.UnresolvedUrl, string.IsNullOrEmpty(node.Protocol) ? Uri.UriSchemeHttp : node.Protocol, node.HostName);
throw new InvalidOperationException(string.Format(Resources.Messages.MultipleNodesWithIdenticalUrl, absoluteUrl));
}
}
// Add the URL
if (url != null)
{
this.urlTable[url] = node;
}
Finally no url is add to the urlTable, which result in FindSiteMapNode cannot find anything.
I am not sure if there needs to be specific configuration. Or should I implement custom XmlSiteMapProvider just add the url.
ISiteMapNodeProvider instances cannot use the FindSiteMapNode function for 2 reasons. The first you have already discovered is that finding by URL can only be done if you set the url attribute explicitly in the node configuration. The second reason is that the SiteMapBuilder doesn't add any of the nodes to the SiteMap until all of the ISiteMapNodeProvider instances have completed running, so it would be moot to add the URL to the URL table anyway.
It might help if you explain what you are trying to accomplish.
The ISiteMapNodeProvider classes have complete control over the data that is added to the SiteMapNode instances and they also have access to their parent SiteMapNode instance. This is generally all that is needed in order to populate the data. Looking up another SiteMapNode from the SiteMap object while populating the data is not supported. But as long as the node you are interested in is populated in the same ISiteMapNodeProvider instance, you can just get a reference to it later by storing it in a variable.
Update
Okay, I reread your question and your comment and it now just seems like you are looking in the wrong place. MvcSiteMapProvider v4 is no longer based on Microsoft's SiteMap provider model, so using XmlSiteMapProvider doesn't make sense, as it would sidestep the entire implementation. The only case where this might make sense is if you have a hybrid ASP.NET and ASP.NET MVC application that you want to keep a consitant menu structure between. See Upgrading from v3 to v4.
There are 2 stages to working with the data. The first stage (the ISiteMapBuilder and ISiteMapNodeProvider) loads the data from various sources (XML, .NET attributes, DynamicNodeProviders, and custom implementations of ISiteMapNodeProvider) and adds it to an object graph that starts at the SiteMap object. Much like Microsoft's model, this data is stored in a shared cache and only loaded when the cache expires. This is the stage you have been focusing on and it definitely doesn't make sense to lookup nodes here.
The second stage is when an individual request is made to access the data. This is where looking up data based on a URL might make sense, but there is already a built-in CurrentNode property that finds the node matching the current URL (or more likely the current route since we are dealing with MVC) which in most cases is the best approach to finding a node. Each node has a ParentNode and ChildNodes properties that can be used to walk up or down the tree from there.
In this second stage, you can access the SiteMap data at any point after the Application_Start event such as within a controller action, in one of the built in HTML helpers, an HTML helper template in the /Views/Shared/DisplayTemplates/ directory, or a custom HTML helper. This is the point in the application life cycle which you might call the lines SiteMaps.Current.FindSiteMapNode(rawUrl) or (more likely) SiteMaps.Current.CurrentNode to get an instance of the node so you can inspect its Attributes property (the custom attributes).
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
var currentNode = MvcSiteMapProvider.SiteMaps.Current.CurrentNode;
string permission = currentNode.Attributes.ContainsKey("permission") ? currentNode.Attributes["permission"].ToString() : string.Empty;
string programs = currentNode.Attributes.ContainsKey("programs") ? currentNode.Attributes["programs"].ToString() : string.Empty;
string agencies = currentNode.Attributes.ContainsKey("agencies") ? currentNode.Attributes["agencies"].ToString() : string.Empty;
// Do something with the custom attributes of the About page here
return View();
}
The most common usage of custom attributes is to use them from within a custom HTML helper template. Here is a custom version of the /Views/Shared/DisplayTemplates/SiteMapNodeModel.cshtml template that displays the custom attributes. Note that this template is called recursively by the Menu, SiteMapPath, and SiteMap HTML helpers. Have a look at this answer for more help if HTML helper customization is what you intend to do.
#model MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
#using System.Web.Mvc.Html
#using MvcSiteMapProvider.Web.Html.Models
#if (Model.IsCurrentNode && Model.SourceMetadata["HtmlHelper"].ToString() != "MvcSiteMapProvider.Web.Html.MenuHelper") {
<text>#Model.Title</text>
} else if (Model.IsClickable) {
if (string.IsNullOrEmpty(Model.Description))
{
#Model.Title
}
else
{
#Model.Title
}
} else {
<text>#Model.Title</text>
}
#string permission = Model.Attributes.ContainsKey("permission") ? Model.Attributes["permission"].ToString() : string.Empty
#string programs = Model.Attributes.ContainsKey("programs") ? Model.Attributes["programs"].ToString() : string.Empty
#string agencies = Model.Attributes.ContainsKey("agencies") ? Model.Attributes["agencies"].ToString() : string.Empty
<div>#permission</div>
<div>#programs</div>
<div>#agencies</div>
After upgrading some of our external websites running on SharePoint 2007 to 2010, we ran a link checker to find problems. We noticed the log showed requests for a file called spsdisco.aspx. Indeed, when examining the source of our web pages, SharePoint is adding the following link element to the page HEAD:
<link href="_vti_bin/spsdisco.aspx" rel="alternate" type="text/xml" />
This is a web service discovery file listing out the names and locations of all of SharePoint's web service endpoints. Even worse, this file is starting to show up in search indexes. At best it is embarrassing; at worst it's a potential vulnerability (these are external websites). Because it's a virtual file, it shows up under every site and subsite, so a manual approach to "hiding" each one is difficult and clumsy.
I can't seem to find any actual documentation about it -- a few references on updating it to include a custom web service, but that's about it. How might we approach a reliable, top-down approach to disabling access to these pages? I think we can find a way to suppress the LINK element in the page, but that's just obscuring the problem.
Is there a location in SharePoint (Site or Central Admin) to turn it off? Would you just add some request filtering to IIS to disallow access to SPSdisco.aspx and the ASMX files?
Update: On Kev's suggestion, I've cross-posted to sharepoint.stackexchange.com.
Update 2: See, I hadn't abandoned this question. We finally had time to get some MS guidance and build a deployable SharePoint solution to address the issue.
As a quick fix I would add a request filtering rule to deny access to SPSDisco.aspx.
But you might want to ask on the new SharePoint Stack Exchange site about a more robust fix:
https://sharepoint.stackexchange.com/
Here is the solution that we arrived at. It was in part based on recommendations by our Microsoft representative, so you might consider this an unofficial, "official" approach.
First, we need keep SharePoint from advertising the disco file to the world (i.e. Google). Simply remove the following line in your master pages:
<SharePoint:SoapDiscoveryLink runat="server"/>
This will suppress the <link href="/_vti_bin/spsdisco.aspx" rel="alternate" type="text/xml"> reference in the HEAD of your pages.
Next, we want to make sure that unauthorized users don't have access to the web services described by the disco file, or anything in _vti_bin for that matter. If your site only runs internal to your firewall (an intranet, for example), then this isn't as important. But if you've got anonymous endpoints that can be accessed externally, you want them locked down.
This is an excellent application for an HttpModule. We'll build one that intercepts any request containing _vti_bin in the path, and if the current user is unauthorized will return a 404 NOT FOUND status code. I chose to return a 404 rather than a 401 UNAUTHORIZED because I don't just want to lock those paths down, I want to hide the fact that anything even exists at those paths.
Our HttpModule looks like this:
using System;
using System.Web;
namespace Custom.SharePoint.HttpModule.SpSecureVtiBin {
public class SpSecureVtiBinModule : IHttpModule {
#region IHttpModule Members
public void Dispose() { }
public void Init( HttpApplication context ) {
context.AuthorizeRequest += new EventHandler( context_AuthorizeRequest );
}
protected virtual void context_AuthorizeRequest( object sender, EventArgs e ) {
HttpApplication app = (HttpApplication)sender;
string requestedPath = app.Request.Path;
if ( requestedPath.ToLowerInvariant().Contains( "_vti_bin" ) ) {
if ( !app.Request.IsAuthenticated ) {
app.Response.StatusCode = 404;
app.Response.StatusDescription = "Not Found";
app.Response.Write( "404 NOT FOUND" );
app.Response.End();
}
}
}
#endregion
}
}
Simple enough. To use the HttpModule, it needs to be registered in the site's web.config file with an entry under \configuration\system.webServer\modules:
<add name="SpSecureVtiBinModule" type="Custom.SharePoint.HttpModule.SpSecureVtiBin.SpSecureVtiBinModule, Custom.SharePoint.HttpModule.SpSecureVtiBin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=[your_public_key_token]" />
Of course, we don't want to modify a SharePoint application's web.config file manually. We'll create an SPFeatureReceiver to do the job:
using System.Collections.ObjectModel;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
namespace Custom.SharePoint.HttpModule.SpSecureVtiBin {
public class ModuleFeatureReceiver : SPFeatureReceiver {
private static string _owner = "SpSecureVtiBinModule";
public override void FeatureActivated( SPFeatureReceiverProperties properties ) {
SPWebApplication app = (SPWebApplication)properties.Feature.Parent;
app.WebConfigModifications.Add( GetModificationForSystemWebServer() );
app.WebService.ApplyWebConfigModifications();
app.Update();
}
public override void FeatureDeactivating( SPFeatureReceiverProperties properties ) {
SPWebApplication app = (SPWebApplication)properties.Feature.Parent;
Collection<SPWebConfigModification> mods = app.WebConfigModifications;
int modCount = mods.Count;
bool modRemoved = false;
for ( int i = modCount - 1; i >= 0; i-- ) {
SPWebConfigModification mod = mods[i];
if ( mod.Owner.Equals( _owner ) || mod.Owner.Equals( "CHK.SharePoint.HttpModule.SpSecureVtiBin.SpSecureVtiBinModule" ) ) {
app.WebConfigModifications.Remove( mod );
modRemoved = true;
}
}
if ( modRemoved ) {
app.WebService.ApplyWebConfigModifications();
app.Update();
}
}
private SPWebConfigModification GetModificationForSystemWebServer() {
return new SPWebConfigModification {
Name = "add[#name='SpSecureVtiBinModule']",
Owner = _owner,
Path = "configuration/system.webServer/modules",
Value = #"<add name=""SpSecureVtiBinModule"" type=""Custom.SharePoint.HttpModule.SpSecureVtiBin.SpSecureVtiBinModule, Custom.SharePoint.HttpModule.SpSecureVtiBin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=[your_public_key_token]"" />",
Sequence = 0
};
}
}
}
Now all that's left is to package up the HttpModule. You'll need to define a Feature in the package and reference the SPFeatureReceiver class. This will cause the web.config entry to be added when the Feature is activated, and the entry to be removed when the Feature is deactivated. Target the Feature for a WebApplication and the assembly deployment target to GlobalAssemblyCache.
I have requirement of specifying web part connections in onet.xml. So when site is created using this site definition the said web parts are already connected and ready to use. Which properties I need to specify for that particular web parts in onet.xml.
I have also hit the wall on this one sometime last year! It looks like connections can no longer be specified on Web Parts in the new .webpart format as they could in the old .dwp format. I ended up including a custom feature in the site definition like kpinhack also suggests. My code for connecting the Web Parts is listed below. The method is just designed for connecting two Web Parts of different types - it does not support multiple Web Parts of the same type on the same page. But I am sure you'll catch the general idea.
private void ConnectWebParts(SPWeb web, string pageName, Type providerType, Type consumerType)
{
SPFile file = web.GetFile(pageName);
SPList list = null;
if (file.InDocumentLibrary)
{
list = file.Item.ParentList;
if (list.ForceCheckout) file.CheckOut();
}
SPLimitedWebPartManager webPartManager =
web.GetLimitedWebPartManager(
pageName,
System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
WebPart provider = null;
foreach (WebPart wp in webPartManager.WebParts)
{
if (wp.GetType() == providerType)
{
provider = wp;
break;
}
}
foreach (WebPart consumer in webPartManager.WebParts)
{
if (consumer.GetType() != consumerType) continue;
ProviderConnectionPointCollection providerConnections = webPartManager.GetProviderConnectionPoints(provider);
ProviderConnectionPoint providerConnection = providerConnections[0];
ConsumerConnectionPointCollection consumerConnections = webPartManager.GetConsumerConnectionPoints(consumer);
ConsumerConnectionPoint consumerConnection = consumerConnections[0];
SPWebPartConnection con = webPartManager.SPConnectWebParts(provider, providerConnection, consumer, consumerConnection);
webPartManager.SPWebPartConnections.Add(con);
}
if (list != null)
{
if (list.ForceCheckout)
{
file.CheckIn("Added Web Part Connections");
}
if (list.EnableVersioning && list.EnableMinorVersions)
{
file.Publish("Added Web Part Connections");
}
}
}
I would configure the WebParts in the SiteProvisioning-Feature, by implementing the 'OnActivated'-Eventhandler. That way the code will run when the website is created, and you can handle errors the way you like it (i.e. if the WebParts are not available when the website is created - for whatever reason)
I hope this helps!
you would need to use the < AllUsersWebPart > tag to declare your web parts, and then declare your connections within the enclosed < WebPart > elements.
example