Beispiel #1
0
        /// <summary>
        /// Performs the data-binding and builds the controls inside the <see cref="Repeater"/>.
        /// </summary>
        private void DataBind(DotvvmRequestContext context)
        {
            var dataSourceBinding = GetDataSourceBinding();

            var index = 0;
            var dataSource = DataSource;
            if (dataSource != null)
            {
                var items = GetIEnumerableFromDataSource(dataSource).Cast<object>().ToArray();
                if (lastBoundArray != null)
                {
                    if (lastBoundArray.SequenceEqual(items)) return;
                }
                Children.Clear();
                foreach (var item in items)
                {
                    var placeholder = new DataItemContainer { DataItemIndex = index };
                    ItemTemplate.BuildContent(context, placeholder);
                    Children.Add(placeholder);
                    placeholder.SetBinding(DataContextProperty, GetItemBinding((IList)items, dataSourceBinding.GetKnockoutBindingExpression(), index));
                    Debug.Assert(placeholder.properties[DataContextProperty] != null);
                    index++;
                }
            }
        }
Beispiel #2
0
 /// <summary>
 /// Ensures the redirect required by the OWIN Security middleware is properly handled by DotVVM client library.
 /// </summary>
 public static void ApplyRedirectResponse(IOwinContext context, string redirectUri)
 {
     if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
     {
         DotvvmRequestContextExtensions.SetRedirectResponse(DotvvmRequestContext.GetCurrent(DotvvmMiddleware.ConvertHttpContext(context)), redirectUri, (int)HttpStatusCode.Redirect, true);
     }
 }
Beispiel #3
0
        public void VerifyToken(DotvvmRequestContext 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.");
        }
Beispiel #4
0
        /// <summary>
        /// Processes the request.
        /// </summary>
        public async Task ProcessRequest(DotvvmRequestContext context)
        {
            bool failedAsUnauthorized = false;
            Exception exception = null;

            try
            {
                await ProcessRequestCore(context);
            }
            catch (DotvvmInterruptRequestExecutionException)
            {
                // 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 DotvvmErrorPageMiddleware.RenderErrorResponse(context.OwinContext, exception);
            }
        }
 /// <summary>
 /// Fixes the response created by the OWIN Security Challenge call to be accepted by DotVVM client library.
 /// </summary>
 public static void ApplyRedirectResponse(IOwinContext context, string redirectUri)
 {
     if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
     {
         DotvvmRequestContext.SetRedirectResponse(context, redirectUri, (int)HttpStatusCode.Redirect);
     }
 }
 public async Task WriteViewModelResponse(DotvvmRequestContext context, DotvvmView view)
 {
     // return the response
     context.OwinContext.Response.ContentType = "application/json; charset=utf-8";
     var serializedViewModel = context.GetSerializedViewModel();
     await context.OwinContext.Response.WriteAsync(serializedViewModel);
 }
        public void RenderPostbackUpdatedControls(DotvvmRequestContext context, DotvvmView page)
        {
            var stack = new Stack<DotvvmControl>();
            stack.Push(page);
            do
            {
                var control = stack.Pop();

                object val;
                if (control.properties != null &&
                    control.properties.TryGetValue(PostBack.UpdateProperty, out val) &&
                    val is bool && (bool)val)
                {
                    using (var w = new StringWriter())
                    {
                        control.Render(new HtmlWriter(w, context), context);

                        var clientId = control.GetDotvvmUniqueId() as string;
                        if (clientId == null)
                        {
                            throw new DotvvmControlException(control, "This control cannot use PostBack.Update=\"true\" because it has dynamic ID. This happens when the control is inside a Repeater or other data-bound control and the RenderSettings.Mode=\"Client\".");
                        }
                        context.PostBackUpdatedControls[clientId] = w.ToString();
                    }
                }
                else
                {
                    foreach (var child in control.Children)
                    {
                        stack.Push(child);
                    }
                }

            } while (stack.Count > 0);
        }
 /// <summary>
 /// Called after the command is invoked.
 /// </summary>
 protected internal override void OnCommandExecuted(DotvvmRequestContext context, ActionInfo actionInfo, Exception exception)
 {
     if (exception != null)
     {
         OnException(context, actionInfo, exception);
     }
 }
        /// <summary>
        /// Builds the <see cref="DotvvmView"/> for the specified HTTP request, resolves the master page hierarchy and performs the composition.
        /// </summary>
        public DotvvmView BuildView(DotvvmRequestContext context)
        {
            // get the page markup
            var markup = markupFileLoader.GetMarkupFileVirtualPath(context);
            

            // build the page
            var pageBuilder = controlBuilderFactory.GetControlBuilder(markup);
            var contentPage = pageBuilder.BuildControl(controlBuilderFactory) as DotvvmView;

            FillsDefaultDirectives(contentPage, context.Configuration);

            // check for master page and perform composition recursively
            while (IsNestedInMasterPage(contentPage))
            {
                // load master page
                var masterPageFile = contentPage.Directives[ParserConstants.MasterPageDirective];
                var masterPage = (DotvvmView)controlBuilderFactory.GetControlBuilder(masterPageFile).BuildControl(controlBuilderFactory);

                FillsDefaultDirectives(masterPage, context.Configuration);
                PerformMasterPageComposition(contentPage, masterPage);

                masterPage.ViewModelType = contentPage.ViewModelType;
                contentPage = masterPage;
            }

            // verifies the SPA request
            VerifySpaRequest(context, contentPage);

            return contentPage;
        }
        public void TestInit()
        {
            configuration = DotvvmConfiguration.CreateDefault();
            configuration.ServiceLocator.RegisterSingleton<IDataProtectionProvider>(() => new DpapiDataProtectionProvider("dotvvm test"));
            configuration.Security.SigningKey = Convert.FromBase64String("Uiq1FXs016lC6QaWIREB7H2P/sn4WrxkvFkqaIKpB27E7RPuMipsORgSgnT+zJmUu8zXNSJ4BdL73JEMRDiF6A1ScRNwGyDxDAVL3nkpNlGrSoLNM1xHnVzSbocLFDrdEiZD2e3uKujguycvWSNxYzjgMjXNsaqvCtMu/qRaEGc=");
            configuration.Security.EncryptionKey = Convert.FromBase64String("jNS9I3ZcxzsUSPYJSwzCOm/DEyKFNlBmDGo9wQ6nxKg=");

            serializer = new DefaultViewModelSerializer(configuration);
            context = new DotvvmRequestContext()
            {
                Configuration = configuration,
                OwinContext = new Microsoft.Owin.Fakes.StubIOwinContext()
                {
                    RequestGet = () => new Microsoft.Owin.Fakes.StubIOwinRequest()
                    {
                        UriGet = () => new Uri("http://localhost:8628/Sample1"),
                        UserGet = () => new WindowsPrincipal(WindowsIdentity.GetAnonymous()),
                        HeadersGet = () => new HeaderDictionary(new Dictionary<string, string[]>()),
                        EnvironmentGet = () => new Dictionary<string, object>()
                        {
                            { "owin.RequestPathBase", "" }
                        }
                    }
                },
                ResourceManager = new ResourceManager(configuration),
                Presenter = configuration.RouteTable.GetDefaultPresenter(),
                ViewModelSerializer = serializer
            };
        }
