| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy |
| // of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| couchTests.users_db = function(debug) { |
| // This tests the users db, especially validations |
| // this should also test that you can log into the couch |
| |
| var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"}); |
| |
| // test that you can treat "_user" as a db-name |
| // this can complicate people who try to secure the users db with |
| // an http proxy and fail to get both the actual db and the _user path |
| // maybe it's not the right approach... |
| // hard to know what else to do, as we don't let non-admins inspect the config |
| // to determine the actual users db name. |
| |
| function testFun() { |
| // test that the validation function is installed |
| var ddoc = usersDb.open("_design/_auth"); |
| T(ddoc.validate_doc_update); |
| |
| // test that you can login as a user using basic auth |
| var jchrisUserDoc = CouchDB.prepareUserDoc({ |
| name: "jchris@apache.org" |
| }, "funnybone"); |
| T(usersDb.save(jchrisUserDoc).ok); |
| |
| T(CouchDB.session().userCtx.name == null); |
| |
| // test that you can use basic auth aginst the users db |
| var s = CouchDB.session({ |
| headers : { |
| // base64_encode("jchris@apache.org:funnybone") |
| "Authorization" : "Basic amNocmlzQGFwYWNoZS5vcmc6ZnVubnlib25l" |
| } |
| }); |
| T(s.userCtx.name == "jchris@apache.org"); |
| T(s.info.authenticated == "default"); |
| T(s.info.authentication_db == "test_suite_users"); |
| TEquals(["oauth", "cookie", "default"], s.info.authentication_handlers); |
| var s = CouchDB.session({ |
| headers : { |
| "Authorization" : "Basic Xzpf" // name and pass of _:_ |
| } |
| }); |
| T(s.name == null); |
| T(s.info.authenticated == "default"); |
| |
| |
| // ok, now create a conflicting edit on the jchris doc, and make sure there's no login. |
| var jchrisUser2 = JSON.parse(JSON.stringify(jchrisUserDoc)); |
| jchrisUser2.foo = "bar"; |
| T(usersDb.save(jchrisUser2).ok); |
| try { |
| usersDb.save(jchrisUserDoc); |
| T(false && "should be an update conflict"); |
| } catch(e) { |
| T(true); |
| } |
| // save as bulk with new_edits=false to force conflict save |
| var resp = usersDb.bulkSave([jchrisUserDoc],{all_or_nothing : true}); |
| |
| var jchrisWithConflict = usersDb.open(jchrisUserDoc._id, {conflicts : true}); |
| T(jchrisWithConflict._conflicts.length == 1); |
| |
| // no login with conflicted user doc |
| try { |
| var s = CouchDB.session({ |
| headers : { |
| "Authorization" : "Basic amNocmlzQGFwYWNoZS5vcmc6ZnVubnlib25l" |
| } |
| }); |
| T(false && "this will throw"); |
| } catch(e) { |
| T(e.error == "unauthorized"); |
| T(/conflict/.test(e.reason)); |
| } |
| |
| // you can delete a user doc |
| s = CouchDB.session().userCtx; |
| T(s.name == null); |
| T(s.roles.indexOf("_admin") !== -1); |
| T(usersDb.deleteDoc(jchrisWithConflict).ok); |
| |
| // you can't change doc from type "user" |
| jchrisUserDoc = usersDb.open(jchrisUserDoc._id); |
| jchrisUserDoc.type = "not user"; |
| try { |
| usersDb.save(jchrisUserDoc); |
| T(false && "should only allow us to save doc when type == 'user'"); |
| } catch(e) { |
| T(e.reason == "doc.type must be user"); |
| } |
| jchrisUserDoc.type = "user"; |
| |
| // "roles" must be an array |
| jchrisUserDoc.roles = "not an array"; |
| try { |
| usersDb.save(jchrisUserDoc); |
| T(false && "should only allow us to save doc when roles is an array"); |
| } catch(e) { |
| T(e.reason == "doc.roles must be an array"); |
| } |
| jchrisUserDoc.roles = []; |
| |
| // "roles" must be an array of strings |
| jchrisUserDoc.roles = [12]; |
| try { |
| usersDb.save(jchrisUserDoc); |
| T(false && "should only allow us to save doc when roles is an array of strings"); |
| } catch(e) { |
| TEquals(e.reason, "doc.roles can only contain strings"); |
| } |
| jchrisUserDoc.roles = []; |
| |
| // "roles" must exist |
| delete jchrisUserDoc.roles; |
| try { |
| usersDb.save(jchrisUserDoc); |
| T(false && "should only allow us to save doc when roles exists"); |
| } catch(e) { |
| T(e.reason == "doc.roles must exist"); |
| } |
| jchrisUserDoc.roles = []; |
| |
| // character : is not allowed in usernames |
| var joeUserDoc = CouchDB.prepareUserDoc({ |
| name: "joe:erlang" |
| }, "qwerty"); |
| try { |
| usersDb.save(joeUserDoc); |
| T(false, "shouldn't allow : in usernames"); |
| } catch(e) { |
| TEquals("Character `:` is not allowed in usernames.", e.reason); |
| } |
| |
| // test that you can login as a user with a password starting with : |
| var doc = CouchDB.prepareUserDoc({ |
| name: "foo@example.org" |
| }, ":bar"); |
| T(usersDb.save(doc).ok); |
| |
| T(CouchDB.session().userCtx.name == null); |
| |
| // test that you can use basic auth aginst the users db |
| var s = CouchDB.session({ |
| headers : { |
| // base64_encode("foo@example.org::bar") |
| "Authorization" : "Basic Zm9vQGV4YW1wbGUub3JnOjpiYXI=" |
| } |
| }); |
| T(s.userCtx.name == "foo@example.org"); |
| |
| }; |
| |
| usersDb.deleteDb(); |
| run_on_modified_server( |
| [{section: "couch_httpd_auth", |
| key: "authentication_db", value: usersDb.name}], |
| testFun |
| ); |
| usersDb.deleteDb(); // cleanup |
| |
| } |