public void TestParsePersonaAssertion()
        {
            try
            {
                Log.D(Database.TAG, "testParsePersonaAssertion");
                var sampleAssertion = "eyJhbGciOiJSUzI1NiJ9.eyJwdWJsaWMta2V5Ijp7ImFsZ29yaXRobSI6IkRTIiwieSI6ImNhNWJiYTYzZmI4MDQ2OGE0MjFjZjgxYTIzN2VlMDcwYTJlOTM4NTY0ODhiYTYzNTM0ZTU4NzJjZjllMGUwMDk0ZWQ2NDBlOGNhYmEwMjNkYjc5ODU3YjkxMzBlZGNmZGZiNmJiNTUwMWNjNTk3MTI1Y2NiMWQ1ZWQzOTVjZTMyNThlYjEwN2FjZTM1ODRiOWIwN2I4MWU5MDQ4NzhhYzBhMjFlOWZkYmRjYzNhNzNjOTg3MDAwYjk4YWUwMmZmMDQ4ODFiZDNiOTBmNzllYzVlNDU1YzliZjM3NzFkYjEzMTcxYjNkMTA2ZjM1ZDQyZmZmZjQ2ZWZiZDcwNjgyNWQiLCJwIjoiZmY2MDA0ODNkYjZhYmZjNWI0NWVhYjc4NTk0YjM1MzNkNTUwZDlmMWJmMmE5OTJhN2E4ZGFhNmRjMzRmODA0NWFkNGU2ZTBjNDI5ZDMzNGVlZWFhZWZkN2UyM2Q0ODEwYmUwMGU0Y2MxNDkyY2JhMzI1YmE4MWZmMmQ1YTViMzA1YThkMTdlYjNiZjRhMDZhMzQ5ZDM5MmUwMGQzMjk3NDRhNTE3OTM4MDM0NGU4MmExOGM0NzkzMzQzOGY4OTFlMjJhZWVmODEyZDY5YzhmNzVlMzI2Y2I3MGVhMDAwYzNmNzc2ZGZkYmQ2MDQ2MzhjMmVmNzE3ZmMyNmQwMmUxNyIsInEiOiJlMjFlMDRmOTExZDFlZDc5OTEwMDhlY2FhYjNiZjc3NTk4NDMwOWMzIiwiZyI6ImM1MmE0YTBmZjNiN2U2MWZkZjE4NjdjZTg0MTM4MzY5YTYxNTRmNGFmYTkyOTY2ZTNjODI3ZTI1Y2ZhNmNmNTA4YjkwZTVkZTQxOWUxMzM3ZTA3YTJlOWUyYTNjZDVkZWE3MDRkMTc1ZjhlYmY2YWYzOTdkNjllMTEwYjk2YWZiMTdjN2EwMzI1OTMyOWU0ODI5YjBkMDNiYmM3ODk2YjE1YjRhZGU1M2UxMzA4NThjYzM0ZDk2MjY5YWE4OTA0MWY0MDkxMzZjNzI0MmEzODg5NWM5ZDViY2NhZDRmMzg5YWYxZDdhNGJkMTM5OGJkMDcyZGZmYTg5NjIzMzM5N2EifSwicHJpbmNpcGFsIjp7ImVtYWlsIjoiamVuc0Btb29zZXlhcmQuY29tIn0sImlhdCI6MTM1ODI5NjIzNzU3NywiZXhwIjoxMzU4MzgyNjM3NTc3LCJpc3MiOiJsb2dpbi5wZXJzb25hLm9yZyJ9.RnDK118nqL2wzpLCVRzw1MI4IThgeWpul9jPl6ypyyxRMMTurlJbjFfs-BXoPaOem878G8-4D2eGWS6wd307k7xlPysevYPogfFWxK_eDHwkTq3Ts91qEDqrdV_JtgULC8c1LvX65E0TwW_GL_TM94g3CvqoQnGVxxoaMVye4ggvR7eOZjimWMzUuu4Lo9Z-VBHBj7XM0UMBie57CpGwH4_Wkv0V_LHZRRHKdnl9ISp_aGwfBObTcHG9v0P3BW9vRrCjihIn0SqOJQ9obl52rMf84GD4Lcy9NIktzfyka70xR9Sh7ALotW7rWywsTzMTu3t8AzMz2MJgGjvQmx49QA~eyJhbGciOiJEUzEyOCJ9.eyJleHAiOjEzNTgyOTY0Mzg0OTUsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDk4NC8ifQ.4FV2TrUQffDya0MOxOQlzJQbDNvCPF2sfTIJN7KOLvvlSFPknuIo5g";
                var result          = PersonaAuthorizer.ParseAssertion(sampleAssertion);
                var email           = (string)result.Get(PersonaAuthorizer.AssertionFieldEmail);
                var origin          = (string)result.Get(PersonaAuthorizer.AssertionFieldOrigin);

                Assert.AreEqual(email, "*****@*****.**");
                Assert.AreEqual(origin, "http://localhost:4984/");
                Assert.AreEqual(PersonaAuthorizer.RegisterAssertion(sampleAssertion), email);

                Uri originURL    = new Uri(origin);
                var gotAssertion = PersonaAuthorizer.AssertionForEmailAndSite(email, originURL);
                Assert.AreEqual(gotAssertion, sampleAssertion);

                // variant form of URL
                originURL    = new Uri("Http://LocalHost:4984/");
                gotAssertion = PersonaAuthorizer.AssertionForEmailAndSite(email, originURL);
                Assert.AreEqual(sampleAssertion, gotAssertion);

                var auth = new PersonaAuthorizer(email);
                Assert.AreEqual(email, auth.GetEmailAddress());
                Assert.AreEqual(null, auth.AssertionForSite(originURL));
            }
            catch (Exception e)
            {
                Assert.Fail(e.Message);
            }
        }