Beispiel #11
0
        /// <summary>
        /// Processes the request.
        /// </summary>
        public async Task ProcessRequest(DotvvmRequestContext context)
        {
            bool      failedAsUnauthorized = false;
            Exception exception            = null;

            try
            {
                await ProcessRequestCore(context);
            }
            catch (DotvvmInterruptRequestExecutionException)
            {
                // 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 DotvvmErrorPageMiddleware.RenderErrorResponse(context.OwinContext, exception);
            }
        }
Beispiel #12
0
        public void CommandResolver_Valid_SimpleTest()
        {
            var path = new[] { new ValueBindingExpression(vm => ((dynamic)vm[0]).A[0], "A()[0]") };
            var commandId = "someCommand";
            var command = new CommandBindingExpression(vm => ((TestA)vm[0]).Test(((TestA)vm[0]).StringToPass, ((dynamic)vm[1]).NumberToPass), commandId);

            var testObject = new
            {
                A = new[]
                {
                    new TestA() { StringToPass = "******" }
                },
                NumberToPass = 16
            };
            var viewRoot = new DotvvmView() { DataContext = testObject };
            viewRoot.SetBinding(Controls.Validation.TargetProperty, new ValueBindingExpression(vm => vm.Last(), "$root"));

            var placeholder = new HtmlGenericControl("div");
            placeholder.SetBinding(DotvvmBindableObject.DataContextProperty, path[0]);
            viewRoot.Children.Add(placeholder);

            var button = new Button();
            button.SetBinding(ButtonBase.ClickProperty, command);
            placeholder.Children.Add(button);

            var resolver = new CommandResolver();
            var context = new DotvvmRequestContext() { ViewModel = testObject };
            context.ModelState.ValidationTargetPath = KnockoutHelper.GetValidationTargetExpression(button);

            resolver.GetFunction(viewRoot, context, path.Select(v => v.Javascript).ToArray(), commandId).Action();

            Assert.AreEqual(testObject.NumberToPass, testObject.A[0].ResultInt);
            Assert.AreEqual(testObject.A[0].ResultString, testObject.A[0].ResultString);
        }
Beispiel #13
0
        /// <summary>
        /// Process an individual request.
        /// </summary>
        public override Task Invoke(IOwinContext context)
        {
            // create the context
            var dotvvmContext = new DotvvmRequestContext()
            {
                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
                dotvvmContext.Route = route;
                dotvvmContext.Parameters = parameters;
                return route.ProcessRequest(dotvvmContext);
            }
            else
            {
                // we cannot handle the request, pass it to another component
                return Next.Invoke(context);
            }
        }
Beispiel #14
0
 public async Task WriteHtmlResponse(DotvvmRequestContext context, DotvvmView view)
 {
     // return the response
     context.OwinContext.Response.ContentType = "text/html; charset=utf-8";
     context.OwinContext.Response.Headers["Cache-Control"] = "no-cache";
     var html = RenderPage(context, view);
     await context.OwinContext.Response.WriteAsync(html);
 }
