/// <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);
     }
 }
示例#2
0
        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.");
        }
示例#3
0
        /// <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);
            }
        }
示例#4
0
        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);
        }
示例#5
0
        /// <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);
            }
        }
示例#6
0
 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);
 }
示例#7
0
        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;
 }
示例#9
0
        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);
        }
示例#10
0
        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);
        }
示例#12
0
        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);
        }
示例#13
0
        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);
        }
示例#14
0
        /// <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);
        }
示例#15
0
 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;
     }
 }
示例#16
0
        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();
            }
        }
示例#17
0
        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()();
        }
示例#18
0
        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);
        }
示例#19
0
 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);
     }
 }
示例#20
0
        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);
            }
        }
示例#21
0
        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);
        }
示例#22
0
        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);
            }
        }
示例#23
0
        /// <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);
            }
        }
示例#24
0
        /// <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);
        }
示例#25
0
        /// <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);
            }
        }
示例#26
0
        protected internal override void OnInit(RedwoodRequestContext context)
        {
            GetRoot().SetValue(Internal.IsSpaPageProperty, true);

            base.OnInit(context);
        }
示例#27
0
 /// <summary>
 /// Processes the request.
 /// </summary>
 public override Task ProcessRequest(RedwoodRequestContext context)
 {
     context.Presenter = presenterFactory();
     return context.Presenter.ProcessRequest(context);
 }
示例#28
0
 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);
 }
示例#29
0
 /// <summary>
 /// Called right before the rendering shall occur.
 /// </summary>
 internal override void OnPreRenderComplete(RedwoodRequestContext context)
 {
     context.ResourceManager.AddRequiredResource(Name);
     base.OnPreRenderComplete(context);
 }
示例#30
0
 /// <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);
 }