Example #2
0
        /// <summary>
        /// Verifies and registers a persona token for use in replication authentication
        /// </summary>
        /// <returns>The response state for further HTTP processing</returns>
        /// <param name="context">The context of the Couchbase Lite HTTP request</param>
        public static ICouchbaseResponseState RegisterPersonaToken(ICouchbaseListenerContext context)
        {
            var response = context.CreateResponse();
            var body     = context.BodyAs <Dictionary <string, object> >();

            string email = PersonaAuthorizer.RegisterAssertion(body.GetCast <string>("assertion"));

            if (email != null)
            {
                response.JsonBody = new Body(new Dictionary <string, object> {
                    { "ok", "registered" },
                    { "email", email }
                });
            }
            else
            {
                response.InternalStatus = StatusCode.BadParam;
                response.JsonBody       = new Body(new Dictionary <string, object> {
                    { "error", "invalid assertion" }
                });
            }

            return(response.AsDefaultState());
        }
Example #3
0
        private Status ParseReplicationProperties(IDictionary <string, object> properties, out bool isPush, out bool createTarget, IDictionary <string, object> results)
        {
            // http://wiki.apache.org/couchdb/Replication
            isPush       = false;
            createTarget = false;
            var sourceDict = ParseSourceOrTarget(properties, "source");
            var targetDict = ParseSourceOrTarget(properties, "target");
            var source     = sourceDict.GetCast <string>("url");
            var target     = targetDict.GetCast <string>("url");

            if (source == null || target == null)
            {
                return(new Status(StatusCode.BadRequest));
            }

            createTarget = properties.GetCast <bool>("create_target", false);
            IDictionary <string, object> remoteDict = null;
            bool targetIsLocal = Manager.IsValidDatabaseName(target);

            if (Manager.IsValidDatabaseName(source))
            {
                //Push replication
                if (targetIsLocal)
                {
                    // This is a local-to-local replication. It is not supported on .NET.
                    return(new Status(StatusCode.NotImplemented));
                }

                remoteDict = targetDict;
                if (results.ContainsKey("database"))
                {
                    results["database"] = GetExistingDatabase(source);
                }

                isPush = true;
            }
            else if (targetIsLocal)
            {
                //Pull replication
                remoteDict = sourceDict;
                if (results.ContainsKey("database"))
                {
                    Database db;
                    if (createTarget)
                    {
                        db = GetDatabase(target);
                        if (db == null)
                        {
                            return(new Status(StatusCode.DbError));
                        }
                    }
                    else
                    {
                        db = GetExistingDatabase(target);
                    }
                    results["database"] = db;
                }
            }
            else
            {
                return(new Status(StatusCode.BadId));
            }

            Uri remote;

            if (!Uri.TryCreate(remoteDict.GetCast <string>("url"), UriKind.Absolute, out remote))
            {
                Log.To.Router.W(TAG, "Unparseable replication URL <{0}> received", remoteDict.GetCast <string>("url"));
                return(new Status(StatusCode.BadRequest));
            }

            if (!remote.Scheme.Equals("http") && !remote.Scheme.Equals("https") && !remote.Scheme.Equals("ws") && !remote.Scheme.Equals("wss"))
            {
                Log.To.Router.W(TAG, "Replication URL <{0}> has unsupported scheme", remote);
                return(new Status(StatusCode.BadRequest));
            }

            var split = remote.PathAndQuery.Split('?');

            if (split.Length != 1)
            {
                Log.To.Router.W(TAG, "Replication URL <{0}> must not contain a query", remote);
                return(new Status(StatusCode.BadRequest));
            }

            var path = split[0];

            if (String.IsNullOrEmpty(path) || path == "/")
            {
                Log.To.Router.W(TAG, "Replication URL <{0}> missing database name", remote);
                return(new Status(StatusCode.BadRequest));
            }

            var database = results.Get("database");

            if (database == null)
            {
                return(new Status(StatusCode.NotFound));
            }

            if (results.ContainsKey("remote"))
            {
                results["remote"] = remote;
            }

            if (results.ContainsKey("headers"))
            {
                results["headers"] = remoteDict.Get("headers") ?? new Dictionary <string, string>();
            }

            if (results.ContainsKey("authorizer"))
            {
                var auth = remoteDict.Get("auth") as IDictionary <string, object>;
                if (auth != null)
                {
                    var persona  = auth.Get("persona") as IDictionary <string, object>;
                    var facebook = auth.Get("facebook") as IDictionary <string, object>;
                    if (persona != null)
                    {
                        string email = persona.Get("email") as string;
                        results["authorizer"] = new PersonaAuthorizer(email);
                    }
                    else if (facebook != null)
                    {
                        string email = facebook.Get("email") as string;
                        results["authorizer"] = new FacebookAuthorizer(email);
                    }
                    else
                    {
                        Log.To.Sync.W(TAG, "Invalid authorizer settings {0}",
                                      new SecureLogJsonString(auth, LogMessageSensitivity.Insecure));
                    }
                }
            }

            // Can't specify both a filter and doc IDs
            if (properties.ContainsKey("filter") && properties.ContainsKey("doc_ids"))
            {
                return(new Status(StatusCode.BadRequest));
            }

            return(new Status(StatusCode.Ok));
        }
