상속: INotifyPropertyChanging, INotifyPropertyChanged
예제 #1
0
        public void VerifySignature()
        {
            var toSign = new Dictionary<string, string> { { "a", "xyz" }, { "b", "1234567890" }, { "c", Convert.ToBase64String(Current.Random(16)) }, { "d", "false" } };
            var sig = GetSignature("/some/dummy/path", toSign);

            var fakeAffiliate = new Affiliate { VerificationModulus = "zB3eUr66GkFESizQCnjrm1jCbhHW/vy2UoCHAMIlsOweMOnbU2y8IohlRBEBaS80CqAPlRNfjtRjzdZU3F+J/lUZqipH5sZjXyE6/rPXbvp3tlRSF0pgcQDlFYmAQWKbPKwt2PCg8/Od+wI7cBnHEfveRTjzMzfeFUzoWPiYEo0=" };
            
            var clock = new Stopwatch();
            clock.Start();

            for (int i = 0; i < 100000; i++)
            {
                Assert.IsTrue(fakeAffiliate.ConfirmSignature(sig, "/some/dummy/path", toSign), "Signature didn't pass and should have");
            }

            clock.Stop();

            Assert.IsTrue(clock.Elapsed < TimeSpan.FromSeconds(120), "100k in 30s [" + clock.Elapsed + "]");
        }
예제 #2
0
        public void ValidCallback()
        {
            var affiliate = new Affiliate { HostFilter = "dev.stackoverflow.com" };
            Assert.IsTrue(affiliate.IsValidCallback("http://dev.stackoverflow.com/blah-blah-blah"));
            Assert.IsTrue(affiliate.IsValidCallback("http://dev.stackoverflow.com/blah-blah-blah/more-blah"));
            Assert.IsTrue(affiliate.IsValidCallback("http://dev.stackoverflow.com/blah-blah-blah/more-blah?param=yesyes"));
            Assert.IsTrue(affiliate.IsValidCallback("http://dev.stackoverflow.com/blah-blah-blah/more-blah?param=yesyes&indeed=nono"));

            affiliate = new Affiliate { HostFilter = "dev.*.stackexchange.com" };
            Assert.IsTrue(affiliate.IsValidCallback("http://dev.webapps.stackexchange.com/blah-blah-blah"));
            Assert.IsTrue(affiliate.IsValidCallback("http://dev.webapps.stackexchange.com/blah-blah-blah/more-blah"));
            Assert.IsTrue(affiliate.IsValidCallback("http://dev.webapps.stackexchange.com/blah-blah-blah/more-blah?param=yesyes"));
            Assert.IsTrue(affiliate.IsValidCallback("http://dev.webapps.stackexchange.com/blah-blah-blah/more-blah?param=yesyes&indeed=nono"));
        }
예제 #3
0
        public void InvalidCallback()
        {
            var affiliate = new Affiliate { HostFilter = "dev.stackoverflow.com" };
            Assert.IsFalse(affiliate.IsValidCallback("http://dev.stackexchange.com/blah-blah-blah"));
            Assert.IsFalse(affiliate.IsValidCallback("http://dev.superuser.com/blah-blah-blah/more-blah"));
            Assert.IsFalse(affiliate.IsValidCallback("http://dev.etc.com/blah-blah-blah/more-blah?param=yesyes"));
            Assert.IsFalse(affiliate.IsValidCallback("http://example.com?indeed=http://dev.stackoverflow.com/"));

            affiliate = new Affiliate { HostFilter = "dev.*.stackexchange.com" };
            Assert.IsFalse(affiliate.IsValidCallback("http://other.stackexchange.com/blah-blah-blah"));
            Assert.IsFalse(affiliate.IsValidCallback("http://stackexchange.com/blah-blah-blah/more-blah"));
            Assert.IsFalse(affiliate.IsValidCallback("http://dev.stackexchange.com/blah-blah-blah/more-blah?param=yesyes"));
            Assert.IsFalse(affiliate.IsValidCallback("http://dev.stackoverflow.com/"));

            affiliate = new Affiliate { HostFilter = "*.stackexchange.com" };
            Assert.IsFalse(affiliate.IsValidCallback("http://one.two.stackexchange.com/"));
        }
