Umbraco - Preventing users editing the page name - umbraco

Does anyone have any suggested strategies for preventing users from editing page names?
I'm developing a site on Umbraco where various partners have their own specific page they can edit exclusively. Access to this page is controlled through the standard Umbraco permissions. However we've found that some of these users have been editing the page title, but we'd like to limit them to only being able to edit the content.
I can't see any obvious way to control this through the built-in permissions.
Perhaps it's possible to plug in some page pre-save code that checks if the user has certain permissions, and if they don't the page name is set to it's pre-edit state?
Any suggestions / pointers are greatly appreciated.

Yes, you can hook up to Umbraco ContentService events and check if Name has changed and do or not do something with this specific node. You can also add some additional checks to determine if user is permitted to change the name (e.g. you can control this by role or anything else whay you need).
Sample code will look like this:
public class UmbracoEvents : ApplicationEventHandler
{
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
ContentService.Saving += ContentService_Saving;
}
private void ContentService_Saving(IContentService sender, Umbraco.Core.Events.SaveEventArgs<Umbraco.Core.Models.IContent> e)
{
foreach (var changedItem in e.SavedEntities)
{
var currentVersion = sender.GetById(changedItem.Id);
if (!currentVersion.Name.InvariantEquals(changedItem.Name))
{
// Additional checks here (or in the above condition) - role / property / etc...
item.Name = currentVersion.Name;
}
}
}
}
You can read more about specific events here too: https://our.umbraco.org/documentation/reference/events/contentservice-events.

Related

MVC3: Session_Start Fires twice when testing for Roles

I need to do some authentication for a web app with MVC3. The customer would like there to be a generic page to show if they do not have any of the role groups in windows AD that are allowed to use the app. I found a pretty simple way to do it, but just curious if it is a valid way or if there is something better out there.
Basically in the Session_Start in the global I am checking for User.IsInRole() and if that returns false then I do a Response.Redirect(). This question is: after it his the code in the IF statement and hits the Response.Redirect() code then it hits the session one more time before it goes to the AccessDenied page in the root of the app. Is this okay? Will it cause any issues If they are valid and does not enter the If to do the response.redirect?
//if (!User.IsInRole("test_user"))
//{
// Response.Redirect("~/AccessDenied.aspx", true);
//}
I would recommend you to write your Authorization filter for MVC3 and do this type of logic there:
public class RoleFilter: AuthorizeAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext filterContext)
{
if (!User.IsInRole("test_user"))
{
filterContext.HttpContext.Response.StatusCode = 302;
filterContext.Result = new RedirectResult("~/AcessDenied.aspx");
}
}
}
Also I wouldn't recommend you to use Response.Redirect because it aborts current thread.

Extending Windows Authentication in ASP.NET MVC 3 Application