Example #4
0
        private Status ParseReplicationProperties(IDictionary <string, object> properties, out bool isPush, out bool createTarget,
                                                  IDictionary <string, object> results)
        {
            // http://wiki.apache.org/couchdb/Replication
            isPush       = false;
            createTarget = false;

            var sourceDict = ParseSourceOrTarget(properties, "source");
            var targetDict = ParseSourceOrTarget(properties, "target");
            var source     = sourceDict.Get("url") as string;
            var target     = targetDict.Get("url") as string;

            if (source == null || target == null)
            {
                return(new Status(StatusCode.BadRequest));
            }

            createTarget = properties.Get("create_target") is bool && (bool)properties.Get("create_target");

            IDictionary <string, object> remoteDict = null;
            bool targetIsLocal = Manager.IsValidDatabaseName(target);

            if (Manager.IsValidDatabaseName(source))
            {
                //Push replication
                if (targetIsLocal)
                {
                    // This is a local-to-local replication. Turn the remote into a full URL to keep the
                    // replicator happy:
                    Database targetDb;
                    if (createTarget)
                    {
                        targetDb = Manager.SharedInstance.GetDatabase(target);
                    }
                    else
                    {
                        targetDb = Manager.SharedInstance.GetExistingDatabase(target);
                    }

                    if (targetDb == null)
                    {
                        return(new Status(StatusCode.BadRequest));
                    }

                    targetDict["url"] = "http://localhost:20000" + targetDb.Path;
                }

                remoteDict = targetDict;
                if (results.ContainsKey("database"))
                {
                    results["database"] = GetExistingDatabase(source);
                }

                isPush = true;
            }
            else if (targetIsLocal)
            {
                //Pull replication
                remoteDict = sourceDict;
                if (results.ContainsKey("database"))
                {
                    Database db;
                    if (createTarget)
                    {
                        db = GetDatabase(target);
                        if (db == null)
                        {
                            return(new Status(StatusCode.DbError));
                        }
                    }
                    else
                    {
                        db = GetExistingDatabase(target);
                    }
                    results["database"] = db;
                }
            }
            else
            {
                return(new Status(StatusCode.BadId));
            }

            Uri remote = new Uri(remoteDict["url"] as string);

            if (!remote.Scheme.Equals("http") && !remote.Scheme.Equals("https") && !remote.Scheme.Equals("cbl"))
            {
                return(new Status(StatusCode.BadRequest));
            }

            var database = results.Get("database");

            if (database == null)
            {
                return(new Status(StatusCode.NotFound));
            }

            if (results.ContainsKey("remote"))
            {
                results["remote"] = remote;
            }

            if (results.ContainsKey("headers"))
            {
                results["headers"] = remoteDict.Get("headers");
            }

            if (results.ContainsKey("authorizer"))
            {
                var auth = remoteDict.Get("auth") as IDictionary <string, object>;
                if (auth != null)
                {
                    //var oauth = auth["oauth"] as IDictionary<string, object>;
                    var persona  = auth.Get("persona") as IDictionary <string, object>;
                    var facebook = auth.Get("facebook") as IDictionary <string, object>;
                    //TODO: OAuth

                    /*if (oauth != null) {
                     *  string consumerKey = oauth.Get("consumer_key") as string;
                     *  string consumerSec = oauth.Get("consumer_secret") as string;
                     *  string token = oauth.Get("token") as string;
                     *  string tokenSec = oauth.Get("token_secret") as string;
                     *  string sigMethod = oauth.Get("signature_method") as string;
                     *  results["authorizer"] =
                     * }*/
                    if (persona != null)
                    {
                        string email = persona.Get("email") as string;
                        results["authorizer"] = new PersonaAuthorizer(email);
                    }
                    else if (facebook != null)
                    {
                        string email = facebook.Get("email") as string;
                        results["authorizer"] = new FacebookAuthorizer(email);
                    }
                    else
                    {
                        Log.W(TAG, "Invalid authorizer settings {0}", auth);
                    }
                }
            }

            // Can't specify both a filter and doc IDs
            if (properties.ContainsKey("filter") && properties.ContainsKey("doc_ids"))
            {
                return(new Status(StatusCode.BadRequest));
            }

            return(new Status(StatusCode.Ok));
        }