예제 #4
0
 partial void DeleteAffiliate(Affiliate instance);
예제 #5
0
 partial void UpdateAffiliate(Affiliate instance);
예제 #6
0
 partial void InsertAffiliate(Affiliate instance);
예제 #7
0
        public ActionResult CreateAffiliate(string filter)
        {
            if (!Affiliate.IsValidFilter(filter)) return RecoverableError("Invalid host filter", new { filter });

            var c = new RSACryptoServiceProvider();
            var key = c.ExportParameters(true);

            // meeeh... it would be nice if there were a way to *fix* the exponent.  May just need to store it...
            for (int i = 0; i < key.Exponent.Length; i++)
                if (key.Exponent[i] != Affiliate.FixedExponent[i])
                    throw new Exception("Exponent not as expected!");

            var modulus = Convert.ToBase64String(key.Modulus);

            var now = Current.Now;

            var newAffiliate = new Affiliate
            {
                CreationDate = now,
                HostFilter = filter,
                OwnerUserId = Current.LoggedInUser.Id,
                VerificationModulus = modulus
            };

            Current.LoggedInUser.LastActivityDate = now;

            Current.WriteDB.Affiliates.InsertOnSubmit(newAffiliate);
            Current.WriteDB.SubmitChanges();

            // This is your key, don't lose it!
            var jsonKey =
                Json(
                    new
                    {
                        D = Convert.ToBase64String(key.D),
                        DP = Convert.ToBase64String(key.DP),
                        DQ = Convert.ToBase64String(key.DQ),
                        InverseQ = Convert.ToBase64String(key.InverseQ),
                        Modulus = Convert.ToBase64String(key.Modulus),
                        P = Convert.ToBase64String(key.P),
                        Q = Convert.ToBase64String(key.Q)
                    });


            var success = 
                Current.Email.SendEmail(
                Current.LoggedInUser.Email,
                Email.Template.AffiliateRegistered,
                new
                {
                    D = Convert.ToBase64String(key.D),
                    DP = Convert.ToBase64String(key.DP),
                    DQ = Convert.ToBase64String(key.DQ),
                    InverseQ = Convert.ToBase64String(key.InverseQ),
                    Modulus = Convert.ToBase64String(key.Modulus),
                    P = Convert.ToBase64String(key.P),
                    Q = Convert.ToBase64String(key.Q),
                    Id = newAffiliate.Id,
                    Host = newAffiliate.HostFilter
                });

            if (!success)
            {
                return IrrecoverableError("An error occurred sending the email", "This has been recorded, and will be looked into shortly");
            }

            return jsonKey;
        }
        /// <summary>
        /// Returns true if the parameters contain a valid signature for a request.
        /// </summary>
        private static bool VerifySignature(Dictionary<string, string> @params, out Affiliate validFor, out string failureReason)
        {
            if ([email protected]("authCode") || [email protected]("affId") || [email protected]("nonce"))
            {
                validFor = null;
                failureReason = "Missing parameter";
                return false;
            }

            validFor = null;

            var authCode = @params["authCode"].ToString();
            int affId;

            if (!int.TryParse(@params["affId"], out affId))
            {
                failureReason = "No affId";
                return false;
            }

            var nonce = @params["nonce"].ToString();

            string nonceMsg;
            if (!Nonces.IsValid(nonce, Current.RemoteIP, out nonceMsg))
            {
                failureReason = "Invalid Nonce [" + nonceMsg + "]";
                return false;
            }

            var affiliate = Current.ReadDB.Affiliates.SingleOrDefault(a => a.Id == affId);

            if (affiliate == null)
            {
                failureReason = "Could not find affiliate";
                return false;
            }

            var copy = new Dictionary<string, string>();
            foreach (var item in @params.Keys.Where(k => k != "authCode"))
            {
                copy[item] = @params[item];
            }

            if (authCode.HasValue() && !affiliate.ConfirmSignature(authCode, Current.RequestUri.AbsolutePath, copy))
            {
                failureReason = "Affiliate signature confirmation failed";
                return false;
            }

            validFor = affiliate;

            Nonces.MarkUsed(nonce, Current.RemoteIP);

            failureReason = null;
            return true;
        }
        /// <summary>
        /// We greatly restrict access to this controller.
        /// 
        /// It can only be entered with a request signed by a registered affiliate.
        /// That is, all GETs must carry a signed authCode.  All POSTs are protected by 
        /// our standard XSRF tricks.
        /// </summary>
        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            Current.NoCache = true;

            if (Current.PostExpectedAndNotReceived)
            {
                Current.ShouldBustFrames = false;
                filterContext.Result = PostExpectedAndNotReceived();
                return;
            }

            var @params = new Dictionary<string, string>();

            foreach (var x in filterContext.HttpContext.Request.QueryString.AllKeys)
            {
                @params[x] = filterContext.HttpContext.Request.QueryString[x];
            }

            if ([email protected]("affId") && Request.Form.AllKeys.Contains("affId"))
            {
                @params["affId"] = Request.Form["affId"];
            }

            var method = filterContext.HttpContext.Request.HttpMethod;

            string failureReason = null;

            var failure = false;
            if ([email protected]("affId"))
            {
                failureReason = "No affId";
                failure = true;
            }

            // OK, this is tricky so pay attention
            if (!failure)
            {
                ViewData["affId"] = @params["affId"];

                // Anything that's a GET in this controller comes from an affiliate
                //    that means the whole thing needs to be signed (all parameters)
                if (method == "GET")
                {
                    // HACK: Ok, we need *one* exception to all this chicanery, so we're just
                    //       hacking it in here
                    var reqUrl = filterContext.RouteData.Route as System.Web.Routing.Route;
                    if (reqUrl != null && reqUrl.Url == "affiliate/form/switch")
                    {
                        int affId;
                        if (int.TryParse(@params["affId"], out affId))
                        {
                            CurrentAffiliate = Current.ReadDB.Affiliates.SingleOrDefault(a => a.Id == affId);

                            if (CurrentAffiliate != null)
                            {
                                Current.ShouldBustFrames = false;

                                base.OnActionExecuting(filterContext);
                                return;
                            }
                        }
                    }
                    else
                    {
                        // For all other routes, confirm that they came from a registered affiliate
                        if ([email protected]("nonce"))
                        {
                            failureReason = "No Nonce";
                            failure = true;
                        }
                        if ([email protected]("authCode"))
                        {
                            failureReason = "No Auth Code";
                            failure = true;
                        }

                        string sigError;
                        if(!VerifySignature(@params, out CurrentAffiliate, out sigError))
                        {
                            failure = true;
                            failureReason = "Signature invalid - " + sigError;
                        }
                    }
                }
                else if (method == "POST")
                {
                    // Anything that's a POST comes from our own forms (in iframes)
                    //   We know this because of the XSRF token.
                    CurrentAffiliate = Current.ReadDB.Affiliates.SingleOrDefault(a => a.Id == int.Parse(@params["affId"].ToString()));

                    if (CurrentAffiliate == null)
                    {
                        failure = true;
                        failureReason = "On Post, invalid affId";
                    }
                }
                else
                {
                    // ... and anything else is a garbage request
                    failure = true;
                    failureReason = " Not GET or POST";
                }
            }

            Current.ShouldBustFrames = false; // no frame busting in this controller

            if (failure)
            {
                Current.LogException(new Exception("Affiliate Forms failure: " + failureReason));
                filterContext.Result = IrrecoverableErrorWithHelp("Affiliate Form failure:", failureReason);
                return;
            }

            var formCanaryCookie = new HttpCookie("canary", "1");
            formCanaryCookie.HttpOnly = false; // the whole point is to check for this via javascript
            formCanaryCookie.Expires = Current.Now + TimeSpan.FromMinutes(5);

            filterContext.HttpContext.Response.Cookies.Add(formCanaryCookie);

            base.OnActionExecuting(filterContext);
        }