Beispiel #15
0
 protected internal override void OnPreRender(DotvvmRequestContext context)
 {
     if (!string.IsNullOrEmpty(FormatString))
     {
         context.ResourceManager.AddCurrentCultureGlobalizationResource();
     }
     base.OnPreRender(context);
 }
Beispiel #16
0
        public override void CreateControls(DotvvmRequestContext context, DotvvmControl container)
        {
            var literal = new Literal();
            literal.FormatString = FormatString;
            literal.SetBinding(Literal.TextProperty, GetValueBinding(ValueBindingProperty));

            container.Children.Add(literal);
        }
 /// <summary>
 /// Serializes the view model.
 /// </summary>
 public string SerializeViewModel(DotvvmRequestContext context)
 {
     if (SendDiff && context.ReceivedViewModelJson != null && context.ViewModelJson["viewModel"] != null)
     {
         context.ViewModelJson["viewModelDiff"] = JsonUtils.Diff((JObject)context.ReceivedViewModelJson["viewModel"], (JObject)context.ViewModelJson["viewModel"], true);
         context.ViewModelJson.Remove("viewModel");
     }
     return context.ViewModelJson.ToString(JsonFormatting);
 }
Beispiel #18
0
        protected internal override void OnPreRender(DotvvmRequestContext context)
        {
            if (context.IsSpaRequest)
            {
                // we need to render the HTML on postback when SPA request arrives
                SetValue(PostBack.UpdateProperty, true);
            }

            base.OnPreRender(context);
        }
        /// <summary>
        /// Gets the markup file virtual path from the current request URL.
        /// </summary>
        public string GetMarkupFileVirtualPath(DotvvmRequestContext context)
        {
            // get file name
            var fileName = context.Route != null ? context.Route.VirtualPath : context.OwinContext.Request.Uri.LocalPath;
            if (!fileName.EndsWith(MarkupFile.ViewFileExtension, StringComparison.CurrentCultureIgnoreCase))
            {
                throw new Exception("The view must be a file with the .dothtml extension!");     // TODO: exception handling
            }

            return fileName;
        }
Beispiel #20
0
        /// <summary>
        /// Process an individual request.
        /// </summary>
        public override async Task Invoke(IOwinContext context)
        {
            if (Interlocked.Exchange(ref configurationSaved, 1) == 0)
            {
                VisualStudioHelper.DumpConfiguration(Configuration, Configuration.ApplicationPhysicalPath);
            }
            // create the context
            var dotvvmContext = new DotvvmRequestContext()
            {
                OwinContext         = context,
                Configuration       = Configuration,
                ResourceManager     = new ResourceManager(Configuration),
                ViewModelSerializer = Configuration.ServiceLocator.GetService <IViewModelSerializer>()
            };

            context.Set(HostingConstants.DotvvmRequestContextOwinKey, dotvvmContext);

            // 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
                dotvvmContext.Route      = route;
                dotvvmContext.Parameters = parameters;
                dotvvmContext.Query      = context.Request.Query
                                           .ToDictionary(d => d.Key, d => d.Value.Length == 1 ? (object)d.Value[0] : d.Value);

                try
                {
                    await route.ProcessRequest(dotvvmContext);

                    return;
                }
                catch (DotvvmInterruptRequestExecutionException)
                {
                    // the response has already been generated, do nothing
                    return;
                }
            }

            // we cannot handle the request, pass it to another component
            await Next.Invoke(context);
        }
Beispiel #21
0
        private byte[] GetOrCreateSessionId(DotvvmRequestContext 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);
                }
            }
        }