Example #5
0
        public virtual Replication GetReplicator(IDictionary <string, object> properties)
        {
            // TODO: in the iOS equivalent of this code, there is: {@"doc_ids", _documentIDs}) - write unit test that detects this bug
            // TODO: ditto for "headers"
            Authorizer  authorizer = null;
            Replication repl       = null;
            Uri         remote     = null;
            IDictionary <string, object> remoteMap;
            IDictionary <string, object> sourceMap = ParseSourceOrTarget(properties, "source");
            IDictionary <string, object> targetMap = ParseSourceOrTarget(properties, "target");
            string source = (string)sourceMap.Get("url");
            string target = (string)targetMap.Get("url");
            bool   createTargetBoolean = (bool)properties.Get("create_target");
            bool   createTarget        = (createTargetBoolean != null && createTargetBoolean);
            bool   continuousBoolean   = (bool)properties.Get("continuous");
            bool   continuous          = (continuousBoolean != null && continuousBoolean);
            bool   cancelBoolean       = (bool)properties.Get("cancel");
            bool   cancel = (cancelBoolean != null && cancelBoolean);

            // Map the 'source' and 'target' JSON params to a local database and remote URL:
            if (source == null || target == null)
            {
                throw new CouchbaseLiteException("source and target are both null", new Status(Status
                                                                                               .BadRequest));
            }
            bool     push      = false;
            Database db        = null;
            string   remoteStr = null;

            if (Couchbase.Lite.Manager.IsValidDatabaseName(source))
            {
                db        = GetExistingDatabase(source);
                remoteStr = target;
                push      = true;
                remoteMap = targetMap;
            }
            else
            {
                remoteStr = source;
                if (createTarget && !cancel)
                {
                    bool mustExist = false;
                    db = GetDatabaseWithoutOpening(target, mustExist);
                    if (!db.Open())
                    {
                        throw new CouchbaseLiteException("cannot open database: " + db, new Status(Status
                                                                                                   .InternalServerError));
                    }
                }
                else
                {
                    db = GetExistingDatabase(target);
                }
                if (db == null)
                {
                    throw new CouchbaseLiteException("database is null", new Status(Status.NotFound));
                }
                remoteMap = sourceMap;
            }
            IDictionary <string, object> authMap = (IDictionary <string, object>)remoteMap.Get(
                "auth");

            if (authMap != null)
            {
                IDictionary <string, object> persona = (IDictionary <string, object>)authMap.Get("persona"
                                                                                                 );
                if (persona != null)
                {
                    string email = (string)persona.Get("email");
                    authorizer = new PersonaAuthorizer(email);
                }
                IDictionary <string, object> facebook = (IDictionary <string, object>)authMap.Get("facebook"
                                                                                                  );
                if (facebook != null)
                {
                    string email = (string)facebook.Get("email");
                    authorizer = new FacebookAuthorizer(email);
                }
            }
            try
            {
                remote = new Uri(remoteStr);
            }
            catch (UriFormatException)
            {
                throw new CouchbaseLiteException("malformed remote url: " + remoteStr, new Status
                                                     (Status.BadRequest));
            }
            if (remote == null || !remote.Scheme.StartsWith("http"))
            {
                throw new CouchbaseLiteException("remote URL is null or non-http: " + remoteStr,
                                                 new Status(Status.BadRequest));
            }
            if (!cancel)
            {
                repl = db.GetReplicator(remote, GetDefaultHttpClientFactory(), push, continuous,
                                        GetWorkExecutor());
                if (repl == null)
                {
                    throw new CouchbaseLiteException("unable to create replicator with remote: " + remote
                                                     , new Status(Status.InternalServerError));
                }
                if (authorizer != null)
                {
                    repl.SetAuthorizer(authorizer);
                }
                string filterName = (string)properties.Get("filter");
                if (filterName != null)
                {
                    repl.SetFilter(filterName);
                    IDictionary <string, object> filterParams = (IDictionary <string, object>)properties
                                                                .Get("query_params");
                    if (filterParams != null)
                    {
                        repl.SetFilterParams(filterParams);
                    }
                }
                if (push)
                {
                    ((Pusher)repl).SetCreateTarget(createTarget);
                }
            }
            else
            {
                // Cancel replication:
                repl = db.GetActiveReplicator(remote, push);
                if (repl == null)
                {
                    throw new CouchbaseLiteException("unable to lookup replicator with remote: " + remote
                                                     , new Status(Status.NotFound));
                }
            }
            return(repl);
        }
