diff options
author | Joan Touzet <wohali@users.noreply.github.com> | 2018-08-06 12:31:45 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-06 12:31:45 -0400 |
commit | 8c306105f3da6391fdb116767e378a567a43cf2e (patch) | |
tree | 04e5355dd2930a84bf442cf4623b63884fdaa8e1 | |
parent | cb87b479443123e94a571e98b68a70176a33b935 (diff) | |
parent | 99e801eca88a4521157d4369847f61cf2a88d1ba (diff) | |
download | couchdb-1278-add-clustered-db-info.tar.gz |
Merge branch 'master' into 1278-add-clustered-db-info1278-add-clustered-db-info
54 files changed, 351 insertions, 565 deletions
diff --git a/.gitignore b/.gitignore index a1cba1e0b..088303039 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,6 @@ share/server/main.js share/www src/b64url/ src/bear/ -src/bcrypt/ src/config/ src/couch/priv/couch_js/config.h src/couch/priv/couchjs diff --git a/.travis.yml b/.travis.yml index b2e7ff0ed..acb0b5102 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: false os: linux otp_release: - - 20.1 + - 20.3 - 19.3 - 18.3 - 17.5 diff --git a/INSTALL.Unix.md b/INSTALL.Unix.md index bfd9c89a2..f0baf58c9 100644 --- a/INSTALL.Unix.md +++ b/INSTALL.Unix.md @@ -74,6 +74,9 @@ You can install the documentation dependencies by running: sudo apt-get --no-install-recommends -y install \ python-sphinx + + sudo pip install --upgrade sphinx_rtd_theme nose requests hypothesis + Be sure to update the version numbers to match your system's available packages. @@ -2158,123 +2158,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -The Erlang code is subject to this license: - -%% Copyright (c) 2011 Hunter Morris <hunter.morris@smarkets.com> - -%% Permission to use, copy, modify, and distribute this software for any -%% purpose with or without fee is hereby granted, provided that the above -%% copyright notice and this permission notice appear in all copies. - -%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -The underlying blowfish code is derived from OpenBSD libc and is -subject to the following license: - -/* - * Blowfish block cipher for OpenBSD - * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de> - * All rights reserved. - * - * Implementation advice by David Mazieres <dm@lcs.mit.edu>. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Niels Provos. - * 4. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -The underlying bcrypt (hashing) code is derived from OpenBSD libc and is -subject to the following license: - -/* - * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Niels Provos. - * 4. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -The asynchronous queue code (c_src/async_queue.c and -c_src/async_queue.h) is from the esnappy project, copyright 2011 -Konstantin V. Sorokin. It is subject to the following license: - -Copyright (c) 2011 Konstantin V. Sorokin -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. - For the src/hyper component: The MIT License (MIT) @@ -13,20 +13,49 @@ include version.mk REBAR?=$(shell echo `pwd`/bin/rebar) + +# Handle the following scenarios: +# 1. When building from a tarball, use version.mk. +# 2. When building from a clean release tag (#.#.#), use that tag. +# 3. When building from a clean RC tag (#.#.#-RC#), use JUST the version +# number inside the tarball, but use the full name for the name of the +# tarball itself. +# 4. When not on a clean tag, use version.mk + git sha + dirty status. + +COUCHDB_GIT_SHA=$(git_sha) + IN_RELEASE = $(shell if [ ! -d .git ]; then echo true; fi) ifeq ($(IN_RELEASE), true) + +# 1. Building from tarball, use version.mk. COUCHDB_VERSION = $(vsn_major).$(vsn_minor).$(vsn_patch) + else -# IN_RC generates a tarball that has the -RCx suffix in the name if needed + +# Gather some additional information. +# We do it this way so we don't bake shell-isms into Makefile +# to make it easier to port to Windows. I know, I know. -jst +# IN_RC contains the -RCx suffix in the name if present IN_RC = $(shell git describe --tags --always --first-parent \ - | grep -Eo -- '-RC[0-9]+' 2>/dev/null) -RELTAG = $(shell git describe --dirty --abbrev=0 --tags --always --first-parent \ - | grep -Eo '^[0-9]+\.[0-9]\.[0-9]+') -ifeq ($(RELTAG),) -COUCHDB_VERSION_SUFFIX = $(shell git rev-parse --short --verify HEAD) + | grep -Eo -- '-RC[0-9]+' 2>/dev/null) +# ON_TAG matches *ONLY* if we are on a release or RC tag +ON_TAG = $(shell git describe --tags --always --first-parent \ + | grep -Eo -- '^[0-9]+\.[0-9]\.[0-9]+(-RC[0-9]+)?$$' 2>/dev/null) +# RELTAG contains the #.#.# from git describe, which might be used +RELTAG = $(shell git describe --tags --always --first-parent \ + | grep -Eo -- '^[0-9]+\.[0-9]\.[0-9]+' 2>/dev/null) +# DIRTY identifies if we're not on a commit +DIRTY = $(shell git describe --dirty | grep -Eo -- '-dirty' 2>/dev/null) +# COUCHDB_GIT_SHA is our current git hash. +COUCHDB_GIT_SHA=$(shell git rev-parse --short --verify HEAD) + +ifeq ($(ON_TAG),) +# 4. Not on a tag. +COUCHDB_VERSION_SUFFIX = $(COUCHDB_GIT_SHA)$(DIRTY) COUCHDB_VERSION = $(vsn_major).$(vsn_minor).$(vsn_patch)-$(COUCHDB_VERSION_SUFFIX) else -COUCHDB_VERSION = $(RELTAG) +# 2 and 3. On a tag. +COUCHDB_VERSION = $(RELTAG)$(DIRTY) endif endif @@ -82,7 +111,7 @@ help: .PHONY: couch # target: couch - Build CouchDB core, use ERL_OPTS to provide custom compiler's options couch: config.erl - @COUCHDB_VERSION=$(COUCHDB_VERSION) $(REBAR) compile $(COMPILE_OPTS) + @COUCHDB_VERSION=$(COUCHDB_VERSION) COUCHDB_GIT_SHA=$(COUCHDB_GIT_SHA) $(REBAR) compile $(COMPILE_OPTS) @cp src/couch/priv/couchjs bin/ @@ -225,7 +254,7 @@ list-eunit-apps: .PHONY: list-eunit-suites # target: list-eunit-suites - List EUnit target test suites list-eunit-suites: - @find ./src/ -type f -name *_test.erl -o -name *_tests.erl -printf "%f\n" \ + @find ./src/ -type f -name *_test.erl -o -name *_tests.erl -exec basename {} \; \ | cut -d '.' -f -1 \ | sort @@ -233,7 +262,7 @@ list-eunit-suites: .PHONY: list-js-suites # target: list-js-suites - List JavaScript test suites list-js-suites: - @find ./test/javascript/tests/ -type f -name *.js -printf "%f\n" \ + @find ./test/javascript/tests/ -type f -name *.js -exec basename {} \; \ | cut -d '.' -f -1 \ | sort diff --git a/Makefile.win b/Makefile.win index 5a2a73ab1..67c15fc69 100644 --- a/Makefile.win +++ b/Makefile.win @@ -14,13 +14,50 @@ include version.mk SHELL=cmd.exe REBAR?=$(shell where rebar.cmd) + +# Handle the following scenarios: +# 1. When building from a tarball, use version.mk. +# 2. When building from a clean release tag (#.#.#), use that tag. +# 3. When building from a clean RC tag (#.#.#-RC#), use JUST the version +# number inside the tarball, but use the full name for the name of the +# tarball itself. +# 4. When not on a clean tag, use version.mk + git sha + dirty status. + +COUCHDB_GIT_SHA=$(git_sha) IN_RELEASE = $(shell if not exist .git echo true) + ifeq ($(IN_RELEASE), true) -COUCHDB_VERSION_SUFFIX= + +# 1. Building from tarball, use version.mk. COUCHDB_VERSION = $(vsn_major).$(vsn_minor).$(vsn_patch) + +else + +# Gather some additional information. +# We do it this way so we don't bake shell-isms into Makefile +# to make it easier to port to Windows. I know, I know. -jst +# COUCHDB_GIT_SHA is our current git hash. +COUCHDB_GIT_SHA=$(shell git rev-parse --short --verify HEAD) +# IN_RC contains the -RCx suffix in the name if present +IN_RC = $(shell git describe --tags --always --first-parent \ + | grep -Eo -- '-RC[0-9]+' 2>/dev/null) +# ON_TAG matches *ONLY* if we are on a release or RC tag +ON_TAG = $(shell git describe --tags --always --first-parent \ + | grep -Eo -- '^[0-9]+\.[0-9]\.[0-9]+(-RC[0-9]+)?$$' 2>/dev/null) +# RELTAG contains the #.#.# from git describe, which might be used +RELTAG = $(shell git describe --tags --always --first-parent \ + | grep -Eo -- '^[0-9]+\.[0-9]\.[0-9]+' 2>/dev/null) +# DIRTY identifies if we're not on a commit +DIRTY = $(shell git describe --dirty | grep -Eo -- '-dirty' 2>/dev/null) + +ifeq ($(ON_TAG),) +# 4. Not on a tag. +COUCHDB_VERSION_SUFFIX = $(COUCHDB_GIT_SHA)$(DIRTY) +COUCHDB_VERSION = $(vsn_major).$(vsn_minor).$(vsn_patch)-$(COUCHDB_VERSION_SUFFIX) else -COUCHDB_VERSION_SUFFIX = -$(shell git rev-parse --short --verify HEAD) -COUCHDB_VERSION = $(vsn_major).$(vsn_minor).$(vsn_patch)$(COUCHDB_VERSION_SUFFIX) +# 2 and 3. On a tag. +COUCHDB_VERSION = $(RELTAG)$(DIRTY) +endif endif DESTDIR= @@ -53,7 +90,7 @@ all: couch fauxton docs .PHONY: couch # target: couch - Build CouchDB core couch: config.erl - @set COUCHDB_VERSION=$(COUCHDB_VERSION) && $(REBAR) compile + @set COUCHDB_VERSION=$(COUCHDB_VERSION) && set COUCHDB_GIT_SHA=$(COUCHDB_GIT_SHA) && $(REBAR) compile @copy src\couch\priv\couchjs.exe bin @@ -178,13 +178,6 @@ This product also includes the following third-party components: Copyright (c) 2015 Twitter, Inc. -* erlang-bcrypt - - Erlang code: Copyright (c) 2011 Hunter Morris <hunter.morris@smarkets.com> - - Blowfish block cipher & bcrypt (hashing) code for OpenBSD, Copyright - 1997 Niels Provos <provos@physnet.uni-hamburg.de> - - The asynchronous queue code (c_src/async_queue.c and c_src/async_queue.h) - is from the esnappy project, copyright 2011 Konstantin V. Sorokin. - * hyper Copyright (c) 2014 Game Analytics ApS diff --git a/build-aux/couchdb-build-release.sh b/build-aux/couchdb-build-release.sh index 4482b713c..2d219e5e4 100755 --- a/build-aux/couchdb-build-release.sh +++ b/build-aux/couchdb-build-release.sh @@ -35,8 +35,12 @@ done cd .. -# create CONTRIBUTORS file + if test -e .git; then + # save git sha in version.mk + git_sha=`git rev-parse --short HEAD` + echo "git_sha=$git_sha" >> $RELDIR/version.mk + # create CONTRIBUTORS file OS=`uname -s` case "$OS" in Linux|CYGWIN*) # GNU sed @@ -25,6 +25,7 @@ PACKAGE_AUTHOR_NAME="The Apache Software Foundation" WITH_CURL="false" WITH_FAUXTON=1 WITH_DOCS=1 +ERLANG_MD5="false" SKIP_DEPS=0 COUCHDB_USER="$(whoami 2>/dev/null || echo couchdb)" @@ -46,6 +47,7 @@ Options: -c | --with-curl request that couchjs is linked to cURL (default false) --disable-fauxton do not build Fauxton --disable-docs do not build any documentation or manpages + --erlang-md5 use erlang for md5 hash operations --dev alias for --with-curl --disable-docs --disable-fauxton --skip-deps do not update erlang dependencies --rebar=PATH use rebar by specified path (version >=2.6.0 && <3.0 required) @@ -78,6 +80,12 @@ parse_opts() { continue ;; + --erlang-md5) + ERLANG_MD5="true" + shift + continue + ;; + --dev) WITH_DOCS=0 WITH_FAUXTON=0 @@ -195,6 +203,7 @@ EOF cat > $rootdir/config.erl << EOF {with_curl, $WITH_CURL}. +{erlang_md5, $ERLANG_MD5}. EOF install_local_rebar() { diff --git a/rebar.config.script b/rebar.config.script index 14117527c..cd469553c 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -56,19 +56,16 @@ DepDescs = [ %% Non-Erlang deps {docs, {url, "https://github.com/apache/couchdb-documentation"}, - {tag, "2.1.2"}, [raw]}, + {tag, "2.2.0"}, [raw]}, {fauxton, {url, "https://github.com/apache/couchdb-fauxton"}, - {tag, "v1.1.15"}, [raw]}, + {tag, "v1.1.17"}, [raw]}, %% Third party deps {folsom, "folsom", {tag, "CouchDB-0.8.2"}}, -{hyper, "hyper", {tag, "CouchDB-2.2.0-3"}}, +{hyper, "hyper", {tag, "CouchDB-2.2.0-4"}}, {ibrowse, "ibrowse", {tag, "CouchDB-4.0.1"}}, {jiffy, "jiffy", {tag, "CouchDB-0.14.11-2"}}, {mochiweb, "mochiweb", {tag, "v2.17.0"}}, -{meck, "meck", {tag, "0.8.8"}}, -{bcrypt, {url, "https://github.com/apache/couchdb-erlang-bcrypt"}, - {tag, "1.0.2"}}, -{triq, "triq", {tag, "v1.2.0"}} +{meck, "meck", {tag, "0.8.8"}} ], @@ -105,7 +102,7 @@ AddConfig = [ {plt_location, local}, {plt_location, COUCHDB_ROOT}, {plt_extra_apps, [ - asn1, bcrypt, compiler, crypto, inets, kernel, os_mon, runtime_tools, + asn1, compiler, crypto, inets, kernel, os_mon, runtime_tools, sasl, setup, ssl, stdlib, syntax_tools, xmerl]}, {warnings, [unmatched_returns, error_handling, race_conditions]}]}, {post_hooks, [{compile, "escript support/build_js.escript"}]} diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini index 0f0d54793..45025e780 100644 --- a/rel/overlay/etc/default.ini +++ b/rel/overlay/etc/default.ini @@ -83,7 +83,8 @@ port = {{cluster_port}} bind_address = 127.0.0.1 backlog = 512 docroot = {{fauxton_root}} -socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}] +socket_options = [{sndbuf, 262144}, {nodelay, true}] +server_options = [{recbuf, undefined}] require_valid_user = false ; List of headers that will be kept when the header Prefer: return=minimal is included in a request. ; If Server header is left out, Mochiweb will add its own one in. @@ -98,6 +99,9 @@ max_db_number_for_dbs_info_req = 100 ; uncomment the next line to enable proxy authentication ; authentication_handlers = {chttpd_auth, proxy_authentication_handler}, {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, default_authentication_handler} +; prevent non-admins from accessing /_all_dbs +;admin_only_all_dbs = false + [database_compaction] ; larger buffer sizes can originate smaller files doc_buffer_size = 524288 ; value in bytes @@ -135,8 +139,8 @@ allow_jsonp = false ; Options for the MochiWeb HTTP server. ;server_options = [{backlog, 128}, {acceptor_pool_size, 16}] ; For more socket options, consult Erlang's module 'inet' man page. -;socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}] -socket_options = [{recbuf, 262144}, {sndbuf, 262144}] +;socket_options = [{recbuf, undefined}, {sndbuf, 262144}, {nodelay, true}] +socket_options = [{sndbuf, 262144}] enable_cors = false enable_xframe_options = false ; CouchDB can optionally enforce a maximum uri length; @@ -149,7 +153,7 @@ enable_xframe_options = false ; x_forwarded_proto = X-Forwarded-Proto ; x_forwarded_ssl = X-Forwarded-Ssl ; Maximum allowed http request size. Applies to both clustered and local port. -max_http_request_size = 67108864 ; 64 MB +max_http_request_size = 4294967296 ; 4GB ; [httpd_design_handlers] ; _view = @@ -203,8 +207,7 @@ require_valid_user = false timeout = 600 ; number of seconds before automatic logout auth_cache_size = 50 ; size is number of cache entries allow_persistent_cookies = false ; set to true to allow persistent cookies -iterations = 10 ; iterations for PBKDF2 password hashing -log_rounds = 10 ; 2^log_rounds iterations for Bcrypt password hashing +iterations = 10 ; iterations for password hashing ; min_iterations = 1 ; max_iterations = 1000000000 ; password_scheme = pbkdf2 @@ -431,13 +434,12 @@ ssl_certificate_max_depth = 3 ; There are currently two plugins available: ; couch_replicator_auth_session - use _session cookie authentication ; couch_replicator_auth_noop - use basic authentication (previous default) -; Currently previous default behavior is still the default. To start using -; session auth, use this as the list of plugins: -; `couch_replicator_auth_session,couch_replicator_auth_noop`. -; In a future release the session plugin might be used by default. +; Currently, the new _session cookie authentication is tried first, before +; falling back to the old basic authenticaion default: +;auth_plugins = couch_replicator_auth_session,couch_replicator_auth_noop +; To restore the old behaviour, use the following value: ;auth_plugins = couch_replicator_auth_noop - [compaction_daemon] ; The delay, in seconds, between each check for which database and view indexes ; need to be compacted. diff --git a/rel/overlay/etc/local.ini b/rel/overlay/etc/local.ini index e3b7b1502..ea5467c9a 100644 --- a/rel/overlay/etc/local.ini +++ b/rel/overlay/etc/local.ini @@ -27,7 +27,7 @@ ; Options for the MochiWeb HTTP server. ;server_options = [{backlog, 128}, {acceptor_pool_size, 16}] ; For more socket options, consult Erlang's module 'inet' man page. -;socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}] +;socket_options = [{sndbuf, 262144}, {nodelay, true}] [httpd] ; NOTE that this only configures the "backend" node-local port, not the diff --git a/rel/reltool.config b/rel/reltool.config index 5e86d9643..2c55d0900 100644 --- a/rel/reltool.config +++ b/rel/reltool.config @@ -15,7 +15,6 @@ {rel, "couchdb", "2.2.0", [ %% stdlib asn1, - bcrypt, compiler, crypto, inets, @@ -68,7 +67,6 @@ %% stdlib {app, asn1, [{incl_cond, include}]}, - {app, bcrypt, [{incl_cond, include}]}, {app, compiler, [{incl_cond, include}]}, {app, crypto, [{incl_cond, include}]}, {app, inets, [{incl_cond, include}]}, diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl index c0179babc..a5628396b 100644 --- a/src/chttpd/src/chttpd.erl +++ b/src/chttpd/src/chttpd.erl @@ -681,7 +681,7 @@ doc_etag(#doc{id=Id, body=Body, revs={Start, [DiskRev|_]}}) -> couch_httpd:doc_etag(Id, Body, {Start, DiskRev}). make_etag(Term) -> - <<SigInt:128/integer>> = crypto:hash(md5, term_to_binary(Term)), + <<SigInt:128/integer>> = couch_hash:md5_hash(term_to_binary(Term)), list_to_binary(io_lib:format("\"~.36B\"",[SigInt])). etag_match(Req, CurrentEtag) when is_binary(CurrentEtag) -> diff --git a/src/chttpd/src/chttpd_auth_request.erl b/src/chttpd/src/chttpd_auth_request.erl index 05c5e8e35..9110ed6bc 100644 --- a/src/chttpd/src/chttpd_auth_request.erl +++ b/src/chttpd/src/chttpd_auth_request.erl @@ -34,7 +34,10 @@ authorize_request_int(#httpd{path_parts=[]}=Req) -> authorize_request_int(#httpd{path_parts=[<<"favicon.ico">>|_]}=Req) -> Req; authorize_request_int(#httpd{path_parts=[<<"_all_dbs">>|_]}=Req) -> - Req; + case config:get_boolean("chttpd", "admin_only_all_dbs", false) of + true -> require_admin(Req); + false -> Req + end; authorize_request_int(#httpd{path_parts=[<<"_dbs_info">>|_]}=Req) -> Req; authorize_request_int(#httpd{path_parts=[<<"_replicator">>], method='PUT'}=Req) -> diff --git a/src/chttpd/src/chttpd_misc.erl b/src/chttpd/src/chttpd_misc.erl index 95345d42b..596e0142b 100644 --- a/src/chttpd/src/chttpd_misc.erl +++ b/src/chttpd/src/chttpd_misc.erl @@ -49,6 +49,7 @@ handle_welcome_req(#httpd{method='GET'}=Req, WelcomeMessage) -> send_json(Req, {[ {couchdb, WelcomeMessage}, {version, list_to_binary(couch_server:get_version())}, + {git_sha, list_to_binary(couch_server:get_git_sha())}, {features, config:features()} ] ++ case config:get("vendor") of [] -> diff --git a/src/chttpd/test/chttpd_welcome_test.erl b/src/chttpd/test/chttpd_welcome_test.erl index af9732f57..b737abd7a 100644 --- a/src/chttpd/test/chttpd_welcome_test.erl +++ b/src/chttpd/test/chttpd_welcome_test.erl @@ -60,6 +60,8 @@ should_have_version(Url) -> Version = couch_util:get_value(<<"version">>, Json, undefined), CouchDB = couch_util:get_value(<<"couchdb">>, Json, undefined), Features = couch_util:get_value(<<"features">>, Json, undefined), + Sha = couch_util:get_value(<<"git_sha">>, Json, undefined), + ?assertNotEqual(Sha, undefined), ?assertEqual(<<"Welcome">>, CouchDB), RealVersion = list_to_binary(couch_server:get_version()), ?assertEqual(RealVersion, Version), diff --git a/src/couch/rebar.config.script b/src/couch/rebar.config.script index 498ce3a82..5321cff66 100644 --- a/src/couch/rebar.config.script +++ b/src/couch/rebar.config.script @@ -39,6 +39,13 @@ Version = case os:getenv("COUCHDB_VERSION") of string:strip(Version0, right) end, +GitSha = case os:getenv("COUCHDB_GIT_SHA") of + false -> + ""; % release builds won’t get a fallback + GitSha0 -> + string:strip(GitSha0, right) +end, + CouchConfig = case filelib:is_file(os:getenv("COUCHDB_CONFIG")) of true -> {ok, Result} = file:consult(os:getenv("COUCHDB_CONFIG")), @@ -64,6 +71,13 @@ ConfigSrc = [["#define ", K, " ", V, $\n] || {K, V} <- ConfigH], ConfigBin = iolist_to_binary(ConfigSrc), ok = CopyIfDifferent(CouchJSConfig, ConfigBin), +MD5Config = case lists:keyfind(erlang_md5, 1, CouchConfig) of + {erlang_md5, true} -> + [{d, 'ERLANG_MD5', true}]; + _ -> + [] +end, + %% TODO support curl on Windows {JS_CFLAGS, JS_LDFLAGS} = case lists:keyfind(with_curl, 1, CouchConfig) of {with_curl, true} -> @@ -142,8 +156,9 @@ AddConfig = [ {port_specs, PortSpecs}, {erl_opts, PlatformDefines ++ [ {d, 'COUCHDB_VERSION', Version}, + {d, 'COUCHDB_GIT_SHA', GitSha}, {i, "../"} - ]}, + ] ++ MD5Config}, {eunit_compile_opts, PlatformDefines} ]. diff --git a/src/couch/src/couch.app.src b/src/couch/src/couch.app.src index cf5cee6b8..6af213624 100644 --- a/src/couch/src/couch.app.src +++ b/src/couch/src/couch.app.src @@ -31,7 +31,6 @@ kernel, stdlib, crypto, - bcrypt, sasl, inets, ssl, diff --git a/src/couch/src/couch.erl b/src/couch/src/couch.erl index f956b4b3d..fd5c9e101 100644 --- a/src/couch/src/couch.erl +++ b/src/couch/src/couch.erl @@ -21,7 +21,6 @@ deps() -> inets, os_mon, crypto, - bcrypt, public_key, ssl, ibrowse, diff --git a/src/couch/src/couch_auth_cache.erl b/src/couch/src/couch_auth_cache.erl index 425cce010..157b0902e 100644 --- a/src/couch/src/couch_auth_cache.erl +++ b/src/couch/src/couch_auth_cache.erl @@ -92,8 +92,6 @@ get_admin(UserName) when is_list(UserName) -> "-pbkdf2-" ++ HashedPwdSaltAndIterations -> [HashedPwd, Salt, Iterations] = string:tokens(HashedPwdSaltAndIterations, ","), make_admin_doc(HashedPwd, Salt, Iterations); - "-bcrypt-" ++ HashedPwd -> - make_admin_doc(HashedPwd); _Else -> nil end. @@ -111,11 +109,6 @@ make_admin_doc(DerivedKey, Salt, Iterations) -> {<<"password_scheme">>, <<"pbkdf2">>}, {<<"derived_key">>, ?l2b(DerivedKey)}]. -make_admin_doc(DerivedKey) -> - [{<<"roles">>, [<<"_admin">>]}, - {<<"password_scheme">>, <<"bcrypt">>}, - {<<"derived_key">>, ?l2b(DerivedKey)}]. - get_from_cache(UserName) -> exec_if_auth_db( fun(_AuthDb) -> diff --git a/src/couch/src/couch_bt_engine.erl b/src/couch/src/couch_bt_engine.erl index a42d116f8..ee0d6d864 100644 --- a/src/couch/src/couch_bt_engine.erl +++ b/src/couch/src/couch_bt_engine.erl @@ -330,7 +330,7 @@ serialize_doc(#st{} = St, #doc{} = Doc) -> Body = Compress(Doc#doc.body), Atts = Compress(Doc#doc.atts), SummaryBin = ?term_to_bin({Body, Atts}), - Md5 = crypto:hash(md5, SummaryBin), + Md5 = couch_hash:md5_hash(SummaryBin), Data = couch_file:assemble_file_chunk(SummaryBin, Md5), % TODO: This is a terrible hack to get around the issues % in COUCHDB-3255. We'll need to come back and figure diff --git a/src/couch/src/couch_db.erl b/src/couch/src/couch_db.erl index b47cc7ece..65ca54a59 100644 --- a/src/couch/src/couch_db.erl +++ b/src/couch/src/couch_db.erl @@ -938,7 +938,7 @@ new_revid(#doc{body=Body, revs={OldStart,OldRevs}, atts=Atts, deleted=Deleted}) ?l2b(integer_to_list(couch_util:rand32())); Atts2 -> OldRev = case OldRevs of [] -> 0; [OldRev0|_] -> OldRev0 end, - crypto:hash(md5, term_to_binary([Deleted, OldStart, OldRev, Body, Atts2], [{minor_version, 1}])) + couch_hash:md5_hash(term_to_binary([Deleted, OldStart, OldRev, Body, Atts2], [{minor_version, 1}])) end. new_revs([], OutBuckets, IdRevsAcc) -> diff --git a/src/couch/src/couch_file.erl b/src/couch/src/couch_file.erl index 9f668ea69..55cb95661 100644 --- a/src/couch/src/couch_file.erl +++ b/src/couch/src/couch_file.erl @@ -132,7 +132,7 @@ append_binary(Fd, Bin) -> append_binary_md5(Fd, Bin) -> ioq:call(Fd, - {append_bin, assemble_file_chunk(Bin, crypto:hash(md5, Bin))}, + {append_bin, assemble_file_chunk(Bin, couch_hash:md5_hash(Bin))}, erlang:get(io_priority)). append_raw_chunk(Fd, Chunk) -> @@ -175,7 +175,7 @@ pread_iolist(Fd, Pos) -> {ok, IoList, <<>>} -> {ok, IoList}; {ok, IoList, Md5} -> - case crypto:hash(md5, IoList) of + case couch_hash:md5_hash(IoList) of Md5 -> {ok, IoList}; _ -> @@ -333,7 +333,7 @@ read_header(Fd) -> write_header(Fd, Data) -> Bin = term_to_binary(Data), - Md5 = crypto:hash(md5, Bin), + Md5 = couch_hash:md5_hash(Bin), % now we assemble the final header binary and write to disk FinalBin = <<Md5/binary, Bin/binary>>, ioq:call(Fd, {write_header, FinalBin}, erlang:get(io_priority)). @@ -559,7 +559,7 @@ load_header(Fd, Pos, HeaderLen, RestBlock) -> end, <<Md5Sig:16/binary, HeaderBin/binary>> = iolist_to_binary(remove_block_prefixes(?PREFIX_SIZE, RawBin)), - Md5Sig = crypto:hash(md5, HeaderBin), + Md5Sig = couch_hash:md5_hash(HeaderBin), {ok, HeaderBin}. diff --git a/src/couch/src/couch_hash.erl b/src/couch/src/couch_hash.erl new file mode 100644 index 000000000..842b37423 --- /dev/null +++ b/src/couch/src/couch_hash.erl @@ -0,0 +1,45 @@ +% 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. + +-module(couch_hash). + +-export([md5_hash/1, md5_hash_final/1, md5_hash_init/0, md5_hash_update/2]). + +-ifdef(ERLANG_MD5). + +md5_hash(Data) -> + erlang:md5(Data). + +md5_hash_final(Context) -> + erlang:md5_final(Context). + +md5_hash_init() -> + erlang:md5_init(). + +md5_hash_update(Context, Data) -> + erlang:md5_update(Context, Data). + +-else. + +md5_hash(Data) -> + crypto:hash(md5, Data). + +md5_hash_final(Context) -> + crypto:hash_final(Context). + +md5_hash_init() -> + crypto:hash_init(md5). + +md5_hash_update(Context, Data) -> + crypto:hash_update(Context, Data). + +-endif. diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl index 050282a0c..a8cfca6d2 100644 --- a/src/couch/src/couch_httpd.erl +++ b/src/couch/src/couch_httpd.erl @@ -622,7 +622,7 @@ rev_etag({Start, DiskRev}) -> <<$", Rev/binary, $">>. make_etag(Term) -> - <<SigInt:128/integer>> = crypto:hash(md5, term_to_binary(Term)), + <<SigInt:128/integer>> = couch_hash:md5_hash(term_to_binary(Term)), iolist_to_binary([$", io_lib:format("~.36B", [SigInt]), $"]). etag_match(Req, CurrentEtag) when is_binary(CurrentEtag) -> diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl index 74cbe5a08..6ac7b75af 100644 --- a/src/couch/src/couch_httpd_auth.erl +++ b/src/couch/src/couch_httpd_auth.erl @@ -309,12 +309,7 @@ handle_session_req(#httpd{method='POST', mochi_req=MochiReq}=Req, AuthModule) -> Secret = ?l2b(ensure_cookie_auth_secret()), UserSalt = couch_util:get_value(<<"salt">>, UserProps), CurrentTime = make_cookie_time(), - Cookie = case UserSalt of - undefined -> - cookie_auth_cookie(Req, ?b2l(UserName), <<Secret/binary>>, CurrentTime); - _ -> - cookie_auth_cookie(Req, ?b2l(UserName), <<Secret/binary, UserSalt/binary>>, CurrentTime) - end, + Cookie = cookie_auth_cookie(Req, ?b2l(UserName), <<Secret/binary, UserSalt/binary>>, CurrentTime), % TODO document the "next" feature in Futon {Code, Headers} = case couch_httpd:qs_value(Req, "next", nil) of nil -> @@ -406,10 +401,7 @@ authenticate(Pass, UserProps) -> Iterations = couch_util:get_value(<<"iterations">>, UserProps, 10000), verify_iterations(Iterations), {couch_passwords:pbkdf2(Pass, UserSalt, Iterations), - couch_util:get_value(<<"derived_key">>, UserProps, nil)}; - <<"bcrypt">> -> - UserHash = couch_util:get_value(<<"derived_key">>, UserProps, nil), - {couch_passwords:bcrypt(Pass, UserHash), UserHash} + couch_util:get_value(<<"derived_key">>, UserProps, nil)} end, couch_passwords:verify(PasswordHash, ExpectedHash). diff --git a/src/couch/src/couch_native_process.erl b/src/couch/src/couch_native_process.erl index 8f8ce8b1d..eee8b2860 100644 --- a/src/couch/src/couch_native_process.erl +++ b/src/couch/src/couch_native_process.erl @@ -363,11 +363,11 @@ bindings(State, Sig, DDoc) -> % thanks to erlview, via: % http://erlang.org/pipermail/erlang-questions/2003-November/010544.html makefun(State, Source) -> - Sig = crypto:hash(md5, Source), + Sig = couch_hash:md5_hash(Source), BindFuns = bindings(State, Sig), {Sig, makefun(State, Source, BindFuns)}. makefun(State, Source, {DDoc}) -> - Sig = crypto:hash(md5, lists:flatten([Source, term_to_binary(DDoc)])), + Sig = couch_hash:md5_hash(lists:flatten([Source, term_to_binary(DDoc)])), BindFuns = bindings(State, Sig, {DDoc}), {Sig, makefun(State, Source, BindFuns)}; makefun(_State, Source, BindFuns) when is_list(BindFuns) -> diff --git a/src/couch/src/couch_passwords.erl b/src/couch/src/couch_passwords.erl index 77e136144..baf78f5d5 100644 --- a/src/couch/src/couch_passwords.erl +++ b/src/couch/src/couch_passwords.erl @@ -12,7 +12,7 @@ -module(couch_passwords). --export([simple/2, pbkdf2/3, pbkdf2/4, bcrypt/2, verify/2]). +-export([simple/2, pbkdf2/3, pbkdf2/4, verify/2]). -export([hash_admin_password/1, get_unhashed_admins/0]). -include_lib("couch/include/couch_db.hrl"). @@ -51,10 +51,7 @@ hash_admin_password("pbkdf2", ClearPassword) -> Salt ,list_to_integer(Iterations)), ?l2b("-pbkdf2-" ++ ?b2l(DerivedKey) ++ "," ++ ?b2l(Salt) ++ "," - ++ Iterations); -hash_admin_password("bcrypt", ClearPassword) -> - LogRounds = list_to_integer(config:get("couch_httpd_auth", "log_rounds", "10")), - ?l2b("-bcrypt-" ++ couch_passwords:bcrypt(couch_util:to_binary(ClearPassword), LogRounds)). + ++ Iterations). -spec get_unhashed_admins() -> list(). get_unhashed_admins() -> @@ -63,8 +60,6 @@ get_unhashed_admins() -> false; % already hashed ({_User, "-pbkdf2-" ++ _}) -> false; % already hashed - ({_User, "-bcrypt-" ++ _}) -> - false; % already hashed ({_User, _ClearPassword}) -> true end, @@ -128,16 +123,6 @@ pbkdf2(Password, Salt, Iterations, BlockIndex, Iteration, Prev, Acc) -> pbkdf2(Password, Salt, Iterations, BlockIndex, Iteration + 1, Next, crypto:exor(Next, Acc)). -%% Define the bcrypt functions to hash a password --spec bcrypt(binary(), binary()) -> binary(); - (binary(), integer()) -> binary(). -bcrypt(Password, Salt) when is_binary(Salt) -> - {ok, Hash} = bcrypt:hashpw(Password, Salt), - list_to_binary(Hash); -bcrypt(Password, LogRounds) when is_integer(LogRounds) -> - {ok, Salt} = bcrypt:gen_salt(LogRounds), - bcrypt(Password, list_to_binary(Salt)). - %% verify two lists for equality without short-circuits to avoid timing attacks. -spec verify(string(), string(), integer()) -> boolean(). verify([X|RestX], [Y|RestY], Result) -> diff --git a/src/couch/src/couch_query_servers.erl b/src/couch/src/couch_query_servers.erl index de8ef1e15..7047364e2 100644 --- a/src/couch/src/couch_query_servers.erl +++ b/src/couch/src/couch_query_servers.erl @@ -90,11 +90,8 @@ group_reductions_results(List) -> finalize(<<"_approx_count_distinct",_/binary>>, Reduction) -> true = hyper:is_hyper(Reduction), {ok, round(hyper:card(Reduction))}; -finalize(<<"_stats",_/binary>>, {_, _, _, _, _} = Unpacked) -> +finalize(<<"_stats",_/binary>>, Unpacked) -> {ok, pack_stats(Unpacked)}; -finalize(<<"_stats",_/binary>>, {Packed}) -> - % Legacy code path before we had the finalize operation - {ok, {Packed}}; finalize(_RedSrc, Reduction) -> {ok, Reduction}. @@ -299,8 +296,12 @@ unpack_stats({PreRed}) when is_list(PreRed) -> get_number(<<"sumsqr">>, PreRed) }. + pack_stats({Sum, Cnt, Min, Max, Sqr}) -> {[{<<"sum">>,Sum}, {<<"count">>,Cnt}, {<<"min">>,Min}, {<<"max">>,Max}, {<<"sumsqr">>,Sqr}]}; +pack_stats({Packed}) -> + % Legacy code path before we had the finalize operation + {Packed}; pack_stats(Stats) when is_list(Stats) -> lists:map(fun pack_stats/1, Stats). @@ -609,4 +610,49 @@ stat_values_test() -> {18, 2, 5, 13, 194} ], stat_values([2,3,5], [7,11,13])). +reduce_stats_test() -> + ?assertEqual([ + {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]} + ], test_reduce(<<"_stats">>, [[[null, key], 2]])), + + ?assertEqual([[ + {[{<<"sum">>,1},{<<"count">>,1},{<<"min">>,1},{<<"max">>,1},{<<"sumsqr">>,1}]}, + {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]} + ]], test_reduce(<<"_stats">>, [[[null, key],[1,2]]])), + + ?assertEqual( + {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]} + , element(2, finalize(<<"_stats">>, {2, 1, 2, 2, 4}))), + + ?assertEqual([ + {[{<<"sum">>,1},{<<"count">>,1},{<<"min">>,1},{<<"max">>,1},{<<"sumsqr">>,1}]}, + {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]} + ], element(2, finalize(<<"_stats">>, [ + {1, 1, 1, 1, 1}, + {2, 1, 2, 2, 4} + ]))), + + ?assertEqual([ + {[{<<"sum">>,1},{<<"count">>,1},{<<"min">>,1},{<<"max">>,1},{<<"sumsqr">>,1}]}, + {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]} + ], element(2, finalize(<<"_stats">>, [ + {1, 1, 1, 1, 1}, + {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]} + ]))), + + ?assertEqual([ + {[{<<"sum">>,1},{<<"count">>,1},{<<"min">>,1},{<<"max">>,1},{<<"sumsqr">>,1}]}, + {[{<<"sum">>,2},{<<"count">>,1},{<<"min">>,2},{<<"max">>,2},{<<"sumsqr">>,4}]} + ], element(2, finalize(<<"_stats">>, [ + {[{<<"sum">>,1},{<<"count">>,1},{<<"min">>,1},{<<"max">>,1},{<<"sumsqr">>,1}]}, + {2, 1, 2, 2, 4} + ]))), + ok. + +test_reduce(Reducer, KVs) -> + ?assertMatch({ok, _}, reduce(<<"javascript">>, [Reducer], KVs)), + {ok, Reduced} = reduce(<<"javascript">>, [Reducer], KVs), + {ok, Finalized} = finalize(Reducer, Reduced), + Finalized. + -endif. diff --git a/src/couch/src/couch_server.erl b/src/couch/src/couch_server.erl index 002f08ebb..ede8227c8 100644 --- a/src/couch/src/couch_server.erl +++ b/src/couch/src/couch_server.erl @@ -15,7 +15,7 @@ -behaviour(config_listener). -vsn(3). --export([open/2,create/2,delete/2,get_version/0,get_version/1,get_uuid/0]). +-export([open/2,create/2,delete/2,get_version/0,get_version/1,get_git_sha/0,get_uuid/0]). -export([all_databases/0, all_databases/2]). -export([init/1, handle_call/3,sup_start_link/0]). -export([handle_cast/2,code_change/3,handle_info/2,terminate/2]). @@ -57,6 +57,7 @@ get_version(short) -> [Version|_Rest] = string:tokens(get_version(), "+"), Version. +get_git_sha() -> ?COUCHDB_GIT_SHA. get_uuid() -> case config:get("couchdb", "uuid", undefined) of diff --git a/src/couch/src/couch_stream.erl b/src/couch/src/couch_stream.erl index 83b0611eb..033562932 100644 --- a/src/couch/src/couch_stream.erl +++ b/src/couch/src/couch_stream.erl @@ -98,9 +98,9 @@ foldl({Engine, EngineState}, Fun, Acc) -> foldl(Engine, <<>>, Fun, Acc) -> foldl(Engine, Fun, Acc); foldl(Engine, Md5, UserFun, UserAcc) -> - InitAcc = {crypto:hash_init(md5), UserFun, UserAcc}, + InitAcc = {couch_hash:md5_hash_init(), UserFun, UserAcc}, {Md5Acc, _, OutAcc} = foldl(Engine, fun foldl_md5/2, InitAcc), - Md5 = crypto:hash_final(Md5Acc), + Md5 = couch_hash:md5_hash_final(Md5Acc), OutAcc. @@ -128,7 +128,7 @@ range_foldl(Engine, From, To, UserFun, UserAcc) when To >= From -> foldl_md5(Bin, {Md5Acc, UserFun, UserAcc}) -> - NewMd5Acc = crypto:hash_update(Md5Acc, Bin), + NewMd5Acc = couch_hash:md5_hash_update(Md5Acc, Bin), {NewMd5Acc, UserFun, UserFun(Bin, UserAcc)}. @@ -201,8 +201,8 @@ init({Engine, OpenerPid, OpenerPriority, Options}) -> {ok, #stream{ engine=Engine, opener_monitor=erlang:monitor(process, OpenerPid), - md5=crypto:hash_init(md5), - identity_md5=crypto:hash_init(md5), + md5=couch_hash:md5_hash_init(), + identity_md5=couch_hash:md5_hash_init(), encoding_fun=EncodingFun, end_encoding_fun=EndEncodingFun, max_buffer=couch_util:get_value( @@ -227,7 +227,7 @@ handle_call({write, Bin}, _From, Stream) -> encoding_fun = EncodingFun} = Stream, if BinSize + BufferLen > Max -> WriteBin = lists:reverse(Buffer, [Bin]), - IdenMd5_2 = crypto:hash_update(IdenMd5, WriteBin), + IdenMd5_2 = couch_hash:md5_hash_update(IdenMd5, WriteBin), case EncodingFun(WriteBin) of [] -> % case where the encoder did some internal buffering @@ -238,7 +238,7 @@ handle_call({write, Bin}, _From, Stream) -> WriteBin2 -> NewEngine = do_write(Engine, WriteBin2), WrittenLen2 = WrittenLen + iolist_size(WriteBin2), - Md5_2 = crypto:hash_update(Md5, WriteBin2) + Md5_2 = couch_hash:md5_hash_update(Md5, WriteBin2) end, {reply, ok, Stream#stream{ @@ -268,9 +268,9 @@ handle_call(close, _From, Stream) -> end_encoding_fun = EndEncodingFun} = Stream, WriteBin = lists:reverse(Buffer), - IdenMd5Final = crypto:hash_final(crypto:hash_update(IdenMd5, WriteBin)), + IdenMd5Final = couch_hash:md5_hash_final(couch_hash:md5_hash_update(IdenMd5, WriteBin)), WriteBin2 = EncodingFun(WriteBin) ++ EndEncodingFun(), - Md5Final = crypto:hash_final(crypto:hash_update(Md5, WriteBin2)), + Md5Final = couch_hash:md5_hash_final(couch_hash:md5_hash_update(Md5, WriteBin2)), Result = case WriteBin2 of [] -> {do_finalize(Engine), WrittenLen, IdenLen, Md5Final, IdenMd5Final}; diff --git a/src/couch/src/couch_users_db.erl b/src/couch/src/couch_users_db.erl index dd6d3208c..c7b41f1fc 100644 --- a/src/couch/src/couch_users_db.erl +++ b/src/couch/src/couch_users_db.erl @@ -23,7 +23,6 @@ -define(SIMPLE, <<"simple">>). -define(PASSWORD_SHA, <<"password_sha">>). -define(PBKDF2, <<"pbkdf2">>). --define(BCRYPT, <<"bcrypt">>). -define(ITERATIONS, <<"iterations">>). -define(SALT, <<"salt">>). -define(replace(L, K, V), lists:keystore(K, 1, L, {K, V})). @@ -60,7 +59,7 @@ before_doc_update(Doc, Db) -> % newDoc.salt = salt % newDoc.password = null save_doc(#doc{body={Body}} = Doc) -> - %% Support all schemes to smooth migration from legacy scheme + %% Support both schemes to smooth migration from legacy scheme Scheme = config:get("couch_httpd_auth", "password_scheme", "pbkdf2"), case {couch_util:get_value(?PASSWORD, Body), Scheme} of {null, _} -> % server admins don't have a user-db password entry @@ -85,13 +84,6 @@ save_doc(#doc{body={Body}} = Doc) -> Body3 = ?replace(Body2, ?SALT, Salt), Body4 = proplists:delete(?PASSWORD, Body3), Doc#doc{body={Body4}}; - {ClearPassword, "bcrypt"} -> - LogRounds = list_to_integer(config:get("couch_httpd_auth", "log_rounds", "10")), - DerivedKey = couch_passwords:bcrypt(ClearPassword, LogRounds), - Body0 = ?replace(Body, ?PASSWORD_SCHEME, ?BCRYPT), - Body1 = ?replace(Body0, ?DERIVED_KEY, DerivedKey), - Body2 = proplists:delete(?PASSWORD, Body1), - Doc#doc{body={Body2}}; {_ClearPassword, Scheme} -> couch_log:error("[couch_httpd_auth] password_scheme value of '~p' is invalid.", [Scheme]), throw({forbidden, "Server cannot hash passwords at this time."}) diff --git a/src/couch/src/test_engine_util.erl b/src/couch/src/test_engine_util.erl index fef9e9f92..6cc6bccdc 100644 --- a/src/couch/src/test_engine_util.erl +++ b/src/couch/src/test_engine_util.erl @@ -186,7 +186,7 @@ gen_write(Engine, St, {create, {DocId, Body, Atts0}}, UpdateSeq) -> [not_found] = Engine:open_docs(St, [DocId]), Atts = [couch_att:to_disk_term(Att) || Att <- Atts0], - Rev = crypto:hash(md5, term_to_binary({DocId, Body, Atts})), + Rev = couch_hash:md5_hash(term_to_binary({DocId, Body, Atts})), Doc0 = #doc{ id = DocId, @@ -323,11 +323,11 @@ gen_write(Engine, St, {Action, {DocId, Body, Atts0}}, UpdateSeq) -> gen_revision(conflict, DocId, _PrevRev, Body, Atts) -> - crypto:hash(md5, term_to_binary({DocId, Body, Atts})); + couch_hash:md5_hash(term_to_binary({DocId, Body, Atts})); gen_revision(delete, DocId, PrevRev, Body, Atts) -> gen_revision(update, DocId, PrevRev, Body, Atts); gen_revision(update, DocId, PrevRev, Body, Atts) -> - crypto:hash(md5, term_to_binary({DocId, PrevRev, Body, Atts})). + couch_hash:md5_hash(term_to_binary({DocId, PrevRev, Body, Atts})). gen_path(conflict, _RevPos, _PrevRevId, Rev, Leaf) -> @@ -373,7 +373,7 @@ prep_atts(Engine, St, [{FileName, Data} | Rest]) -> write_att(Stream, FileName, OrigData, <<>>) -> {StreamEngine, Len, Len, Md5, Md5} = couch_stream:close(Stream), - couch_util:check_md5(Md5, crypto:hash(md5, OrigData)), + couch_util:check_md5(Md5, couch_hash:md5_hash(OrigData)), Len = size(OrigData), couch_att:new([ {name, FileName}, diff --git a/src/couch/src/test_request.erl b/src/couch/src/test_request.erl index 4dfde1a33..48f49bda6 100644 --- a/src/couch/src/test_request.erl +++ b/src/couch/src/test_request.erl @@ -101,7 +101,11 @@ request(Method, Url, Headers, Body, Opts, N) -> {error, {'EXIT', {normal, _}}} -> % Connection closed right after a successful request that % used the same connection. - request(Method, Url, Headers, Body, N - 1); + request(Method, Url, Headers, Body, Opts, N - 1); + {error, retry_later} -> + % CouchDB is busy, let’s wait a bit + timer:sleep(3000 div N), + request(Method, Url, Headers, Body, Opts, N - 1); Error -> Error end. diff --git a/src/couch/src/test_util.erl b/src/couch/src/test_util.erl index 738e9a3fb..efb506460 100644 --- a/src/couch/src/test_util.erl +++ b/src/couch/src/test_util.erl @@ -101,6 +101,9 @@ start_applications([App|Apps], Acc) -> io:format(standard_error, "Application ~s was left running!~n", [App]), application:stop(App), start_applications([App|Apps], Acc); + {error, Reason} -> + io:format(standard_error, "Cannot start application '~s', reason ~p~n", [App, Reason]), + throw({error, {cannot_start, App, Reason}}); ok -> start_applications(Apps, [App|Acc]) end. diff --git a/src/couch/test/couch_passwords_tests.erl b/src/couch/test/couch_passwords_tests.erl index a56627361..dea6d6b7b 100644 --- a/src/couch/test/couch_passwords_tests.erl +++ b/src/couch/test/couch_passwords_tests.erl @@ -14,6 +14,7 @@ -include_lib("couch/include/couch_eunit.hrl"). + pbkdf2_test_()-> {"PBKDF2", [ @@ -51,44 +52,3 @@ pbkdf2_test_()-> {ok, <<"eefe3d61cd4da4e4e9945b3d6ba2158c2634e984">>}, couch_passwords:pbkdf2(<<"password">>, <<"salt">>, 16777216, 20) )}}]}. - - -bcrypt_test_() -> - { - "Bcrypt", - { - setup, - fun() -> - test_util:start_applications([bcrypt]) - end, - fun test_util:stop_applications/1, - [ - {"Log rounds: 4", - {timeout, 1, fun bcrypt_logRounds_4/0}}, - {"Log rounds: 5", - {timeout, 1, fun bcrypt_logRounds_5/0}}, - {"Log rounds: 12", - {timeout, 5, fun bcrypt_logRounds_12/0}}, - {"Null byte", - {timeout, 5, fun bcrypt_null_byte/0}} - - ] - } - }. - -bcrypt_logRounds_4() -> - bcrypt_assert_equal(<<"password">>, 4). - -bcrypt_logRounds_5() -> - bcrypt_assert_equal(<<"password">>, 5). - -bcrypt_logRounds_12() -> - bcrypt_assert_equal(<<"password">>, 12). - -bcrypt_null_byte() -> - bcrypt_assert_equal(<<"passw\0rd">>, 12). - -bcrypt_assert_equal(Password, Rounds) when is_integer(Rounds) -> - HashPass = couch_passwords:bcrypt(Password, Rounds), - ReHashPass = couch_passwords:bcrypt(Password, HashPass), - ?assertEqual(HashPass, ReHashPass). diff --git a/src/couch/test/couchdb_attachments_tests.erl b/src/couch/test/couchdb_attachments_tests.erl index a85a01f48..04859dbc9 100644 --- a/src/couch/test/couchdb_attachments_tests.erl +++ b/src/couch/test/couchdb_attachments_tests.erl @@ -208,7 +208,7 @@ should_upload_attachment_with_valid_md5_header({Host, DbName}) -> Headers = [ {"Content-Length", "34"}, {"Content-Type", "text/plain"}, - {"Content-MD5", ?b2l(base64:encode(crypto:hash(md5, Body)))}, + {"Content-MD5", ?b2l(base64:encode(couch_hash:md5_hash(Body)))}, {"Host", Host} ], {ok, Code, Json} = request("PUT", AttUrl, Headers, Body), @@ -224,7 +224,7 @@ should_upload_attachment_by_chunks_with_valid_md5_header({Host, DbName}) -> Body = [chunked_body([Part1, Part2]), "\r\n"], Headers = [ {"Content-Type", "text/plain"}, - {"Content-MD5", ?b2l(base64:encode(crypto:hash(md5, AttData)))}, + {"Content-MD5", ?b2l(base64:encode(couch_hash:md5_hash(AttData)))}, {"Host", Host}, {"Transfer-Encoding", "chunked"} ], @@ -239,7 +239,7 @@ should_upload_attachment_by_chunks_with_valid_md5_trailer({Host, DbName}) -> AttData = <<"We all live in a yellow submarine!">>, <<Part1:21/binary, Part2:13/binary>> = AttData, Body = [chunked_body([Part1, Part2]), - "Content-MD5: ", base64:encode(crypto:hash(md5, AttData)), + "Content-MD5: ", base64:encode(couch_hash:md5_hash(AttData)), "\r\n\r\n"], Headers = [ {"Content-Type", "text/plain"}, diff --git a/src/couch_epi/src/couch_epi_data.erl b/src/couch_epi/src/couch_epi_data.erl index 93e39f69d..bbed828bb 100644 --- a/src/couch_epi/src/couch_epi_data.erl +++ b/src/couch_epi/src/couch_epi_data.erl @@ -111,4 +111,4 @@ definitions({module, Modules}) -> hash_of_file(FilePath) -> {ok, Data} = file:read_file(FilePath), - crypto:hash(md5, Data). + couch_hash:md5_hash(Data). diff --git a/src/couch_epi/src/couch_epi_util.erl b/src/couch_epi/src/couch_epi_util.erl index e99db4668..ea4b10ea8 100644 --- a/src/couch_epi/src/couch_epi_util.erl +++ b/src/couch_epi/src/couch_epi_util.erl @@ -22,7 +22,7 @@ module_version(Module) -> VSNs. hash(Term) -> - <<SigInt:128/integer>> = crypto:hash(md5, term_to_binary(Term)), + <<SigInt:128/integer>> = couch_hash:md5_hash(term_to_binary(Term)), lists:flatten(io_lib:format("\"~.36B\"",[SigInt])). module_exists(Module) -> diff --git a/src/couch_event/src/couch_event_os_sup.erl b/src/couch_event/src/couch_event_os_sup.erl deleted file mode 100644 index f219d0000..000000000 --- a/src/couch_event/src/couch_event_os_sup.erl +++ /dev/null @@ -1,82 +0,0 @@ -% 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. - - -% This causes an OS process to spawned and it is notified every time a database -% is updated. -% -% The notifications are in the form of a the database name sent as a line of -% text to the OS processes stdout. - - --module(couch_event_os_sup). --behaviour(supervisor). --behaviour(config_listener). - --vsn(2). - --export([ - start_link/0, - init/1 -]). - --export([ - handle_config_change/5, - handle_config_terminate/3 -]). - - -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - - -init([]) -> - UpdateNotifierExes = config:get("update_notification"), - Children = [ - { - config_listener_mon, - {config_listener_mon, start_link, [?MODULE, nil]}, - permanent, - 5000, - worker, - [config_listener_mon] - } - | [child(Id, Exe) || {Id, Exe} <- UpdateNotifierExes]], - - {ok, { - {one_for_one, 10, 3600}, - Children - }}. - - -handle_config_change("update_notification", Id, deleted, _, _) -> - supervisor:terminate_child(?MODULE, Id), - supervisor:delete_child(?MODULE, Id), - {ok, nil}; -handle_config_change("update_notification", Id, Exe, _, _) when is_list(Exe) -> - supervisor:start_child(?MODULE, child(Id, Exe)), - {ok, nil}; -handle_config_change(_, _, _, _, _) -> - {ok, nil}. - -handle_config_terminate(_Server, _Reason, _State) -> - ok. - -child(Id, Arg) -> - { - Id, - {couch_event_os_listener, start_link, [Arg]}, - permanent, - 1000, - supervisor, - [couch_event_os_listener] - }. diff --git a/src/couch_event/src/couch_event_sup2.erl b/src/couch_event/src/couch_event_sup2.erl index 36fbe542e..2d88b93d4 100644 --- a/src/couch_event/src/couch_event_sup2.erl +++ b/src/couch_event/src/couch_event_sup2.erl @@ -38,13 +38,6 @@ init(_) -> 5000, worker, [couch_event_server] - }, - {couch_event_os_sup, - {couch_event_os_sup, start_link, []}, - permanent, - 5000, - supervisor, - [couch_event_os_sup] } ], {ok, {{one_for_one, 5, 10}, Children}}. diff --git a/src/couch_index/test/couch_index_ddoc_updated_tests.erl b/src/couch_index/test/couch_index_ddoc_updated_tests.erl index 40dadcc62..0e23adf91 100644 --- a/src/couch_index/test/couch_index_ddoc_updated_tests.erl +++ b/src/couch_index/test/couch_index_ddoc_updated_tests.erl @@ -118,7 +118,7 @@ fake_index() -> (idx_name, {_DbName, DDoc}) -> DDoc#doc.id; (signature, {_DbName, DDoc}) -> - crypto:hash(md5, term_to_binary(DDoc)); + couch_hash:md5_hash(term_to_binary(DDoc)); (update_seq, Seq) -> Seq end), diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl index 82bbd7928..533dd2de9 100644 --- a/src/couch_mrview/src/couch_mrview.erl +++ b/src/couch_mrview/src/couch_mrview.erl @@ -220,7 +220,7 @@ query_all_docs(Db, Args, Callback, Acc) when is_list(Args) -> query_all_docs(Db, Args0, Callback, Acc) -> Sig = couch_util:with_db(Db, fun(WDb) -> {ok, Info} = couch_db:get_db_info(WDb), - couch_index_util:hexsig(crypto:hash(md5, term_to_binary(Info))) + couch_index_util:hexsig(couch_hash:md5_hash(term_to_binary(Info))) end), Args1 = Args0#mrargs{view_type=map}, Args2 = couch_mrview_util:validate_args(Args1), diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl index eb461d017..120a9b873 100644 --- a/src/couch_mrview/src/couch_mrview_util.erl +++ b/src/couch_mrview/src/couch_mrview_util.erl @@ -157,7 +157,7 @@ ddoc_to_mrst(DbName, #doc{id=Id, body={Fields}}) -> keyseq_indexed=KeySeqIndexed }, SigInfo = {Views, Language, DesignOpts, couch_index_util:sort_lib(Lib)}, - {ok, IdxState#mrst{sig=crypto:hash(md5, term_to_binary(SigInfo))}}. + {ok, IdxState#mrst{sig=couch_hash:md5_hash(term_to_binary(SigInfo))}}. set_view_type(_Args, _ViewName, []) -> @@ -203,7 +203,7 @@ view_sig(Db, State, View, #mrargs{include_docs=true}=Args) -> keyseq_indexed=KeySeqIndexed } = State, Term = view_sig_term(BaseSig, UpdateSeq, PurgeSeq, KeySeqIndexed, SeqIndexed), - couch_index_util:hexsig(crypto:hash(md5, term_to_binary(Term))); + couch_index_util:hexsig(couch_hash:md5_hash(term_to_binary(Term))); view_sig(Db, State, {_Nth, _Lang, View}, Args) -> view_sig(Db, State, View, Args); view_sig(_Db, State, View, Args0) -> @@ -217,7 +217,7 @@ view_sig(_Db, State, View, Args0) -> extra=[] }, Term = view_sig_term(Sig, UpdateSeq, PurgeSeq, KeySeqIndexed, SeqIndexed, Args), - couch_index_util:hexsig(crypto:hash(md5, term_to_binary(Term))). + couch_index_util:hexsig(couch_hash:md5_hash(term_to_binary(Term))). view_sig_term(BaseSig, UpdateSeq, PurgeSeq, false, false) -> {BaseSig, UpdateSeq, PurgeSeq}; @@ -1008,7 +1008,7 @@ sig_vsn_12x(State) -> {ViewInfo, State#mrst.language, State#mrst.design_opts, couch_index_util:sort_lib(State#mrst.lib)} end, - crypto:hash(md5, term_to_binary(SigData)). + couch_hash:md5_hash(term_to_binary(SigData)). old_view_format(View) -> { diff --git a/src/couch_replicator/src/couch_replicator_auth.erl b/src/couch_replicator/src/couch_replicator_auth.erl index 60273fc32..7f51cdd1c 100644 --- a/src/couch_replicator/src/couch_replicator_auth.erl +++ b/src/couch_replicator/src/couch_replicator_auth.erl @@ -28,7 +28,7 @@ -type code() :: non_neg_integer(). --define(DEFAULT_PLUGINS, "couch_replicator_auth_noop"). +-define(DEFAULT_PLUGINS, "couch_replicator_auth_session,couch_replicator_auth_noop"). % Behavior API diff --git a/src/couch_replicator/src/couch_replicator_ids.erl b/src/couch_replicator/src/couch_replicator_ids.erl index e8faf8ea3..e10b98082 100644 --- a/src/couch_replicator/src/couch_replicator_ids.erl +++ b/src/couch_replicator/src/couch_replicator_ids.erl @@ -112,7 +112,7 @@ maybe_append_filters(Base, {error, FilterParseError} -> throw({error, FilterParseError}) end, - couch_util:to_hex(crypto:hash(md5, term_to_binary(Base2))). + couch_util:to_hex(couch_hash:md5_hash(term_to_binary(Base2))). maybe_append_options(Options, RepOptions) -> diff --git a/src/couch_replicator/test/couch_replicator_many_leaves_tests.erl b/src/couch_replicator/test/couch_replicator_many_leaves_tests.erl index b2445a236..eee5b1647 100644 --- a/src/couch_replicator/test/couch_replicator_many_leaves_tests.erl +++ b/src/couch_replicator/test/couch_replicator_many_leaves_tests.erl @@ -141,7 +141,7 @@ add_doc_siblings(Db, _DocId, 0, AccDocs, AccRevs) -> add_doc_siblings(Db, DocId, NumLeaves, AccDocs, AccRevs) -> Value = ?l2b(?i2l(NumLeaves)), - Rev = crypto:hash(md5, Value), + Rev = couch_hash:md5_hash(Value), Doc = #doc{ id = DocId, revs = {1, [Rev]}, diff --git a/src/couch_replicator/test/couch_replicator_test_helper.erl b/src/couch_replicator/test/couch_replicator_test_helper.erl index 8ee2114f0..fd0409164 100644 --- a/src/couch_replicator/test/couch_replicator_test_helper.erl +++ b/src/couch_replicator/test/couch_replicator_test_helper.erl @@ -93,16 +93,16 @@ find_att([Att | Rest], Name) -> att_md5(Att) -> Md50 = couch_att:foldl( Att, - fun(Chunk, Acc) -> crypto:hash_update(Acc, Chunk) end, - crypto:hash_init(md5)), - crypto:hash_final(Md50). + fun(Chunk, Acc) -> couch_hash:md5_hash_update(Acc, Chunk) end, + couch_hash:md5_hash_init()), + couch_hash:md5_hash_final(Md50). att_decoded_md5(Att) -> Md50 = couch_att:foldl_decode( Att, - fun(Chunk, Acc) -> crypto:hash_update(Acc, Chunk) end, - crypto:hash_init(md5)), - crypto:hash_final(Md50). + fun(Chunk, Acc) -> couch_hash:md5_hash_update(Acc, Chunk) end, + couch_hash:md5_hash_init()), + couch_hash:md5_hash_final(Md50). db_url(DbName) -> iolist_to_binary([ diff --git a/src/fabric/src/fabric.erl b/src/fabric/src/fabric.erl index 5c517a3bd..185ffebe5 100644 --- a/src/fabric/src/fabric.erl +++ b/src/fabric/src/fabric.erl @@ -289,13 +289,14 @@ purge_docs(_DbName, _IdsRevs) -> not_implemented. %% @doc spawns a process to upload attachment data and -%% returns a function that shards can use to communicate -%% with the spawned middleman process +%% returns a fabric attachment receiver context tuple +%% with the spawned middleman process, an empty binary, +%% or exits with an error tuple {Error, Arg} -spec att_receiver(#httpd{}, Length :: undefined | chunked | pos_integer() | {unknown_transfer_encoding, any()}) -> - function() | binary(). + {fabric_attachment_receiver, pid(), chunked | pos_integer()} | binary(). att_receiver(Req, Length) -> - fabric_doc_attachments:receiver(Req, Length). + fabric_doc_atts:receiver(Req, Length). %% @equiv all_docs(DbName, [], Callback, Acc0, QueryArgs) all_docs(DbName, Callback, Acc, QueryArgs) -> diff --git a/src/mem3/src/mem3_rep.erl b/src/mem3/src/mem3_rep.erl index 3d9187796..8d996d617 100644 --- a/src/mem3/src/mem3_rep.erl +++ b/src/mem3/src/mem3_rep.erl @@ -106,8 +106,8 @@ make_local_id(#shard{node=SourceNode}, #shard{node=TargetNode}, Filter) -> make_local_id(SourceThing, TargetThing, Filter) -> - S = couch_util:encodeBase64Url(crypto:hash(md5, term_to_binary(SourceThing))), - T = couch_util:encodeBase64Url(crypto:hash(md5, term_to_binary(TargetThing))), + S = couch_util:encodeBase64Url(couch_hash:md5_hash(term_to_binary(SourceThing))), + T = couch_util:encodeBase64Url(couch_hash:md5_hash(term_to_binary(TargetThing))), F = case is_function(Filter) of true -> {new_uniq, Hash} = erlang:fun_info(Filter, new_uniq), @@ -339,7 +339,7 @@ update_locals(Acc) -> find_repl_doc(SrcDb, TgtUUIDPrefix) -> SrcUUID = couch_db:get_uuid(SrcDb), - S = couch_util:encodeBase64Url(crypto:hash(md5, term_to_binary(SrcUUID))), + S = couch_util:encodeBase64Url(couch_hash:md5_hash(term_to_binary(SrcUUID))), DocIdPrefix = <<"_local/shard-sync-", S/binary, "-">>, FoldFun = fun(#doc{id = DocId, body = {BodyProps}} = Doc, _) -> TgtUUID = couch_util:get_value(<<"target_uuid">>, BodyProps, <<>>), diff --git a/src/setup/src/setup.erl b/src/setup/src/setup.erl index 943318675..3ae455f54 100644 --- a/src/setup/src/setup.erl +++ b/src/setup/src/setup.erl @@ -30,15 +30,29 @@ require_node_count(undefined) -> require_node_count(_) -> ok. -error_bind_address() -> - throw({error, "Cluster setup requires bind_addres != 127.0.0.1"}). - -require_bind_address("127.0.0.1", undefined) -> - error_bind_address(); -require_bind_address("127.0.0.1", <<"127.0.0.1">>) -> - error_bind_address(); -require_bind_address(_, _) -> - ok. +error_local_bind_address() -> + throw({error, "Cluster setup requires a remote bind_address (not 127.0.0.1 nor ::1)"}). + +error_invalid_bind_address(InvalidBindAddress) -> + throw({error, io:format("Setup requires a valid IP bind_address. " ++ + "~p is invalid.", [InvalidBindAddress])}). + +require_remote_bind_address(OldBindAddress, NewBindAddress) -> + case {OldBindAddress, NewBindAddress} of + {"127.0.0.1", undefined} -> error_local_bind_address(); + {_, <<"127.0.0.1">>} -> error_local_bind_address(); + {"::1", undefined} -> error_local_bind_address(); + {_, <<"::1">>} -> error_local_bind_address(); + {_, undefined} -> ok; + {_, PresentNewBindAddress} -> require_valid_bind_address(PresentNewBindAddress) + end. + +require_valid_bind_address(BindAddress) -> + ListBindAddress = binary_to_list(BindAddress), + case inet_parse:address(ListBindAddress) of + {ok, _} -> ok; + {error, _} -> error_invalid_bind_address(ListBindAddress) + end. is_cluster_enabled() -> % bind_address != 127.0.0.1 AND admins != empty @@ -122,7 +136,6 @@ enable_cluster_http(Options) -> {ok, "201", _, _} -> ok; Else -> - couch_log:notice("send_req: ~p~n", [Else]), {error, Else} end. @@ -143,13 +156,13 @@ enable_cluster_int(Options, false) -> % if bind_address == 127.0.0.1 and no bind_address in req -> error CurrentBindAddress = config:get("chttpd","bind_address"), NewBindAddress = proplists:get_value(bind_address, Options), - ok = require_bind_address(CurrentBindAddress, NewBindAddress), + ok = require_remote_bind_address(CurrentBindAddress, NewBindAddress), NodeCount = couch_util:get_value(node_count, Options), ok = require_node_count(NodeCount), Port = proplists:get_value(port, Options), setup_node(NewCredentials, NewBindAddress, NodeCount, Port), - couch_log:notice("Enable Cluster: ~p~n", [Options]). + couch_log:debug("Enable Cluster: ~p~n", [Options]). set_admin(Username, Password) -> config:set("admins", binary_to_list(Username), binary_to_list(Password)). @@ -162,6 +175,7 @@ setup_node(NewCredentials, NewBindAddress, NodeCount, Port) -> set_admin(Username, Password) end, + ok = require_valid_bind_address(NewBindAddress), case NewBindAddress of undefined -> config:set("chttpd", "bind_address", "0.0.0.0"); @@ -211,7 +225,7 @@ enable_single_node(Options) -> setup_node(NewCredentials, NewBindAddress, 1, Port), Dbs = proplists:get_value(ensure_dbs_exist, Options, cluster_system_dbs()), finish_cluster_int(Dbs, has_cluster_system_dbs(Dbs)), - couch_log:notice("Enable Single Node: ~p~n", [Options]). + couch_log:debug("Enable Single Node: ~p~n", [Options]). add_node(Options) -> @@ -220,7 +234,7 @@ add_node(Options) -> add_node_int(_Options, false) -> {error, cluster_not_enabled}; add_node_int(Options, true) -> - couch_log:notice("add node_int: ~p~n", [Options]), + couch_log:debug("add node_int: ~p~n", [Options]), ErlangCookie = erlang:get_cookie(), % POST to nodeB/_setup @@ -251,7 +265,6 @@ add_node_int(Options, true) -> % when done, PUT :5986/nodes/nodeB create_node_doc(Host, Name); Else -> - couch_log:notice("send_req: ~p~n", [Else]), Else end. diff --git a/test/javascript/tests/design_docs.js b/test/javascript/tests/design_docs.js index 6e12001d7..ed1e72f3f 100644 --- a/test/javascript/tests/design_docs.js +++ b/test/javascript/tests/design_docs.js @@ -373,7 +373,13 @@ couchTests.design_docs = function(debug) { } T(db.deleteDoc(designDoc).ok); - T(db.open(designDoc._id) == null); + waitForSuccess(function() { + var ddoc = db.open(designDoc._id) + if (ddoc != null) { + throw({}); + } + return true; + }, 'db.open(designDoc._id)'); T(db.view("test/no_docs") == null); T(db.ensureFullCommit().ok); diff --git a/test/javascript/tests/users_db_security.js b/test/javascript/tests/users_db_security.js index c55c76434..1db6c14c5 100644 --- a/test/javascript/tests/users_db_security.js +++ b/test/javascript/tests/users_db_security.js @@ -15,8 +15,6 @@ couchTests.users_db_security = function(debug) { var usersDb = new CouchDB(db_name, {"X-Couch-Full-Commit":"false"}); try { usersDb.createDb(); } catch (e) { /* ignore if exists*/ } - var passwordSchemes = ['pbkdf2', 'bcrypt']; - if (debug) debugger; var loginUser = function(username) { @@ -32,13 +30,7 @@ couchTests.users_db_security = function(debug) { // the actual tests var username1 = username.replace(/[0-9]$/, ""); var password = pws[username]; - waitForSuccess(function() { - var req = CouchDB.login(username1, pws[username]); - if (req.ok) { - return true - } - throw({}); - }, 'loginUser'); + T(CouchDB.login(username1, pws[username]).ok); }; var open_as = function(db, docId, username) { @@ -94,7 +86,7 @@ couchTests.users_db_security = function(debug) { } }; - var testFun = function(scheme, derivedKeyTest, saltTest) + var testFun = function() { // _users db @@ -113,12 +105,11 @@ couchTests.users_db_security = function(debug) { // jan's gonna be admin as he's the first user TEquals(true, usersDb.save(userDoc).ok, "should save document"); + wait(5000) userDoc = open_as(usersDb, "org.couchdb.user:jchris", "jchris"); TEquals(undefined, userDoc.password, "password field should be null 1"); - TEquals(scheme, userDoc.password_scheme, "password_scheme should be " + scheme); - derivedKeyTest(userDoc.derived_key); - saltTest(userDoc.salt); - + TEquals(40, userDoc.derived_key.length, "derived_key should exist"); + TEquals(32, userDoc.salt.length, "salt should exist"); // create server admin @@ -150,13 +141,10 @@ couchTests.users_db_security = function(debug) { var jchrisDoc = open_as(usersDb, "org.couchdb.user:jchris", "jan"); TEquals(undefined, jchrisDoc.password, "password field should be null 2"); - TEquals(scheme, jchrisDoc.password_scheme, "password_scheme should be " + scheme); - derivedKeyTest(jchrisDoc.derived_key); - saltTest(jchrisDoc.salt); + TEquals(40, jchrisDoc.derived_key.length, "derived_key should exist"); + TEquals(32, jchrisDoc.salt.length, "salt should exist"); - if(userDoc.salt || jchrisDoc.salt) { - TEquals(true, userDoc.salt != jchrisDoc.salt, "should have new salt"); - } + TEquals(true, userDoc.salt != jchrisDoc.salt, "should have new salt"); TEquals(true, userDoc.derived_key != jchrisDoc.derived_key, "should have new derived_key"); @@ -239,7 +227,7 @@ couchTests.users_db_security = function(debug) { TEquals("forbidden", e.error, "non-admins can't read design docs"); } - // admin should be able to read _list + // admin shold be able to read _list var listPath = ddoc["_id"] + "/_list/names/test"; var result = request_as(usersDb, listPath, "jan"); var lines = result.responseText.split("\n"); @@ -385,140 +373,14 @@ couchTests.users_db_security = function(debug) { }); }; - var derivedKeyTests = { - pbkdf2: function(derived_key) { - TEquals(40, derived_key.length, "derived_key should exist"); - }, - bcrypt: function(derived_key) { - TEquals(60, derived_key.length, "derived_key should exist"); - } - }; - var saltTests = { - pbkdf2: function(salt) { - TEquals(32, salt.length, "salt should exist"); - }, - bcrypt: function(salt) { - TEquals(undefined, salt, "salt should not exist"); - } - }; - passwordSchemes.forEach(function(scheme){ - run_on_modified_server( - [{ - section: "couch_httpd_auth", - key: "iterations", value: "1" - }, { - section: "couch_httpd_auth", - key: "password_scheme", value: scheme - }, { - section: "admins", - key: "jan", value: "apple" - }], - function() { - try { - testFun(scheme, derivedKeyTests[scheme], saltTests[scheme]); - } catch (e) { - throw(e) - } finally { - CouchDB.login("jan", "apple"); - usersDb.deleteDb(); // cleanup - waitForSuccess(function() { - var req = CouchDB.request("GET", db_name); - if (req.status == 404) { - return true - } - throw({}); - }, 'usersDb.deleteDb') - - usersDb.createDb(); - waitForSuccess(function() { - var req = CouchDB.request("GET", db_name); - if (req.status == 200) { - return true - } - throw({}); - }, 'usersDb.creteDb') - } - } - ); - }); - - var testFunUpdatePasswordScheme = function() { - var userDocs = { - jchris: { - _id: "org.couchdb.user:jchris", - type: "user", - name: "jchris", - password: "mp3", - roles: [] - }, - fdmanana: { - _id: "org.couchdb.user:fdmanana", - type: "user", - name: "fdmanana", - password: "foobar", - roles: [] - } - }; - - // create new user (has pbkdf2 hash) - TEquals(true, usersDb.save(userDocs.jchris).ok, "should save document"); - wait(5000); - var userDoc = open_as(usersDb, "org.couchdb.user:jchris", "jchris"); - TEquals(undefined, userDoc.password, "password field should be null 1"); - TEquals("pbkdf2", userDoc.password_scheme, "password_scheme should be pbkdf2"); - derivedKeyTests.pbkdf2(userDoc.derived_key); - saltTests.pbkdf2(userDoc.salt); - - // change scheme to bcrypt - CouchDB.login("jan", "apple"); - var xhr = CouchDB.request("PUT", "/_node/node1@127.0.0.1/_config/couch_httpd_auth/password_scheme", { - body : JSON.stringify("bcrypt"), - headers: {"X-Couch-Persist": "false"} - }); - TEquals(200, xhr.status); - xhr = CouchDB.request("GET", "/_node/node1@127.0.0.1/_config/couch_httpd_auth/password_scheme"); - var scheme = JSON.parse(xhr.responseText); - TEquals("bcrypt", scheme); - - // create new user (has bcrypt hash) - TEquals(true, usersDb.save(userDocs.fdmanana).ok, "should save document"); - wait(5000); - userDoc = open_as(usersDb, "org.couchdb.user:fdmanana", "fdmanana"); - TEquals(undefined, userDoc.password, "password field should be null 1"); - TEquals("bcrypt", userDoc.password_scheme, "password_scheme should be bcrypt"); - derivedKeyTests.bcrypt(userDoc.derived_key); - saltTests.bcrypt(userDoc.salt); - - // test that both users can still log in - TEquals(true, CouchDB.login(userDocs.jchris.name, userDocs.jchris.password).ok); - TEquals(true, CouchDB.login(userDocs.fdmanana.name, userDocs.fdmanana.password).ok); - - // change scheme back to pbkdf2 - CouchDB.login("jan", "apple"); - var xhr = CouchDB.request("PUT", "/_node/node1@127.0.0.1/_config/couch_httpd_auth/password_scheme", { - body : JSON.stringify("pbkdf2"), - headers: {"X-Couch-Persist": "false"} - }); - TEquals(200, xhr.status); - xhr = CouchDB.request("GET", "/_node/node1@127.0.0.1/_config/couch_httpd_auth/password_scheme"); - var scheme = JSON.parse(xhr.responseText); - TEquals("pbkdf2", scheme); - - // test that both users can still log in - TEquals(true, CouchDB.login(userDocs.jchris.name, userDocs.jchris.password).ok); - TEquals(true, CouchDB.login(userDocs.fdmanana.name, userDocs.fdmanana.password).ok); - }; run_on_modified_server( - [{ - section: "couch_httpd_auth", - key: "iterations", value: "1" - }, { - section: "admins", - key: "jan", value: "apple" - }], + [{section: "couch_httpd_auth", + key: "iterations", value: "1"}, + {section: "admins", + key: "jan", value: "apple"}], function() { try { - testFunUpdatePasswordScheme(); + testFun(); } finally { CouchDB.login("jan", "apple"); usersDb.deleteDb(); // cleanup @@ -540,6 +402,5 @@ couchTests.users_db_security = function(debug) { } } ); - CouchDB.logout(); }; |