Beispiel #22
0
        /// <summary>
        /// Gets the markup file virtual path from the current request URL.
        /// </summary>
        public string GetMarkupFileVirtualPath(DotvvmRequestContext context)
        {
            // get file name
            var fileName = context.Route != null ? context.Route.VirtualPath : context.OwinContext.Request.Uri.LocalPath;

            if (!fileName.EndsWith(MarkupFile.ViewFileExtension, StringComparison.CurrentCultureIgnoreCase))
            {
                throw new Exception("The view must be a file with the .dothtml extension!");     // TODO: exception handling
            }

            return(fileName);
        }
        /// <summary>
        /// Builds the view model for the client.
        /// </summary>
        public void BuildViewModel(DotvvmRequestContext context)
        {
            // serialize the ViewModel
            var serializer = CreateJsonSerializer();
            var viewModelConverter = new ViewModelJsonConverter(context.IsPostBack, viewModelMapper)
            {
                UsedSerializationMaps = new HashSet<ViewModelSerializationMap>()
            };
            serializer.Converters.Add(viewModelConverter);
            var writer = new JTokenWriter();
            try
            {
                serializer.Serialize(writer, context.ViewModel);
            }
            catch (Exception ex)
            {
                throw new Exception($"Could not serialize viewModel of type { context.ViewModel.GetType().Name }. Serialization failed at property { writer.Path }. {GeneralViewModelRecomendations}", ex);
            }

            // persist CSRF token
            writer.Token["$csrfToken"] = context.CsrfToken;

            // persist encrypted values
            if (viewModelConverter.EncryptedValues.Count > 0)
                writer.Token["$encryptedValues"] = viewModelProtector.Protect(viewModelConverter.EncryptedValues.ToString(Formatting.None), context);

            // serialize validation rules
            var validationRules = SerializeValidationRules(viewModelConverter);

            // create result object
            var result = new JObject();
            result["viewModel"] = writer.Token;
            result["url"] = context.OwinContext.Request.Uri.PathAndQuery;
            result["virtualDirectory"] = DotvvmMiddleware.GetVirtualDirectory(context.OwinContext);
            if (context.ResultIdFragment != null)
            {
                result["resultIdFragment"] = context.ResultIdFragment;
            }
            if (context.IsPostBack || context.IsSpaRequest)
            {
                result["action"] = "successfulCommand";
                var renderedResources = new HashSet<string>(context.ReceivedViewModelJson?["renderedResources"]?.Values<string>() ?? new string[] { });
                result["resources"] = BuildResourcesJson(context, rn => !renderedResources.Contains(rn));
            }
            else
            {
                result["renderedResources"] = JArray.FromObject(context.ResourceManager.RequiredResources);
            }
            // TODO: do not send on postbacks
            if (validationRules.Count > 0) result["validationRules"] = validationRules;

            context.ViewModelJson = result;
        }
        /// <summary>
        /// Called before the command is executed.
        /// </summary>
        protected internal override void OnCommandExecuting(DotvvmRequestContext 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);
        }
Beispiel #25
0
        protected string RenderPage(DotvvmRequestContext context, DotvvmView view)
        {
            // embed resource links
            EmbedResourceLinks(view);

            // prepare the render context
            // get the HTML
            using (var textWriter = new StringWriter())
            {
                var htmlWriter = new HtmlWriter(textWriter, context);
                view.Render(htmlWriter, context);
                return textWriter.ToString();
            }
        }
Beispiel #26
0
        /// <summary>
        /// Process an individual request.
        /// </summary>
        public override async Task Invoke(IOwinContext context)
        {
            if (Interlocked.Exchange(ref configurationSaved, 1) == 0) {
                VisualStudioHelper.DumpConfiguration(Configuration, Configuration.ApplicationPhysicalPath);
            }
            // create the context
            var dotvvmContext = new DotvvmRequestContext()
            {
                OwinContext = context,
                Configuration = Configuration,
                ResourceManager = new ResourceManager(Configuration),
                ViewModelSerializer = Configuration.ServiceLocator.GetService<IViewModelSerializer>()
            };
            context.Set(HostingConstants.DotvvmRequestContextOwinKey, dotvvmContext);

            // 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
                dotvvmContext.Route = route;
                dotvvmContext.Parameters = parameters;
                dotvvmContext.Query = context.Request.Query
                    .ToDictionary(d => d.Key, d => d.Value.Length == 1 ? (object) d.Value[0] : d.Value);

                try
                {
                    await route.ProcessRequest(dotvvmContext);
                    return;
                }
                catch (DotvvmInterruptRequestExecutionException)
                {
                    // the response has already been generated, do nothing
                    return;
                }
            }
            
            // we cannot handle the request, pass it to another component
            await Next.Invoke(context);
        }
Beispiel #27
0
        internal override void OnPreRenderComplete(DotvvmRequestContext context)
        {
            EnsureControlHasId();

            if (Children.Count != 1 || !(Children[0] is Literal))
            {
                throw new Exception("The <dot:InlineScript>...</dot:InlineScript> control can only contain text content!");
            }
            
            var script = ((Literal)Children[0]).Text;
            context.ResourceManager.AddStartupScript("inlinescript_" + ID, script, Constants.DotvvmResourceName);
            
            base.OnPreRenderComplete(context);
        }
        /// <summary>
        /// Initializes the view model for the specified view.
        /// </summary>
        public object InitializeViewModel(DotvvmRequestContext context, DotvvmView 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);
        }
Beispiel #29
0
 /// <summary>
 /// Processes the request.
 /// </summary>
 public async Task ProcessRequest(DotvvmRequestContext context)
 {
     try
     {
         await ProcessRequestCore(context);
     }
     catch (UnauthorizedAccessException)
     {
         context.OwinContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
     }
     catch (DotvvmControlException ex)
     {
         ex.FileName = Path.Combine(ApplicationPath, ex.FileName);
         throw;
     }
 }
Beispiel #30
0
        public void RenderPage(DotvvmRequestContext context, DotvvmView 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();
            }
        }