Example #6
0
        /// <summary>Private Constructor</summary>
        protected Replication(Database db, Uri remote, bool continuous, IHttpClientFactory clientFactory, TaskFactory workExecutor, CancellationTokenSource tokenSource = null)
        {
            LocalDatabase           = db;
            Continuous              = continuous;
            WorkExecutor            = workExecutor;
            CancellationTokenSource = tokenSource ?? new CancellationTokenSource();
            RemoteUrl      = remote;
            Status         = ReplicationStatus.Stopped;
            online         = true;
            RequestHeaders = new Dictionary <String, Object>();

            if (RemoteUrl.GetQuery() != null && !RemoteUrl.GetQuery().IsEmpty())
            {
                var uri = new Uri(remote.ToString());
                var personaAssertion = URIUtils.GetQueryParameter(uri, PersonaAuthorizer.QueryParameter);

                if (personaAssertion != null && !personaAssertion.IsEmpty())
                {
                    var email      = PersonaAuthorizer.RegisterAssertion(personaAssertion);
                    var authorizer = new PersonaAuthorizer(email);
                    Authorizer = authorizer;
                }

                var facebookAccessToken = URIUtils.GetQueryParameter(uri, FacebookAuthorizer.QueryParameter);

                if (facebookAccessToken != null && !facebookAccessToken.IsEmpty())
                {
                    var email                  = URIUtils.GetQueryParameter(uri, FacebookAuthorizer.QueryParameterEmail);
                    var authorizer             = new FacebookAuthorizer(email);
                    Uri remoteWithQueryRemoved = null;

                    try
                    {
                        remoteWithQueryRemoved = new UriBuilder(remote.Scheme, remote.GetHost(), remote.Port, remote.AbsolutePath).Uri;
                    }
                    catch (UriFormatException e)
                    {
                        throw new ArgumentException("Invalid URI format.", "remote", e);
                    }

                    FacebookAuthorizer.RegisterAccessToken(facebookAccessToken, email, remoteWithQueryRemoved.ToString());

                    Authorizer = authorizer;
                }
                // we need to remove the query from the URL, since it will cause problems when
                // communicating with sync gw / couchdb
                try
                {
                    RemoteUrl = new UriBuilder(remote.Scheme, remote.GetHost(), remote.Port, remote.AbsolutePath).Uri;
                }
                catch (UriFormatException e)
                {
                    throw new ArgumentException("Invalid URI format.", "remote", e);
                }
            }

            Batcher = new Batcher <RevisionInternal>(workExecutor, InboxCapacity, ProcessorDelay,
                                                     inbox =>
            {
                Log.V(Database.Tag, "*** " + this + ": BEGIN processInbox (" + inbox.Count + " sequences)");
                ProcessInbox(new RevisionList(inbox));
                Log.V(Database.Tag, "*** " + this.ToString() + ": END processInbox (lastSequence=" + LastSequence);
                UpdateActive();
            }, CancellationTokenSource);

            this.clientFactory = clientFactory ?? CouchbaseLiteHttpClientFactory.Instance;
        }