/// <summary> /// Called after the command is invoked. /// </summary> protected internal override void OnCommandExecuted(RedwoodRequestContext context, ActionInfo actionInfo, Exception exception) { if (exception != null) { OnException(context, actionInfo, exception); } }
public void VerifyToken(RedwoodRequestContext context, string token) { if (context == null) throw new ArgumentNullException("context"); if (string.IsNullOrWhiteSpace(token)) throw new SecurityException("CSRF protection token is missing."); // Get application key helper var keyHelper = new ApplicationKeyHelper(context.Configuration.Security); // Get token var userIdentity = ProtectionHelpers.GetUserIdentity(context); var requestIdentity = ProtectionHelpers.GetRequestIdentity(context); byte[] tokenSid; try { var tokenData = Convert.FromBase64String(token); tokenSid = keyHelper.UnprotectData(tokenData, KDF_LABEL_TOKEN, userIdentity, requestIdentity); } catch (Exception ex) { // Incorrect Base64 formatting of crypto protection error throw new SecurityException("CSRF protection token is invalid.", ex); } // Get SID from cookie and compare with token one var cookieSid = this.GetOrCreateSessionId(context); if (!cookieSid.SequenceEqual(tokenSid)) throw new SecurityException("CSRF protection token is invalid."); }
/// <summary> /// Process an individual request. /// </summary> public override Task Invoke(IOwinContext context) { // create the context var redwoodContext = new RedwoodRequestContext() { OwinContext = context, Configuration = configuration, ResourceManager = new ResourceManager(configuration) }; // attempt to translate Googlebot hashbang espaced fragment URL to a plain URL string. string url; if (!TryParseGooglebotHashbangEscapedFragment(context.Request.QueryString, out url)) { url = context.Request.Path.Value; } url = url.Trim('/'); // find the route IDictionary<string, object> parameters = null; var route = configuration.RouteTable.FirstOrDefault(r => r.IsMatch(url, out parameters)); if (route != null) { // handle the request redwoodContext.Route = route; redwoodContext.Parameters = parameters; return route.ProcessRequest(redwoodContext); } else { // we cannot handle the request, pass it to another component return Next.Invoke(context); } }
public void CommandResolver_Valid_SimpleTest() { var path = new[] { "A[0]" }; var command = "Test(StringToPass, _parent.NumberToPass)"; var testObject = new { A = new[] { new TestA() { StringToPass = "******" } }, NumberToPass = 16 }; var viewRoot = new RedwoodView() { DataContext = testObject }; viewRoot.SetBinding(Validate.TargetProperty, new ValueBindingExpression("_root")); var placeholder = new HtmlGenericControl("div"); placeholder.SetBinding(RedwoodBindableControl.DataContextProperty, new ValueBindingExpression(path[0])); viewRoot.Children.Add(placeholder); var button = new Button(); button.SetBinding(ButtonBase.ClickProperty, new CommandBindingExpression(command)); placeholder.Children.Add(button); var resolver = new CommandResolver(); var context = new RedwoodRequestContext() { ViewModel = testObject }; context.ModelState.ValidationTargetPath = KnockoutHelper.GetValidationTargetExpression(button, true); resolver.GetFunction(viewRoot, context, path, command).GetAction()(); Assert.AreEqual(testObject.NumberToPass, testObject.A[0].ResultInt); Assert.AreEqual(testObject.A[0].ResultString, testObject.A[0].ResultString); }
/// <summary> /// Processes the request. /// </summary> public async Task ProcessRequest(RedwoodRequestContext context) { bool failedAsUnauthorized = false; Exception exception = null; try { await ProcessRequestCore(context); } catch (RedwoodInterruptRequestExecutionException) { // the response has already been generated, do nothing return; } catch (UnauthorizedAccessException ex) { failedAsUnauthorized = true; exception = ex; } if (failedAsUnauthorized) { // unauthorized error context.OwinContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; await RedwoodErrorPageMiddleware.RenderErrorResponse(context.OwinContext, exception); } }
public async Task WriteViewModelResponse(RedwoodRequestContext context, RedwoodView view) { // return the response context.OwinContext.Response.ContentType = "application/json; charset=utf-8"; var serializedViewModel = context.GetSerializedViewModel(); await context.OwinContext.Response.WriteAsync(serializedViewModel); }
internal override void OnPreRenderComplete(RedwoodRequestContext context) { EnsureControlHasId(); context.ResourceManager.AddRequiredResource(Constants.RedwoodFileUploadResourceName); context.ResourceManager.AddRequiredResource(Constants.RedwoodFileUploadCssResourceName); base.OnPreRenderComplete(context); }
/// <summary> /// Adds the post back updated controls. /// </summary> public void AddPostBackUpdatedControls(RedwoodRequestContext context) { var result = new JObject(); foreach (var control in context.PostBackUpdatedControls) { result[control.Key] = JValue.CreateString(control.Value); } context.ViewModelJson["updatedControls"] = result; }
protected internal override void OnPreRender(RedwoodRequestContext context) { if (context.IsSpaRequest) { // we need to render the HTML on postback when SPA request arrives SetValue(PostBack.UpdateProperty, true); } base.OnPreRender(context); }
private byte[] GetOrCreateSessionId(RedwoodRequestContext context) { if (context == null) throw new ArgumentNullException("context"); if (string.IsNullOrWhiteSpace(context.Configuration.Security.SessionIdCookieName)) throw new FormatException("Configured SessionIdCookieName is missing or empty."); // Get cookie manager var mgr = new ChunkingCookieManager(); // TODO: Make this configurable // Get application key helper var keyHelper = new ApplicationKeyHelper(context.Configuration.Security); // Get cookie value var sidCookieValue = mgr.GetRequestCookie(context.OwinContext, context.Configuration.Security.SessionIdCookieName); if (string.IsNullOrWhiteSpace(sidCookieValue)) { // No SID - generate and protect new one var rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); var sid = new byte[SID_LENGTH]; rng.GetBytes(sid); var protectedSid = keyHelper.ProtectData(sid, KDF_LABEL_SID); // Save to cookie sidCookieValue = Convert.ToBase64String(protectedSid); mgr.AppendResponseCookie( context.OwinContext, context.Configuration.Security.SessionIdCookieName, // Configured cookie name sidCookieValue, // Base64-encoded SID value new Microsoft.Owin.CookieOptions { HttpOnly = true, // Don't allow client script access Secure = context.OwinContext.Request.IsSecure // If request goes trough HTTPS, mark as secure only }); // Return newly generated SID return sid; } else { // Try to read from cookie try { var protectedSid = Convert.FromBase64String(sidCookieValue); var sid = keyHelper.UnprotectData(protectedSid, KDF_LABEL_SID); return sid; } catch (Exception ex) { // Incorrect Base64 formatting of crypto protection error throw new SecurityException("Value of the SessionID cookie is corrupted or has been tampered with.", ex); } } }
/// <summary> /// Called before the command is executed. /// </summary> protected internal override void OnCommandExecuting(RedwoodRequestContext context, ActionInfo actionInfo) { if (!string.IsNullOrEmpty(context.ModelState.ValidationTargetPath)) { // perform the validation context.ModelState.Errors.AddRange(viewModelValidator.ValidateViewModel(context.ModelState.ValidationTarget)); // return the model state when error occurs context.FailOnInvalidModelState(); } base.OnCommandExecuting(context, actionInfo); }
public string Unprotect(string protectedData, RedwoodRequestContext context) { if (protectedData == null) throw new ArgumentNullException("protectedData"); if (string.IsNullOrWhiteSpace(protectedData)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "protectedData"); if (context == null) throw new ArgumentNullException("context"); // Get application key helper var keyHelper = new ApplicationKeyHelper(context.Configuration.Security); // Unprotect serialized data var userIdentity = ProtectionHelpers.GetUserIdentity(context); var requestIdentity = ProtectionHelpers.GetRequestIdentity(context); return keyHelper.UnprotectString(protectedData, KDF_LABEL, userIdentity, requestIdentity); }
internal override void OnPreRenderComplete(RedwoodRequestContext context) { EnsureControlHasId(); if (Children.Count != 1 || !(Children[0] is Literal)) { throw new Exception("The <rw:InlineScript>...</rw:InlineScript> control can only contain text content!"); } var script = ((Literal)Children[0]).Text; context.ResourceManager.AddStartupScript("inlinescript_" + ID, script, Constants.RedwoodResourceName); base.OnPreRenderComplete(context); }
/// <summary> /// Initializes the view model for the specified view. /// </summary> public object InitializeViewModel(RedwoodRequestContext context, RedwoodView view) { string viewModel; if (!view.Directives.TryGetValue(Constants.ViewModelDirectiveName, out viewModel)) { throw new Exception("Couldn't find a viewmodel for the specified view!"); // TODO: exception handling } var viewModelType = Type.GetType(viewModel); if (viewModelType == null) { throw new Exception(string.Format("Couldn't create a class of type '{0}'!", viewModel)); // TODO: exception handling } return CreateViewModelInstance(viewModelType); }
public Page BuildPage(RedwoodRequestContext context, MarkupFile markupFile) { try { var serializer = new RwHtml.RwHtmlSerializer(); var result = serializer.LoadFromString(markupFile.Contents); return (Page)result; } catch (ParserException ex) { // add the file name to the exception and rethrow ex.FileName = markupFile.FileName; throw; } }
public void RenderPage(RedwoodRequestContext context, RedwoodView view) { // embed resource links EmbedResourceLinks(view); // prepare the render context var renderContext = new RenderContext(context); // get the HTML using (var textWriter = new StringWriter()) { var htmlWriter = new HtmlWriter(textWriter, context); view.Render(htmlWriter, renderContext); context.RenderedHtml = textWriter.ToString(); } }
public void CommandResolver_CannotCallSetter() { var testObject = new TestA() { StringToPass = "******" }; var viewRoot = new RedwoodView() { DataContext = testObject }; viewRoot.SetBinding(Validate.TargetProperty, new ValueBindingExpression("_root")); viewRoot.SetBinding(RedwoodProperty.Register<Action, RedwoodView>("Test"), new CommandBindingExpression("set_StringToPass(StringToPass)")); var path = new string[] { }; var command = "set_StringToPass(StringToPass)"; var resolver = new CommandResolver(); var context = new RedwoodRequestContext() { ViewModel = testObject }; resolver.GetFunction(viewRoot, context, path, command).GetAction()(); }
private const int SID_LENGTH = 32; // 256-bit identifier #endregion Fields #region Methods public string GenerateToken(RedwoodRequestContext context) { if (context == null) throw new ArgumentNullException("context"); // Get SID var sid = this.GetOrCreateSessionId(context); // Get application key helper var keyHelper = new ApplicationKeyHelper(context.Configuration.Security); // Get token var userIdentity = ProtectionHelpers.GetUserIdentity(context); var requestIdentity = ProtectionHelpers.GetRequestIdentity(context); var tokenData = keyHelper.ProtectData(sid, KDF_LABEL_TOKEN, userIdentity, requestIdentity); // Return encoded token return Convert.ToBase64String(tokenData); }
public async Task<MarkupFile> GetMarkup(RedwoodRequestContext context, string applicationPhysicalPath) { try { var fileName = Path.Combine(applicationPhysicalPath, context.Presenter.ResolveViewFileName()); using (var sr = new StreamReader(fileName, Encoding.UTF8)) { return new MarkupFile() { Contents = await sr.ReadToEndAsync(), FileName = fileName }; } } catch (IOException ex) { throw new RedwoodHttpException("The markup file '{0}' could not be loaded. See InnerException for details.", ex); } }
public ViewModelBase LocateViewModel(RedwoodRequestContext context, Page page) { Type type; try { type = context.Presenter.ResolveViewModelType(); } catch (Exception ex) { throw new RedwoodHttpException("The viewmodel class for requested page was not found!", ex); } try { return (ViewModelBase)Activator.CreateInstance(type); } catch (Exception ex) { throw new RedwoodHttpException(string.Format("The instance of viewmodel '{0}' could not be created. There is not a default parameterless constructor, or an exception was thrown when the constructor was called. See InnerException for details.", type.FullName), ex); } }
public async Task RenderPage(RedwoodRequestContext context, Page page, string serializedViewModel) { // set up integration scripts //var integrationScripts = page.OfType<IntegrationScripts>().Single(); //integrationScripts.SerializedViewModel = serializedViewModel; //integrationScripts.InternalScriptUrls = new List<string>() { // context.OwinContext.Request.PathBase + "/Scripts/knockout-3.0.0.js", // context.OwinContext.Request.PathBase + "/Scripts/knockout.mapping-latest.js", // context.OwinContext.Request.PathBase + "/Scripts/knockout.validation.js", // context.OwinContext.Request.PathBase + "/Scripts/Redwood.js", // context.OwinContext.Request.PathBase + "/Data.js" //}; // get the HTML var writer = new HtmlWriter(); page.Render(writer); var html = writer.ToString(); // return the response context.OwinContext.Response.ContentType = "text/html; charset=utf-8"; await context.OwinContext.Response.WriteAsync(html); }
public virtual void CreateHeaderControls(RedwoodRequestContext context, GridView gridView, string sortCommandPath, HtmlGenericControl cell) { if (AllowSorting) { if (string.IsNullOrEmpty(sortCommandPath)) { throw new Exception("Cannot use column sorting where no sort command is specified. Either put IGridViewDataSet in the DataSource property of the GridView, or set the SortChanged command on the GridView to implement custom sorting logic!"); } var sortExpression = GetSortExpression(); // TODO: verify that sortExpression is a single property name var linkButton = new LinkButton() { Text = HeaderText }; linkButton.SetBinding(ButtonBase.ClickProperty, new CommandBindingExpression(string.Format("{0} (\"{1}\")", sortCommandPath, sortExpression))); cell.Children.Add(linkButton); } else { var literal = new Literal(HeaderText); cell.Children.Add(literal); } }
/// <summary> /// Processes the request and renders the output. /// </summary> private async Task ProcessRequestCore(RedwoodRequestContext context) { // get the page markup var markup = await MarkupFileLoader.GetMarkup(context, context.ApplicationPhysicalPath); // build the page var page = PageBuilder.BuildPage(context, markup); // locate view model var viewModel = ViewModelLoader.LocateViewModel(context, page); // init the view model lifecycle page.DataContext = viewModel; viewModel.Init(context); if (context.OwinContext.Request.Method == "GET") { // standard get await viewModel.Load(context, false); } else if (context.OwinContext.Request.Method == "POST") { // postback Action invokedCommand; using (var sr = new StreamReader(context.OwinContext.Request.Body)) { ViewModelSerializer.DeseralizePostData(sr.ReadToEnd(), viewModel, out invokedCommand); } await viewModel.Load(context, true); // invoke the postback event invokedCommand(); } else { // unknown HTTP method await RenderErrorResponse(context, HttpStatusCode.MethodNotAllowed, new RedwoodHttpException("Only GET and POST methods are supported!")); return; } viewModel.PreRender(context); // render the output var serializedViewModel = ViewModelSerializer.SerializeViewModel(viewModel); if (context.OwinContext.Request.Method == "GET") { // standard get await OutputRenderer.RenderPage(context, page, serializedViewModel); } else if (context.OwinContext.Request.Method == "POST") { // postback await OutputRenderer.RenderViewModel(context, page, serializedViewModel); } }
/// <summary> /// Renders the error response. /// </summary> public static async Task RenderErrorResponse(RedwoodRequestContext context, HttpStatusCode code, Exception error) { context.OwinContext.Response.StatusCode = (int)code; context.OwinContext.Response.ContentType = "text/html"; var template = new ErrorPageTemplate() { Error = error, ErrorCode = (int)code, ErrorDescription = code.ToString(), IpAddress = context.OwinContext.Request.RemoteIpAddress, CurrentUserName = context.OwinContext.Request.User.Identity.Name, Url = context.OwinContext.Request.Uri.ToString(), Verb = context.OwinContext.Request.Method }; if (error is ParserException) { template.FileName = ((ParserException)error).FileName; template.LineNumber = ((ParserException)error).Position.LineNumber; template.PositionOnLine = ((ParserException)error).Position.PositionOnLine; } var text = template.TransformText(); await context.OwinContext.Response.WriteAsync(text); }
/// <summary> /// Processes the request. /// </summary> public async Task ProcessRequest(RedwoodRequestContext context) { Exception error = null; try { await ProcessRequestCore(context); } catch (Exception ex) { error = ex; } if (error != null) { await RenderErrorResponse(context, HttpStatusCode.InternalServerError, error); } }
protected internal override void OnInit(RedwoodRequestContext context) { GetRoot().SetValue(Internal.IsSpaPageProperty, true); base.OnInit(context); }
/// <summary> /// Processes the request. /// </summary> public override Task ProcessRequest(RedwoodRequestContext context) { context.Presenter = presenterFactory(); return context.Presenter.ProcessRequest(context); }
public async Task RenderViewModel(RedwoodRequestContext context, Page page, string serializedViewModel) { // return the response context.OwinContext.Response.ContentType = "application/json; charset=utf-8"; await context.OwinContext.Response.WriteAsync(serializedViewModel); }
/// <summary> /// Called right before the rendering shall occur. /// </summary> internal override void OnPreRenderComplete(RedwoodRequestContext context) { context.ResourceManager.AddRequiredResource(Name); base.OnPreRenderComplete(context); }
/// <summary> /// Occurs after the page commands are executed. /// </summary> protected internal override void OnPreRender(RedwoodRequestContext context) { DataBind(context); // TODO: we should handle observable collection operations to persist controlstate of controls inside the Repeater base.OnPreRender(context); }