Beispiel #31
0
 /// <summary>
 /// Processes the request.
 /// </summary>
 public async Task ProcessRequest(DotvvmRequestContext context)
 {
     try
     {
         await ProcessRequestCore(context);
     }
     catch (UnauthorizedAccessException)
     {
         context.OwinContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
     }
     catch (DotvvmControlException ex)
     {
         ex.FileName = Path.Combine(ApplicationPath, ex.FileName);
         throw;
     }
 }
 /// <summary>
 /// If the request is SPA request, we need to verify that the page contains the same SpaContentPlaceHolder.
 /// Also we need to check that the placeholder is the same.
 /// </summary>
 private void VerifySpaRequest(DotvvmRequestContext context, DotvvmView page)
 {
     if (context.IsSpaRequest)
     {
         var spaContentPlaceHolders = page.GetAllDescendants().OfType<SpaContentPlaceHolder>().ToList();
         if (spaContentPlaceHolders.Count > 1)
         {
             throw new Exception("Multiple controls of type <dot:SpaContentPlaceHolder /> found on the page! This control can be used only once!");   // TODO: exception handling
         }
         if (spaContentPlaceHolders.Count == 0 || spaContentPlaceHolders[0].GetSpaContentPlaceHolderUniqueId() != context.GetSpaContentPlaceHolderUniqueId())
         {
             // the client has loaded different page which does not contain current SpaContentPlaceHolder - he needs to be redirected
             context.RedirectToUrl(context.OwinContext.Request.Uri.AbsoluteUri);
         }
     }
 }
Beispiel #33
0
        public async Task ProcessStaticCommandRequest(DotvvmRequestContext context)
        {
            JObject postData;

            using (var jsonReader = new JsonTextReader(new StreamReader(context.OwinContext.Request.Body)))
            {
                postData = JObject.Load(jsonReader);
            }
            // validate csrf token
            context.CsrfToken = postData["$csrfToken"].Value <string>();
            CsrfProtector.VerifyToken(context, context.CsrfToken);

            var command    = postData["command"].Value <string>();
            var arguments  = postData["args"] as JArray;
            var lastDot    = command.LastIndexOf('.');
            var typeName   = command.Remove(lastDot);
            var methodName = command.Substring(lastDot + 1);
            var methodInfo = Type.GetType(typeName).GetMethod(methodName);

            if (!Attribute.IsDefined(methodInfo, typeof(AllowStaticCommandAttribute)))
            {
                throw new DotvvmHttpException($"This method cannot be called from the static command. If you need to call this method, add the '{nameof(AllowStaticCommandAttribute)}' to the method.");
            }
            var target          = methodInfo.IsStatic ? null : arguments[0].ToObject(methodInfo.DeclaringType);
            var methodArguments =
                arguments.Skip(methodInfo.IsStatic ? 0 : 1)
                .Zip(methodInfo.GetParameters(), (arg, parameter) => arg.ToObject(parameter.ParameterType))
                .ToArray();
            var actionInfo = new ActionInfo()
            {
                IsControlCommand = false,
                Action           = () => methodInfo.Invoke(target, methodArguments)
            };
            var filters = context.Configuration.Runtime.GlobalFilters
                          .Concat(methodInfo.DeclaringType.GetCustomAttributes <ActionFilterAttribute>())
                          .Concat(methodInfo.GetCustomAttributes <ActionFilterAttribute>())
                          .ToArray();

            var    task = ExecuteCommand(actionInfo, context, filters);
            await  task;
            object result = TaskUtils.GetResult(task);

            using (var writer = new StreamWriter(context.OwinContext.Response.Body))
            {
                writer.WriteLine(JsonConvert.SerializeObject(result));
            }
        }
Beispiel #34
0
        public void Authorize(DotvvmRequestContext context)
        {
            // the user must not be anonymous
            if (context.OwinContext.Request.User == null || !context.OwinContext.Request.User.Identity.IsAuthenticated)
            {
                SetUnauthorizedResponse(context);
            }

            // if the role is set
            if (Roles != null && Roles.Length > 0)
            {
                if (!Roles.Any(r => context.OwinContext.Request.User.IsInRole(r)))
                {
                    SetUnauthorizedResponse(context);
                }
            }
        }
Beispiel #35
0
        private const string KDF_LABEL_TOKEN = "DotVVM.Framework.Security.DefaultCsrfProtector.Token"; // Key derivation label for protecting token

        public string GenerateToken(DotvvmRequestContext 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);
        }
        /// <summary>
        /// Builds the view model for the client.
        /// </summary>
        public void BuildViewModel(DotvvmRequestContext context, DotvvmView view)
        {
            // serialize the ViewModel
            var serializer = CreateJsonSerializer();
            var viewModelConverter = new ViewModelJsonConverter()
            {
                EncryptedValues = new JArray(),
                UsedSerializationMaps = new HashSet<ViewModelSerializationMap>()
            };
            serializer.Converters.Add(viewModelConverter);
            var writer = new JTokenWriter();
            serializer.Serialize(writer, context.ViewModel);

            // persist CSRF token
            writer.Token["$csrfToken"] = context.CsrfToken;

            // persist encrypted values
            if (viewModelConverter.EncryptedValues.Count > 0)
                writer.Token["$encryptedValues"] = viewModelProtector.Protect(viewModelConverter.EncryptedValues.ToString(Formatting.None), context);

            // serialize validation rules
            var validationRules = SerializeValidationRules(viewModelConverter);

            // create result object
            var result = new JObject();
            result["viewModel"] = writer.Token;
            result["url"] = context.OwinContext.Request.Uri.PathAndQuery;
            result["virtualDirectory"] = DotvvmMiddleware.GetVirtualDirectory(context.OwinContext);
            if (context.IsPostBack || context.IsSpaRequest)
            {
                result["action"] = "successfulCommand";
                var renderedResources = new HashSet<string>(context.ReceivedViewModelJson?["renderedResources"]?.Values<string>() ?? new string[] { });
                result["resources"] = BuildResourcesJson(context, rn => !renderedResources.Contains(rn));
            }
            else
            {
                result["renderedResources"] = JArray.FromObject(context.ResourceManager.RequiredResources);
            }
            // TODO: do not send on postbacks
            if (validationRules.Count > 0) result["validationRules"] = validationRules;

            context.ViewModelJson = result;
        }