after a lot of googling and reading several solutions on how to manage mixed mode authentication in ASP.NET apps, I still have no fitting solution for my problem.
I've got to implement an intranet application for a bunch of different user groups. Until now i've used windows authenthication which was very simple to implement. My problems arise when it comes to authorizing usergroups for special application functionalities.
Using [Authorize(Users = "DOMAIN\\USER")] works great but due to that i have no access to the active directory managament, it is impossible to me to configure rolemanagement in the way I need it for my application.
What I'd like to do is defining custom roles and memberships in addition to the ones that are defined within the active directory (is such an extension possible? e.g. by implementing an own membershipprovider?).
What do you think is the best solution for my problem. Do I really have to implement a complex mixed mode authentication with forms authentication in addition to windows authentication?
Used Technologies:
MS SQL Server 2008
MS VS 2010
ASP.NET MVC 3 - Razor View Engine
Telerik Extensions for ASP.NET MVC
IIS 7 on Windows Server 2008
EDIT (final solution thanks to the help of dougajmcdonald):
After pointing me to use a custom IPrincipal implementation I've found some solutions here and here. Putting everything together I came to the following solution:
1.Create a custom principal implementation:
public class MyPrincipal: WindowsPrincipal
{
List<string> _roles;
public MyPrincipal(WindowsIdentity identity) : base(identity) {
// fill roles with a sample string just to test if it works
_roles = new List<string>{"someTestRole"};
// TODO: Get roles for the identity out of a custom DB table
}
public override bool IsInRole(string role)
{
if (base.IsInRole(role) || _roles.Contains(role))
{
return true;
}
else
{
return false;
}
}
}
2.Integrate my custom principal implementation into the application through extending the "Global.asax.cs" file:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
{
WindowsIdentity wi = (WindowsIdentity)HttpContext.Current.User.Identity;
MyPrincipal mp = new MyPrincipal(wi);
HttpContext.Current.User = mp;
}
}
3.Use my custom roles for authorization in my application
public class HomeController : Controller
{
[Authorize(Roles= "someTestRole")]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
}
It works!!! yeah!
I'm not sure if this still applies in MVC, but in Webforms one way to do this would be as follows:
Create a new IPrincipal implementation perhaps extending WindowsPrincipal
In this class, give it a collection of roles (your own custom roles)
Populate those roles, by perhaps getting them from the DB.
Override IsInRole to return true if the role provided is EITHER true from the base call (WindowsAuthentication/Role) OR from your own custom role collection.
This way you can still hook into Principal.IsInRole("MyRole") and also the principal [PrincipalPermission()] annotation.
Hope it helps.
EDIT in answer to q's:
To integrate the principal into the authorisation you need to write your own method for OnAuthenticate in the global.asax for the type of authentication, so I would guess for you, something like this:
void WindowsAuthentication_OnAuthenticate(object sender, WindowsAuthenticationEventArgs e)
{
// ensure we have a name and made it through authentication
if (e.Identity != null && e.Identity.IsAuthenticated)
{
//create your principal, pass in the identity so you know what permissions are tied to
MyCustomePrincipal opPrincipal = new MyCustomePrincipal(e.Identity);
//assign your principal to the HttpContext.Current.User, or perhaps Thread.Current
HttpContext.Current.User = opPrincipal;
}
}
I believe Authorize came in at a later date to the PrincipalPermission, but I'm not too sure as to when/why of the differences I'm afraid :( - sorry!

User's last login with Forms Authentication

I need to record the last login info of a user into the database. So I'm thinking to put something like the code below in all of my controllers' action methods.
if (User.Identity.IsAuthenticated == true)
{
// save DateTime.Now for this user
}
However, I have a moderately huge number of controller actions. And this approach will also be difficult to manage in the long run. Is there a simpler and best way to do this?
Thanks.
[edit]
With Forms Authentication, when the logon form's "remember me" is checked, the next time the user comes back they don't go to the login page anymore. That's the reason I came up with the idea above. But I really don't like it and I will appreciate your suggestions.
The built-in MembershipUser type (which you are likely using) has a LastLoginDate and a LastActivityDate property. You should use those if possible.
If not, you can handle both "last login" and "last access" with an HttpModule:
public class LastTimeModule : IHttpModule
{
public void Init(HttpApplication context)
{
// This event is raised prior to *any* handler request ("last access")
context.PreRequestHandlerExecute += new EventHandler(OnPreRequest);
// This event is raised when a user is authenticated ("last login")
context.PostAuthenticateRequest += new EventHandler(OnPostAuthenticateRequest);
}
void OnPostAuthenticateRequest(object sender, EventArgs e)
{
// Log time here
}
public void OnPreRequest(Object source, EventArgs e)
{
// Log time here
}
}
If you're using MVC you could create a custom action filter and apply it to your controllers.

Implementing a WAP site using ASP.NET-MVC

We plan on implementing a WAP site using ASP.NET-MVC.
Has anyone any experiance of this? Are there any Gotchas?
We will also be implementing a "standard" web site for browsers. Would it be possible to have a single set of Models and Controllers, and just have seperate views for each site?
It is possible to have for the most part a single set of models and controllers.
The way to do it will be via implementing the following Theming/Templating engine.
[Theming Support][1]
I piggy backed my solution on top of a Theming/Templating engine.
The major deviation from the article source is in the Global.asax.cs file where you need to add the following lines of code:
protected void Application_BeginRequest(Object Sender, EventArgs e)
{
SetTheme();
}
//this will set the responses Content Type to xhtml and is necessary as C# sends the WML response header
protected void Application_PreSendRequestHeaders(Object Sender, EventArgs e)
{
if (this.Context.Items["themeName"].ToString() == "xhtml")
{
this.Context.Response.ContentType = "application/vnd.wap.xhtml+xml";
}
}
private void SetTheme()
{
//set the content type for the ViewEngine to utilize.
HttpContext context = this.Context;
MobileCapabilities currentCapabilities = (MobileCapabilities)context.Request.Browser;
String prefMime = currentCapabilities.PreferredRenderingMime;
string accept = context.Request.ServerVariables["HTTP_ACCEPT"];
context.Items.Remove("theme");
context.Items.Remove("themeName");
if (accept.Contains("application/vnd.wap.xhtml+xml"))
{
context.Items.Add("themeName", "xhtml");
}
else if (prefMime == "text/vnd.wap.wml")
{
context.Items.Add("themeName", "WAP");
}
if (!context.Items.Contains("themeName"))
{
context.Items.Add("themeName", "Default");
}
}
I know I had to make a couple of code changes to make it MVC 1 compatible, but I can't remember the exact changes.
The other major problem I had was debugging the output. For this I used firefox with an extension ([User Agent Switcher][2]) that I've changed to add Accept Types to it.
For WAP2/XHTML1.2 the Accept Types are: text/html,application/vnd.wap.xhtml+xml,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Obviously you need your masterpage and content pages to adhere to WML or XHTML1.2
[1]: http://frugalcoder.us/post/2008/11/13/ASPNet-MVC-Theming.aspx Theming Support
[2]: http://chrispederick.com/work/user-agent-switcher/ User Agent Switcher

How to config Asp.net Mvc for redirecting every request to configuration page?

For Asp.net Mvc project, I need to redirect every request to configuration page when user(should be admin of this website) visit this website at the first time. This operation like default login page(every request will be redirect to default login page if access denied).
After user config the configuration file, Route table will be mapped to normal controllers.
Ps. This page should helps Admin for detect error configuration and easy to deploy.
Update #1
I try to use ASP.NET MVC WebFormRouting Demo on Codeplex. But I can't redirect when user visit some existing page like "~/AccessDenied.aspx" or "~/web.config".
routes.MapWebFormRoute("RedirectToConfig", "{*anything}", "~/App_Config");
Thanks,
From your description, this appears to be an authorization concern, so I would recommend a custom Authorize attribute class (inherit from AuthorizeAttribute).
From here you can override the OnAuthorization method where you can check if the user has completed your required configuration steps and set the filterContext.Result accordingly. A basic implementation would look something like this (this assumes you have a valid /Account/Configure route):
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
var user = ; // get your user object
if(user.IsConfigured == false) // example
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{
"ConfigureUserRoute",
filterContext.RouteData.Values["ConfigureUserRoute"]
},
{"controller", "Account"},
{"action", "Configure"}
});
return;
}
}
}
You can find other examples of how to create a custom AuthorizeAttribute class here on StackOverflow.
2 ideas:
Use a catch-all rule on top of your routing table and put a constraint on it that checks for the config status
Put the code for this check in Application_BeginRequest in GlobalAsax
Details for the catch-all idea:
Create a rule with url "{*path}" and put it first in your list
Create a constraint to activate this rule only in case the configuration is not done yet
Create a simple controller e.g. ConfigController with a single action that does nothing but a RedirectToUrl("config.aspx")
But the solution in Application_BeginRequest would be simpler, since the whole code to handle this in one place
Now, I can apply technique from my another question to solve this problem. By keep some value in static instance when application is starting. Please look at the following code.
partial ConfigBootstapper.cs
public class ConfigBootstapper
{
public static EnableRedirectToConfigManager = false;
}
partial ConfigModule.cs
void HttpApplication_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
if (ConfigBootstapper.EnableRedirectToConfigManager)
{
app.Response.Redirect("~/App_Config");
}
}
partial Global.asax
protected void Application_Start()
{
[logic for setting ConfigBootstapper.EnableRedirectToConfigManager value]
}
PS. Don't forget to checking some condition that cause infinite-loop before redirect.

Resources