Beispiel #37
0
        private void DataBind(DotvvmRequestContext context)
        {
            Children.Clear();

            var dataSourceBinding = GetDataSourceBinding();
            // var dataSourcePath = dataSourceBinding.GetViewModelPathExpression(this, DataSourceProperty);
            var dataSource = DataSource;

            Action<string> sortCommand = null;
            if (dataSource is IGridViewDataSet)
            {
                sortCommand = ((IGridViewDataSet)dataSource).SetSortExpression; // dataSourcePath + ".SetSortExpression";
            }
            else
            {
                var sortCommandBinding = GetCommandBinding(SortChangedProperty);
                if (sortCommandBinding != null)
                {
                    sortCommand = s => sortCommandBinding.Delegate(new []{ s }.Concat(BindingExpression.GetDataContexts(this, true)).ToArray(), null);
                }
            }

            var index = 0;
            if (dataSource != null)
            {
                // create header row
                CreateHeaderRow(context, sortCommand);
                var items = GetIEnumerableFromDataSource(dataSource);
                foreach (var item in items)
                {
                    // create row
                    var placeholder = new DataItemContainer { DataItemIndex = index };
                    placeholder.SetBinding(DataContextProperty, GetItemBinding((IList)items, dataSourceBinding.GetKnockoutBindingExpression(), index));
                    Children.Add(placeholder);

                    CreateRow(context, placeholder);

                    index++;
                }
            }
        }
Beispiel #38
0
        private async Task RenderResponse(IOwinContext context, bool isPost, string errorMessage, List <UploadedFile> uploadedFiles)
        {
            var outputRenderer = configuration.ServiceLocator.GetService <IOutputRenderer>();

            if (isPost && context.Request.Headers.Get(Constants.DotvvmFileUploadAsyncHeaderName) == "true")
            {
                // modern browser - return JSON
                if (string.IsNullOrEmpty(errorMessage))
                {
                    await outputRenderer.RenderPlainJsonResponse(context, uploadedFiles);
                }
                else
                {
                    await outputRenderer.RenderPlainTextResponse(context, errorMessage);

                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                }
            }
            else
            {
                // old browser - return HTML
                var template = new FileUploadPageTemplate();
                template.FormPostUrl        = DotvvmRequestContext.TranslateVirtualPath("~/" + Constants.FileUploadHandlerMatchUrl, context);
                template.AllowMultipleFiles = context.Request.Query["multiple"] == "true";

                if (isPost)
                {
                    if (string.IsNullOrEmpty(errorMessage))
                    {
                        template.StartupScript = string.Format("reportProgress(false, 100, {0})",
                                                               JsonConvert.SerializeObject(uploadedFiles));
                    }
                    else
                    {
                        template.StartupScript = string.Format("reportProgress(false, 100, {0})",
                                                               JsonConvert.SerializeObject(errorMessage));
                    }
                }
                await outputRenderer.RenderHtmlResponse(context, template.TransformText());
            }
        }
Beispiel #39
0
 /// <summary>
 /// Processes the request.
 /// </summary>
 public async Task ProcessRequest(DotvvmRequestContext context)
 {
     try
     {
         await ProcessRequestCore(context);
     }
     catch (DotvvmInterruptRequestExecutionException)
     {
         // the response has already been generated, do nothing
         return;
     }
     catch (UnauthorizedAccessException)
     {
         context.OwinContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
     }
     catch (DotvvmControlException ex)
     {
         ex.FileName = Path.Combine(ApplicationPath, ex.FileName);
         throw;
     }
 }
Beispiel #40
0
        protected Task ExecuteCommand(ActionInfo action, DotvvmRequestContext context, IEnumerable <ActionFilterAttribute> methodFilters)
        {
            // run OnCommandExecuting on action filters
            foreach (var filter in methodFilters)
            {
                filter.OnCommandExecuting(context, action);
            }
            object result = null;

            try
            {
                result = action.Action();
            }
            catch (Exception ex)
            {
                if (ex is TargetInvocationException)
                {
                    ex = ex.InnerException;
                }
                if (ex is DotvvmInterruptRequestExecutionException)
                {
                    throw new DotvvmInterruptRequestExecutionException("The request execution was interrupted in the command!", ex);
                }
                context.CommandException = ex;
            }

            // run OnCommandExecuted on action filters
            foreach (var filter in methodFilters.Reverse())
            {
                filter.OnCommandExecuted(context, action, context.CommandException);
            }

            if (context.CommandException != null && !context.IsCommandExceptionHandled)
            {
                throw new Exception("Unhandled exception occured in the command!", context.CommandException);
            }

            return(result as Task ?? (result == null ? TaskUtils.GetCompletedTask() : Task.FromResult(result)));
        }
        /// <summary>
        /// Process an individual request.
        /// </summary>
        public override Task Invoke(IOwinContext context)
        {
            // create the context
            var dotvvmContext = new DotvvmRequestContext()
            {
                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
                dotvvmContext.Route      = route;
                dotvvmContext.Parameters = parameters;
                dotvvmContext.Query      = context.Request.Query
                                           .ToDictionary(d => d.Key, d => d.Value.Length == 1 ? (object)d.Value[0] : d.Value);

                return(route.ProcessRequest(dotvvmContext));
            }
            else
            {
                // we cannot handle the request, pass it to another component
                return(Next.Invoke(context));
            }
        }
 /// <summary>
 /// Ensures the redirect required by the ASP.NET Core Security middleware is properly handled by DotVVM client library.
 /// </summary>
 public static Task ApplyRedirectResponse(HttpContext context, string redirectUri)
 {
     DotvvmRequestContextExtensions.SetRedirectResponse(DotvvmRequestContext.GetCurrent(DotvvmMiddleware.ConvertHttpContext(context)), redirectUri, (int)HttpStatusCode.Redirect, allowSpaRedirect: false);
     throw new DotvvmInterruptRequestExecutionException();
 }
Beispiel #43
0
        public void ProcessStaticCommandRequest(DotvvmRequestContext context)
        {
            JObject postData;

            using (var jsonReader = new JsonTextReader(new StreamReader(context.OwinContext.Request.Body)))
            {
                postData = JObject.Load(jsonReader);
            }
            // validate csrf token
            context.CsrfToken = postData["$csrfToken"].Value <string>();
            CsrfProtector.VerifyToken(context, context.CsrfToken);

            var command    = postData["command"].Value <string>();
            var arguments  = postData["args"] as JArray;
            var lastDot    = command.LastIndexOf('.');
            var typeName   = command.Remove(lastDot);
            var methodName = command.Substring(lastDot + 1);
            var methodInfo = Type.GetType(typeName).GetMethod(methodName);

            if (!Attribute.IsDefined(methodInfo, typeof(StaticCommandCallableAttribute)))
            {
                throw new DotvvmHttpException("method validation failed");
            }
            var target     = methodInfo.IsStatic ? null : arguments[0].ToObject(methodInfo.DeclaringType);
            var marguments = arguments.Skip(1).Zip(methodInfo.GetParameters(),
                                                   (arg, parameter) => arg.ToObject(parameter.ParameterType)).ToArray();
            var actionInfo = new ActionInfo()
            {
                IsControlCommand = false,
                Action           = () => methodInfo.Invoke(target, marguments)
            };
            var filters = methodInfo.GetCustomAttributes <ActionFilterAttribute>();

            foreach (var filter in filters)
            {
                filter.OnCommandExecuting(context, actionInfo);
            }
            Exception exception = null;
            object    result    = null;

            try
            {
                result = methodInfo.Invoke(target, marguments);
            }
            catch (Exception ex)
            {
                if (ex is TargetInvocationException)
                {
                    ex = ex.InnerException;
                }
                if (ex is DotvvmInterruptRequestExecutionException)
                {
                    throw new DotvvmInterruptRequestExecutionException("The request execution was interrupted in the command!", ex);
                }
                exception = ex;
            }
            foreach (var filter in filters)
            {
                filter.OnCommandExecuted(context, actionInfo, exception);
            }
            if (exception != null)
            {
                throw new Exception("unhandled exception in command", exception);
            }

            using (var writer = new StreamWriter(context.OwinContext.Response.Body))
            {
                writer.WriteLine(JsonConvert.SerializeObject(result));
            }
        }
Beispiel #44
0
        public async Task ProcessRequestCore(DotvvmRequestContext context)
        {
            if (context.OwinContext.Request.Method != "GET" && context.OwinContext.Request.Method != "POST")
            {
                // unknown HTTP method
                context.OwinContext.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                throw new DotvvmHttpException("Only GET and POST methods are supported!");
            }
            if (context.OwinContext.Request.Headers["X-PostbackType"] == "StaticCommand")
            {
                ProcessStaticCommandRequest(context);
                return;
            }
            var isPostBack = DetermineIsPostBack(context.OwinContext);

            context.IsPostBack = isPostBack;
            context.ChangeCurrentCulture(context.Configuration.DefaultCulture);

            // build the page view
            var page = DotvvmViewBuilder.BuildView(context);

            page.SetValue(Internal.RequestContextProperty, context);

            // run the preinit phase in the page
            DotvvmControlCollection.InvokePageLifeCycleEventRecursive(page, LifeCycleEventType.PreInit);

            // locate and create the view model
            context.ViewModel = ViewModelLoader.InitializeViewModel(context, page);
            page.DataContext  = context.ViewModel;

            // get action filters
            var globalFilters    = context.Configuration.Runtime.GlobalFilters.ToList();
            var viewModelFilters = context.ViewModel.GetType().GetCustomAttributes <ActionFilterAttribute>(true).ToList();

            // run OnViewModelCreated on action filters
            foreach (var filter in globalFilters.Concat(viewModelFilters))
            {
                filter.OnViewModelCreated(context);
            }

            // init the view model lifecycle
            if (context.ViewModel is IDotvvmViewModel)
            {
                ((IDotvvmViewModel)context.ViewModel).Context = context;
                await((IDotvvmViewModel)context.ViewModel).Init();
            }

            // run the init phase in the page
            DotvvmControlCollection.InvokePageLifeCycleEventRecursive(page, LifeCycleEventType.Init);

            if (!isPostBack)
            {
                // perform standard get
                if (context.ViewModel is IDotvvmViewModel)
                {
                    await((IDotvvmViewModel)context.ViewModel).Load();
                }

                // run the load phase in the page
                DotvvmControlCollection.InvokePageLifeCycleEventRecursive(page, LifeCycleEventType.Load);
            }
            else
            {
                // perform the postback
                string postData;
                using (var sr = new StreamReader(context.OwinContext.Request.Body))
                {
                    postData = await sr.ReadToEndAsync();
                }
                ViewModelSerializer.PopulateViewModel(context, postData);

                // validate CSRF token
                CsrfProtector.VerifyToken(context, context.CsrfToken);

                if (context.ViewModel is IDotvvmViewModel)
                {
                    await((IDotvvmViewModel)context.ViewModel).Load();
                }

                // validate CSRF token
                CsrfProtector.VerifyToken(context, context.CsrfToken);

                // run the load phase in the page
                DotvvmControlCollection.InvokePageLifeCycleEventRecursive(page, LifeCycleEventType.Load);

                // invoke the postback command
                ActionInfo actionInfo;
                ViewModelSerializer.ResolveCommand(context, page, postData, out actionInfo);

                if (actionInfo != null)
                {
                    // get filters
                    var methodFilters = actionInfo.Binding.ActionFilters == null?globalFilters.Concat(viewModelFilters).ToArray() :
                                            globalFilters.Concat(viewModelFilters).Concat(actionInfo.Binding.ActionFilters).ToArray();

                    // run OnCommandExecuting on action filters
                    foreach (var filter in methodFilters)
                    {
                        filter.OnCommandExecuting(context, actionInfo);
                    }

                    Exception commandException = null;
                    try
                    {
                        var result = actionInfo.Action();
                        if (result is Task)
                        {
                            await(Task) result;
                        }
                    }
                    catch (Exception ex)
                    {
                        if (ex is TargetInvocationException)
                        {
                            ex = ex.InnerException;
                        }
                        if (ex is DotvvmInterruptRequestExecutionException)
                        {
                            throw new DotvvmInterruptRequestExecutionException("The request execution was interrupted in the command!", ex);
                        }
                        commandException = ex;
                    }

                    // run OnCommandExecuted on action filters
                    foreach (var filter in methodFilters)
                    {
                        filter.OnCommandExecuted(context, actionInfo, commandException);
                    }

                    if (commandException != null && !context.IsCommandExceptionHandled)
                    {
                        throw new Exception("Unhandled exception occured in the command!", commandException);
                    }
                }
            }

            if (context.ViewModel is IDotvvmViewModel)
            {
                await((IDotvvmViewModel)context.ViewModel).PreRender();
            }

            // run the prerender phase in the page
            DotvvmControlCollection.InvokePageLifeCycleEventRecursive(page, LifeCycleEventType.PreRender);

            // run the prerender complete phase in the page
            DotvvmControlCollection.InvokePageLifeCycleEventRecursive(page, LifeCycleEventType.PreRenderComplete);

            // generate CSRF token if required
            if (string.IsNullOrEmpty(context.CsrfToken))
            {
                context.CsrfToken = CsrfProtector.GenerateToken(context);
            }

            // run OnResponseRendering on action filters
            foreach (var filter in globalFilters.Concat(viewModelFilters))
            {
                filter.OnResponseRendering(context);
            }

            // render the output
            ViewModelSerializer.BuildViewModel(context);
            if (!context.IsInPartialRenderingMode)
            {
                // standard get
                await OutputRenderer.WriteHtmlResponse(context, page);
            }
            else
            {
                // postback or SPA content
                OutputRenderer.RenderPostbackUpdatedControls(context, page);
                ViewModelSerializer.AddPostBackUpdatedControls(context);
                await OutputRenderer.WriteViewModelResponse(context, page);
            }

            if (context.ViewModel != null)
            {
                ViewModelLoader.DisposeViewModel(context.ViewModel);
            }
        }
Beispiel #45
0
 public static DotvvmRequestContext GetDotvvmContext(this IOwinContext owinContext)
 {
     return(DotvvmRequestContext.GetCurrent(owinContext));
 }