1
0
Fork 0

Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel

Conflicts:
	Documentation/DbaManual/Authentication.md
	Documentation/DbaManual/UserManagementTOC.md
	js/common/modules/org/arangodb/users.js
This commit is contained in:
Frank Celler 2013-02-20 13:52:20 +01:00
commit 36da27ac6f
92 changed files with 2799 additions and 698 deletions

View File

@ -19,7 +19,7 @@ BUILT_SOURCES += @V8_LIBS@
@echo "--------------------------------------------------------------------------------"
@echo
cd @top_srcdir@/3rdParty/V8 && $(MAKE) library=static strictaliasing=off snapshot=off @V8_TARGET@
cd @top_srcdir@/3rdParty/V8 && $(MAKE) library=static strictaliasing=off snapshot=off werror=no @V8_TARGET@
touch @srcdir@/.v8-build-@TRI_BITS@

View File

@ -1,6 +1,19 @@
v1.2.beta3 (XXXX-XX-XX)
-----------------------
* issue #393: added REST API for user management at /_api/user
* issue #393, #128: added simple cryptographic functions for user actions in module "crypto":
* require("org/arangodb/crypto").md5()
* require("org/arangodb/crypto").sha256()
* require("org/arangodb/crypto").rand()
* added replaceByExample() Javascript and REST API method
* added updateByExample() Javascript and REST API method
* added optional "limit" parameter for removeByExample() Javascript and REST API method
* fixed issue #413
* updated bundled V8 version from 3.9.4 to 3.16.14.1

View File

@ -13,8 +13,8 @@ will allow the administrator to restrict access to collections and queries to
certain users, given them either read or write access.
Currently, you can only secure the access to ArangoDB in an all-or-nothing
fashion. The collection `_users` contains all user and the SHA256 of their
passwords. A user can be active or inactive. A typical document of this
fashion. The collection `_users` contains all users and a salted SHA256 hash
of their passwords. A user can be active or inactive. A typical document of this
collection is
@EXAMPLE_ARANGOSH_OUTPUT{AuthenticationExample1}
@ -40,10 +40,18 @@ web interface.
@anchor UserManagementSave
@copydetails JSF_saveUser
@CLEARPAGE
@anchor UserManagementDocument
@copydetails JSF_documentUser
@CLEARPAGE
@anchor UserManagementReplace
@copydetails JSF_replaceUser
@CLEARPAGE
@anchor UserManagementUpdate
@copydetails JSF_updateUser
@CLEARPAGE
@anchor UserManagementRemove
@copydetails JSF_removeUser

View File

@ -6,6 +6,7 @@ TOC {#DbaManualAuthenticationTOC}
- @ref DbaManualAuthenticationCommandLine
- @ref UserManagementIntro
- @ref UserManagementSave "users.save"
- @ref UserManagementDocument "users.document"
- @ref UserManagementReplace "users.replace"
- @ref UserManagementRemove "users.remove"
- @ref UserManagementReload "users.reload"

View File

@ -0,0 +1,11 @@
> curl --data @- -X PUT --dump - http://localhost:8529/_api/simple/replace-by-example
{ "collection" : "test", "example" : { "age" : 37, "likes" : "tennis" }, "newValue" : { "foo" : "bar" }, "limit" : 3 }
HTTP/1.1 200 Ok
content-type: application/json
{
"code": 200,
"replaced": 1,
"error": false
}

View File

@ -0,0 +1,11 @@
> curl --data @- -X PUT --dump - http://localhost:8529/_api/simple/update-by-example
{ "collection" : "test", "example" : { "age" : 37, "likes" : "tennis" }, "newValue" : { "age" : null, "likes" : "foo" }, "keepNull" : false }
HTTP/1.1 200 Ok
content-type: application/json
{
"code": 200,
"updated": 1,
"error": false
}

View File

@ -65,3 +65,11 @@ dispose the server-side cursor afterwards.
@CLEARPAGE
@anchor HttpSimpleRemoveByExample
@copydetails JSA_PUT_api_simple_remove_by_example
@CLEARPAGE
@anchor HttpSimpleReplaceByExample
@copydetails JSA_PUT_api_simple_replace_by_example
@CLEARPAGE
@anchor HttpSimpleUpdateByExample
@copydetails JSA_PUT_api_simple_update_by_example

View File

@ -0,0 +1,40 @@
HTTP Interface for User Management {#HttpUser}
==============================================
@NAVIGATE_HttpUser
@EMBEDTOC{HttpUserTOC}
User Management {#HttpUserIntro}
================================
This is an introduction to ArangoDB's Http interface for managing users.
The interface provides a simple means to add, update, and remove users.
All users managed through this interface will be stored in the system
collection `_users`.
This specialised interface intentionally does not provide all functionality
that is available in the regular document REST API.
Operations on users may become more restricted than regular document operations,
and extra privilege and security security checks may be introduced in the
future for this interface.
@anchor HttpUserSave
@copydetails JSF_POST_api_user
@CLEARPAGE
@anchor HttpUserReplace
@copydetails JSF_PUT_api_user
@CLEARPAGE
@anchor HttpUserUpdate
@copydetails JSF_PATCH_api_user
@CLEARPAGE
@anchor HttpUserRemove
@copydetails JSF_DELETE_api_user
@CLEARPAGE
@anchor HttpUserDocument
@copydetails JSF_GET_api_user

View File

@ -0,0 +1,10 @@
TOC {#HttpUserTOC}
===================
- @ref HttpUser
- @ref HttpUserIntro
- @ref HttpUserSave "POST /_api/user"
- @ref HttpUserReplace "PUT /_api/user/username"
- @ref HttpUserUpdate "PATCH /_api/user/username"
- @ref HttpUserRemove "DELETE /_api/user/username"
- @ref HttpUserDocument "GET /_api/user/username"

View File

@ -19,6 +19,7 @@ ArangoDB for API Implementors (@VERSION) {#ImplementorManual}
@CHAPTER_REF{HttpImport}
@CHAPTER_REF{HttpBatch}
@CHAPTER_REF{HttpSystem}
@CHAPTER_REF{HttpUser}
@CHAPTER_REF{HttpMisc}
@CHAPTER_REF{Communication}
@CHAPTER_REF{NamingConventions}

View File

@ -49,6 +49,7 @@ DOXYGEN = \
Doxygen/js/actions/system/api-query.c \
Doxygen/js/actions/system/api-simple.c \
Doxygen/js/actions/system/api-system.c \
Doxygen/js/actions/system/api-user.c \
Doxygen/js/common/bootstrap/module-console.c \
Doxygen/js/common/bootstrap/module-fs.c \
Doxygen/js/common/bootstrap/modules.c \
@ -98,6 +99,7 @@ WIKI = \
HttpQuery \
HttpSimple \
HttpSystem \
HttpUser \
ImpManual \
ImpManualBasics \
ImplementorManual \

View File

@ -320,8 +320,22 @@ The `removeByExample` method returns the number of documents removed:
arangosh> db.mycollection.removeByExample({ value: 1 });
2
The number of documents to remove can optionally be limited.
The method is also available via the REST API (@ref HttpSimple).
### Replace / Update by Example
ArangoDB 1.2 also provides "Replace by Example" and "Update by Example" methods that
can be used to fully or partially update documents from a collection that match the
specified example. This can be used to replace document values easily with a single
operation.
The `replaceByExample` and `updateByExaple` methods return the number of documents
modified. Both operations can be limited to a specific number of documents if required.
Both methods are also available via the REST API (@ref HttpSimple).
### Collection revision id
ArangoDB 1.2 collections have a revision property. The current revision of a collection

View File

@ -89,6 +89,14 @@ Collection Methods {#HandlingDocumentsCollectionMethods}
@anchor HandlingDocumentsRemoveByExample
@copydetails JSF_ArangoCollection_prototype_removeByExample
@CLEARPAGE
@anchor HandlingDocumentsReplaceByExample
@copydetails JSF_ArangoCollection_prototype_replaceByExample
@CLEARPAGE
@anchor HandlingDocumentsUpdateByExample
@copydetails JSF_ArangoCollection_prototype_updateByExample
@CLEARPAGE
Database Methods {#HandlingDocumentsDatabaseMethods}
----------------------------------------------------

View File

@ -13,6 +13,8 @@ TOC {#HandlingDocumentsTOC}
- @ref HandlingDocumentsUpdate "collection.update"
- @ref HandlingDocumentsRemove "collection.remove"
- @ref HandlingDocumentsRemoveByExample "collection.removeByExample"
- @ref HandlingDocumentsReplaceByExample "collection.replaceByExample"
- @ref HandlingDocumentsUpdateByExample "collection.updateByExample"
- @ref HandlingDocumentsDatabaseMethods
- @ref HandlingDocumentsDbRead "db._document"
- @ref HandlingDocumentsDbReplace "db._replace"

View File

@ -221,12 +221,23 @@ Sequential Access and Cursors {#SimpleQueriesCursor}
Modification Queries {#SimpleQueriesModify}
===========================================
ArangoDB also allows removing documents based on an example document. Every
document in the collection will be compared against the specified example
document and be deleted if all attributes match.
ArangoDB also allows removing, replacing, and updating documents based
on an example document. Every document in the collection will be
compared against the specified example document and be deleted/replaced/
updated if all attributes match.
This method should be used with caution as it intended to remove lots of
documents from a collection.
These method should be used with caution as they are intended to remove or
modify lots of documents in a collection.
All methods can optionally be restricted to a specific number of operations.
However, if a limit is specific but is less than the number of matches, it
will be undefined which of the matching documents will get removed/modified.
@anchor SimpleQueryRemoveByExample
@copydetails JSF_ArangoCollection_prototype_removeByExample
@anchor SimpleQueryReplaceByExample
@copydetails JSF_ArangoCollection_prototype_replaceByExample
@anchor SimpleQueryUpdateByExample
@copydetails JSF_ArangoCollection_prototype_updateByExample

View File

@ -29,3 +29,5 @@ TOC {#SimpleQueriesTOC}
- @ref SimpleQueryCount "query.count"
- @ref SimpleQueriesModify
- @ref SimpleQueryRemoveByExample "collection.removeByExample"
- @ref SimpleQueryReplaceByExample "collection.replaceByExample"
- @ref SimpleQueryUpdateByExample "collection.updateByExample"

View File

@ -62,35 +62,30 @@ case $TRI_OS_LONG in
echo "Using configuration for Arch Linux"
OPTIONS="$OPTIONS --disable-mruby"
LDD_INFO="yes"
RESULTS="$RESULTS arangoirb"
;;
Linux-LinuxMint-13*)
echo "Using configuration for LinuxMint 13"
OPTIONS="$OPTIONS --disable-mruby"
LDD_INFO="yes"
RESULTS="$RESULTS arangoirb"
;;
Linux-openSUSE-12*)
echo "Using configuration for openSuSE 12.X"
OPTIONS="$OPTIONS --disable-mruby "
LDD_INFO="yes"
RESULTS="$RESULTS arangoirb"
;;
Linux-openSUSE-11*)
echo "Using configuration for openSuSE 11.X"
OPTIONS="$OPTIONS --disable-mruby"
LDD_INFO="yes"
RESULTS="$RESULTS arangoirb"
;;
Linux-Debian-6*)
echo "Using configuration for Debian"
OPTIONS="$OPTIONS --enable-all-in-one-libev --enable-all-in-one-v8 --disable-mruby"
LDD_INFO="yes"
RESULTS="$RESULTS arangoirb"
;;
Linux-Debian*)
@ -117,19 +112,17 @@ case $TRI_OS_LONG in
echo "Using configuration for Ubuntu"
OPTIONS="$OPTIONS --enable-all-in-one-libev --enable-all-in-one-v8 --disable-mruby"
LDD_INFO="yes"
RESULTS="$RESULTS arangoirb"
;;
Darwin*)
echo "Using configuration for DARWIN"
CPPFLAGS='-isystem /usr/include -isystem /opt/local/include -Wno-deprecated-declarations'
LDFLAGS='-L/usr/lib -L/opt/local/lib' # need to use OpenSSL from system
LDFLAGS='-L/usr/lib '
OPTIONS="$OPTIONS --enable-all-in-one-libev --enable-all-in-one-v8 --enable-all-in-one-icu --disable-mruby"
RESULTS="$RESULTS arangoirb"
if [ "${TRI_MACH}" == "x86_64" ]; then
X=$(uname -r)
OPTIONS="$OPTIONS --build x86_64-apple-darwin${X}"
fi
# we need 64 bits
TRI_MACH="x86_64"
X=$(uname -r)
OPTIONS="$OPTIONS --build x86_64-apple-darwin${X}"
;;
*)

View File

@ -310,13 +310,8 @@ export systemddir
echo
echo "########################################################"
echo "Call EPM to build the package."
if [ "$TRI_MACH" == "amd64" ] ; then
echo " sudo -E epm -a ${EPM_ARCH} -f ${package_type} ${product_name} ${sfolder_name}/${LIST}"
sudo -E epm -a ${TRI_MACH} -f ${package_type} ${product_name} ${sfolder_name}/${LIST} || exit 1
else
echo " sudo -E epm -f ${package_type} ${product_name} ${sfolder_name}/${LIST}"
sudo -E epm -f ${package_type} ${product_name} ${sfolder_name}/${LIST} || exit 1
fi
echo " sudo -E epm -a ${EPM_MACH} -f ${package_type} ${product_name} ${sfolder_name}/${LIST}"
sudo -E epm -a ${TRI_MACH} -f ${package_type} ${product_name} ${sfolder_name}/${LIST} || exit 1
echo "########################################################"
echo

View File

@ -405,7 +405,7 @@ describe ArangoDB do
@cid = ArangoDB.create_collection(@cn, false)
(0...20).each{|i|
ArangoDB.post("/_api/document?collection=#{@cn}", :body => "{ \"value\" : #{i} }")
ArangoDB.post("/_api/document?collection=#{@cn}", :body => "{ \"value\" : #{i}, \"value2\" : 99 }")
}
end
@ -418,15 +418,15 @@ describe ArangoDB do
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 1 } }"
doc = ArangoDB.log_put("#{prefix}-remove-by-example", cmd, :body => body)
# remove first
# remove first
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['deleted'].should eq(1)
# remove again
doc = ArangoDB.log_put("#{prefix}-remove-by-example", cmd, :body => body)
# remove again
doc = ArangoDB.log_put("#{prefix}-remove-by-example", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
@ -532,6 +532,238 @@ describe ArangoDB do
doc.parsed_response['code'].should eq(200)
doc.parsed_response['deleted'].should eq(0)
end
it "removes the examples, with limit" do
cmd = api + "/remove-by-example"
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"limit\" : 5 }"
doc = ArangoDB.log_put("#{prefix}-remove-by-example-limit", cmd, :body => body)
# remove some
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['deleted'].should eq(5)
# remove some more
doc = ArangoDB.log_put("#{prefix}-remove-by-example-limit", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['deleted'].should eq(5)
# remove the rest
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"limit\" : 50 }"
doc = ArangoDB.log_put("#{prefix}-remove-by-example-limit", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['deleted'].should eq(10)
end
end
################################################################################
## replace-by-example query
################################################################################
context "replace-by-example query:" do
before do
@cn = "UnitTestsCollectionByExample"
ArangoDB.drop_collection(@cn)
@cid = ArangoDB.create_collection(@cn, false)
(0...20).each{|i|
ArangoDB.post("/_api/document?collection=#{@cn}", :body => "{ \"value\" : #{i}, \"value2\" : 99 }")
}
end
after do
ArangoDB.drop_collection(@cn)
end
it "replaces the examples" do
cmd = api + "/replace-by-example"
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 1 }, \"newValue\" : { \"foo\" : \"bar\" } }"
doc = ArangoDB.log_put("#{prefix}-replace-by-example", cmd, :body => body)
# replace one
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['replaced'].should eq(1)
# replace other
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 2 }, \"newValue\" : { \"foo\" : \"baz\" } }"
doc = ArangoDB.log_put("#{prefix}-replace-by-example", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['replaced'].should eq(1)
# replace all others
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"moo\" : \"fox\" } }"
doc = ArangoDB.log_put("#{prefix}-replace-by-example", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['replaced'].should eq(18)
# remove non-existing values
[ 21, 22, 100, 101, 99, "\"meow\"", "\"\"", "\"null\"" ].each{|value|
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : " + value.to_s + " }, \"newValue\" : { } }"
doc = ArangoDB.log_put("#{prefix}-replace-by-example", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['replaced'].should eq(0)
}
end
it "replaces the examples, with limit" do
cmd = api + "/replace-by-example"
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"foo\" : \"bar\" }, \"limit\" : 5 }"
doc = ArangoDB.log_put("#{prefix}-replace-by-example-limit", cmd, :body => body)
# replace some
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['replaced'].should eq(5)
# replace some more
doc = ArangoDB.log_put("#{prefix}-replace-by-example-limit", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['replaced'].should eq(5)
# replace the rest
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"fox\" : \"box\" }, \"limit\" : 50 }"
doc = ArangoDB.log_put("#{prefix}-replace-by-example-limit", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['replaced'].should eq(10)
end
end
################################################################################
## update-by-example query
################################################################################
context "update-by-example query:" do
before do
@cn = "UnitTestsCollectionByExample"
ArangoDB.drop_collection(@cn)
@cid = ArangoDB.create_collection(@cn, false)
(0...20).each{|i|
ArangoDB.post("/_api/document?collection=#{@cn}", :body => "{ \"value\" : #{i}, \"value2\" : 99 }")
}
end
after do
ArangoDB.drop_collection(@cn)
end
it "updates the examples" do
cmd = api + "/update-by-example"
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 1 }, \"newValue\" : { \"foo\" : \"bar\" } }"
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
# update one
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['updated'].should eq(1)
# update other
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 2 }, \"newValue\" : { \"foo\" : \"baz\" } }"
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['updated'].should eq(1)
# update other, overwrite
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 3 }, \"newValue\" : { \"foo\" : \"baz\", \"value\" : 12 } }"
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['updated'].should eq(1)
# update other, remove
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 12 }, \"newValue\" : { \"value2\" : null }, \"keepNull\" : false }"
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['updated'].should eq(2)
# update all but the 2 from before
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"moo\" : \"fox\" } }"
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['updated'].should eq(18)
# update non-existing values
[ 100, 101, 99, "\"meow\"", "\"\"", "\"null\"" ].each{|value|
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : " + value.to_s + " }, \"newValue\" : { } }"
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['updated'].should eq(0)
}
end
it "updates the examples, with limit" do
cmd = api + "/update-by-example"
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"foo\" : \"bar\", \"value2\" : 17 }, \"limit\" : 5 }"
doc = ArangoDB.log_put("#{prefix}-update-by-example-limit", cmd, :body => body)
# update some
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['updated'].should eq(5)
# update some more
doc = ArangoDB.log_put("#{prefix}-update-by-example-limit", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['updated'].should eq(5)
# update the rest
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"fox\" : \"box\" }, \"limit\" : 50 }"
doc = ArangoDB.log_put("#{prefix}-replace-by-example-limit", cmd, :body => body)
doc.code.should eq(200)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['updated'].should eq(10)
end
end
################################################################################

View File

@ -0,0 +1,420 @@
# coding: utf-8
require 'rspec'
require './arangodb.rb'
describe ArangoDB do
api = "/_api/user"
prefix = "api-users"
context "user management:" do
before do
(0...10).each{|i|
ArangoDB.delete("/_api/user/users-" + i.to_s);
}
end
after do
(0...10).each{|i|
ArangoDB.delete("/_api/user/users-" + i.to_s);
}
end
################################################################################
## adding users
################################################################################
context "adding users" do
it "add user, no username" do
body = "{ \"passwd\" : \"fox\" }"
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1700)
end
it "add user, empty username" do
body = "{ \"username\" : \"\", \"passwd\" : \"fox\" }"
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1700)
end
it "add user, no passwd" do
body = "{ \"username\" : \"users-1\" }"
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
doc.code.should eq(201)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(201)
doc = ArangoDB.get(api + "/users-1")
doc.code.should eq(200)
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['user'].should eq("users-1")
doc.parsed_response['active'].should eq(true)
end
it "add user, username and passwd" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\" }"
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
doc.code.should eq(201)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(201)
doc = ArangoDB.get(api + "/users-1")
doc.code.should eq(200)
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['user'].should eq("users-1")
doc.parsed_response['active'].should eq(true)
end
it "add user, username passwd, active, extra" do
body = "{ \"username\" : \"users-2\", \"passwd\" : \"fox\", \"active\" : false, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
doc.code.should eq(201)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(201)
doc = ArangoDB.get(api + "/users-2")
doc.code.should eq(200)
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['user'].should eq("users-2")
doc.parsed_response['active'].should eq(false)
doc.parsed_response['extra'].should eq({ "foo" => true })
end
it "add user, duplicate username" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\" }"
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
doc.code.should eq(201)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(201)
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(1702)
end
end
################################################################################
## replacing users
################################################################################
context "replacing users" do
it "replace, no user" do
doc = ArangoDB.log_put("#{prefix}-replace", api)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
end
it "replace non-existing user" do
doc = ArangoDB.log_put("#{prefix}-replace", api + "/users-1")
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(404)
end
it "replace already removed user" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-replace", api, :body => body)
doc.code.should eq(201)
# remove
doc = ArangoDB.log_delete("#{prefix}-replace", api + "/users-1")
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(202)
# now replace
doc = ArangoDB.log_put("#{prefix}-replace", api + "/users-1")
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(404)
end
it "replace, empty body" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-replace", api, :body => body)
# replace
body = "{ }"
doc = ArangoDB.log_put("#{prefix}-replace", api + "/users-1", :body => body)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc = ArangoDB.get(api + "/users-1")
doc.code.should eq(200)
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['user'].should eq("users-1")
doc.parsed_response['active'].should eq(true)
end
it "replace existing user, no passwd" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-replace", api, :body => body)
# replace
body = "{ \"active\" : false, \"extra\" : { \"foo\" : false } }"
doc = ArangoDB.log_put("#{prefix}-replace", api + "/users-1", :body => body)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc = ArangoDB.get(api + "/users-1")
doc.code.should eq(200)
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['user'].should eq("users-1")
doc.parsed_response['active'].should eq(false)
doc.parsed_response['extra'].should eq({ "foo" => false })
end
it "replace existing user" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-replace", api, :body => body)
# replace
body = "{ \"passwd\" : \"fox2\", \"active\" : false, \"extra\" : { \"foo\" : false } }"
doc = ArangoDB.log_put("#{prefix}-replace", api + "/users-1", :body => body)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc = ArangoDB.get(api + "/users-1")
doc.code.should eq(200)
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['user'].should eq("users-1")
doc.parsed_response['active'].should eq(false)
doc.parsed_response['extra'].should eq({ "foo" => false })
end
end
################################################################################
## updating users
################################################################################
context "updating users" do
it "update, no user" do
doc = ArangoDB.log_patch("#{prefix}-update", api)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
end
it "update non-existing user" do
doc = ArangoDB.log_patch("#{prefix}-update", api + "/users-1")
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(404)
end
it "update already removed user" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-update", api, :body => body)
doc.code.should eq(201)
# remove
doc = ArangoDB.log_delete("#{prefix}-update", api + "/users-1")
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(202)
# now update
doc = ArangoDB.log_patch("#{prefix}-update", api + "/users-1")
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(404)
end
it "update, empty body" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-update", api, :body => body)
# update
body = "{ }"
doc = ArangoDB.log_patch("#{prefix}-update", api + "/users-1", :body => body)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc = ArangoDB.get(api + "/users-1")
doc.code.should eq(200)
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['user'].should eq("users-1")
doc.parsed_response['active'].should eq(true)
doc.parsed_response['extra'].should eq({ "foo" => true })
end
it "update existing user, no passwd" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-update", api, :body => body)
# update
body = "{ \"active\" : false, \"extra\" : { \"foo\" : false } }"
doc = ArangoDB.log_patch("#{prefix}-update", api + "/users-1", :body => body)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc = ArangoDB.get(api + "/users-1")
doc.code.should eq(200)
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['user'].should eq("users-1")
doc.parsed_response['active'].should eq(false)
doc.parsed_response['extra'].should eq({ "foo" => false })
end
it "update existing user" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-update", api, :body => body)
# update
body = "{ \"passwd\" : \"fox2\", \"active\" : false }"
doc = ArangoDB.log_patch("#{prefix}-update", api + "/users-1", :body => body)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc = ArangoDB.get(api + "/users-1")
doc.code.should eq(200)
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['user'].should eq("users-1")
doc.parsed_response['active'].should eq(false)
doc.parsed_response['extra'].should eq({ "foo" => true })
end
end
################################################################################
## removing users
################################################################################
context "removing users" do
it "remove, no user" do
doc = ArangoDB.log_delete("#{prefix}-remove", api)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
end
it "remove non-existing user" do
doc = ArangoDB.log_delete("#{prefix}-remove", api + "/users-1")
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(404)
end
it "remove already removed user" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-delete", api, :body => body)
doc.code.should eq(201)
# remove for the first time
doc = ArangoDB.log_delete("#{prefix}-remove", api + "/users-1")
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(202)
# remove again
doc = ArangoDB.log_delete("#{prefix}-remove", api + "/users-1")
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(404)
end
it "remove existing user" do
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-delete", api, :body => body)
# remove
doc = ArangoDB.log_delete("#{prefix}-remove", api + "/users-1")
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(202)
end
end
################################################################################
## fetching users
################################################################################
context "fetching users" do
it "no user specified" do
doc = ArangoDB.log_get("#{prefix}-fetch", api)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(400)
end
it "fetch non-existing user" do
doc = ArangoDB.log_get("#{prefix}-fetch", api + "/users-16")
doc.code.should eq(404)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(404)
doc.parsed_response['errorNum'].should eq(1703)
end
it "fetch user" do
body = "{ \"username\" : \"users-2\", \"passwd\" : \"fox\", \"active\" : false, \"extra\" : { \"foo\" : true } }"
doc = ArangoDB.log_post("#{prefix}-fetch", api, :body => body)
doc.code.should eq(201)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(201)
doc = ArangoDB.get(api + "/users-2")
doc.code.should eq(200)
doc.parsed_response['error'].should eq(false)
doc.parsed_response['code'].should eq(200)
doc.parsed_response['user'].should eq("users-2")
doc.parsed_response['active'].should eq(false)
doc.parsed_response['extra'].should eq({ "foo" => true })
doc.parsed_response.should_not have_key("passwd")
end
end
end
end

View File

@ -21,4 +21,5 @@ rspec --color --format d \
api-explain-spec.rb \
api-cursor-spec.rb \
api-statistics-spec.rb \
api-simple-spec.rb
api-simple-spec.rb \
api-users-spec.rb

View File

@ -214,6 +214,7 @@ SHELL_COMMON = @top_srcdir@/js/common/tests/shell-document.js \
@top_srcdir@/js/common/tests/shell-compactor.js \
@top_srcdir@/js/common/tests/shell-simple-query.js \
@top_srcdir@/js/common/tests/shell-statement.js \
@top_srcdir@/js/common/tests/shell-crypto.js \
@top_srcdir@/js/common/tests/shell-users.js \
@top_srcdir@/js/common/tests/shell-index.js \
@top_srcdir@/js/common/tests/shell-index-geo.js \

View File

@ -336,7 +336,7 @@ bool RestDocumentHandler::createDocument () {
////////////////////////////////////////////////////////////////////////////////
bool RestDocumentHandler::readDocument () {
size_t len = _request->suffix().size();
const size_t len = _request->suffix().size();
switch (len) {
case 0:
@ -403,10 +403,6 @@ bool RestDocumentHandler::readDocument () {
bool RestDocumentHandler::readSingleDocument (bool generateBody) {
vector<string> const& suffix = _request->suffix();
/// check for an etag
TRI_voc_rid_t ifNoneRid = extractRevision("if-none-match", 0);
TRI_voc_rid_t ifRid = extractRevision("if-match", "rev");
// split the document reference
const string& collection = suffix[0];
const string& key = suffix[1];
@ -451,7 +447,10 @@ bool RestDocumentHandler::readSingleDocument (bool generateBody) {
assert(document);
assert(document->_key);
TRI_voc_rid_t rid = document->_rid;
const TRI_voc_rid_t rid = document->_rid;
// check for an etag
const TRI_voc_rid_t ifNoneRid = extractRevision("if-none-match", 0);
const TRI_voc_rid_t ifRid = extractRevision("if-match", "rev");
if (ifNoneRid == 0) {
if (ifRid == 0 || ifRid == rid) {
@ -772,10 +771,11 @@ bool RestDocumentHandler::modifyDocument (bool isPatch) {
}
// extract the revision
TRI_voc_rid_t revision = extractRevision("if-match", "rev");
const TRI_voc_rid_t revision = extractRevision("if-match", "rev");
// extract or chose the update policy
TRI_doc_update_policy_e policy = extractUpdatePolicy();
const TRI_doc_update_policy_e policy = extractUpdatePolicy();
const bool waitForSync = extractWaitForSync();
TRI_doc_mptr_t* document = 0;
@ -839,13 +839,13 @@ bool RestDocumentHandler::modifyDocument (bool isPatch) {
if (holder.registerJson(TRI_UNKNOWN_MEM_ZONE, patchedJson)) {
// do not acquire an extra lock
res = trx.updateDocument(key, &document, patchedJson, policy, extractWaitForSync(), revision, &rid, false);
res = trx.updateDocument(key, &document, patchedJson, policy, waitForSync, revision, &rid, false);
}
}
}
else {
// replacing an existing document, using a lock
res = trx.updateDocument(key, &document, json, policy, extractWaitForSync(), revision, &rid, true);
res = trx.updateDocument(key, &document, json, policy, waitForSync, revision, &rid, true);
}
res = trx.finish(res);
@ -934,10 +934,11 @@ bool RestDocumentHandler::deleteDocument () {
const string& key = suffix[1];
// extract the revision
TRI_voc_rid_t revision = extractRevision("if-match", "rev");
const TRI_voc_rid_t revision = extractRevision("if-match", "rev");
// extract or choose the update policy
TRI_doc_update_policy_e policy = extractUpdatePolicy();
const TRI_doc_update_policy_e policy = extractUpdatePolicy();
const bool waitForSync = extractWaitForSync();
if (policy == TRI_DOC_UPDATE_ILLEGAL) {
generateError(HttpResponse::BAD,
@ -961,7 +962,7 @@ bool RestDocumentHandler::deleteDocument () {
const TRI_voc_cid_t cid = trx.cid();
TRI_voc_rid_t rid = 0;
res = trx.deleteDocument(key, policy, extractWaitForSync(), revision, &rid);
res = trx.deleteDocument(key, policy, waitForSync, revision, &rid);
if (res == TRI_ERROR_NO_ERROR) {
res = trx.commit();
}

View File

@ -207,9 +207,9 @@ void RestVocbaseBaseHandler::generate20x (const HttpResponse::HttpResponseCode r
if (responseCode != HttpResponse::OK) {
// 200 OK is sent is case of delete or update.
// in these cases we do not return etag nor location
_response->setHeader("ETag", "\"" + rev + "\"");
_response->setHeader("etag", 4, "\"" + rev + "\"");
// handle does not need to be RFC 2047-encoded
_response->setHeader("location", DOCUMENT_PATH + "/" + handle);
_response->setHeader("location", 8, DOCUMENT_PATH + "/" + handle);
}
// _id and _key are safe and do not need to be JSON-encoded
@ -297,7 +297,7 @@ void RestVocbaseBaseHandler::generateNotModified (const TRI_voc_rid_t rid) {
const string rev = StringUtils::itoa(rid);
_response = createResponse(HttpResponse::NOT_MODIFIED);
_response->setHeader("ETag", "\"" + rev + "\"");
_response->setHeader("etag", 4, "\"" + rev + "\"");
}
////////////////////////////////////////////////////////////////////////////////
@ -315,13 +315,10 @@ void RestVocbaseBaseHandler::generateDocument (const TRI_voc_cid_t cid,
return;
}
// add document identifier to buffer
TRI_string_buffer_t buffer;
string id = DocumentHelper::assembleDocumentId(_resolver.getCollectionName(cid), document->_key);
const string id = DocumentHelper::assembleDocumentId(_resolver.getCollectionName(cid), document->_key);
TRI_json_t augmented;
TRI_InitArrayJson(TRI_UNKNOWN_MEM_ZONE, &augmented);
TRI_Init2ArrayJson(TRI_UNKNOWN_MEM_ZONE, &augmented, 8);
TRI_json_t* _id = TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, id.c_str());
@ -330,7 +327,7 @@ void RestVocbaseBaseHandler::generateDocument (const TRI_voc_cid_t cid,
}
// convert rid from uint64_t to string
string rid = StringUtils::itoa(document->_rid);
const string rid = StringUtils::itoa(document->_rid);
TRI_json_t* _rev = TRI_CreateString2CopyJson(TRI_UNKNOWN_MEM_ZONE, rid.c_str(), rid.size());
if (_rev) {
@ -354,6 +351,9 @@ void RestVocbaseBaseHandler::generateDocument (const TRI_voc_cid_t cid,
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, &augmented, "_to", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, to.c_str()));
}
// add document identifier to buffer
TRI_string_buffer_t buffer;
// convert object to string
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
@ -378,7 +378,7 @@ void RestVocbaseBaseHandler::generateDocument (const TRI_voc_cid_t cid,
// and generate a response
_response = createResponse(HttpResponse::OK);
_response->setContentType("application/json; charset=utf-8");
_response->setHeader("ETag", "\"" + StringUtils::itoa(document->_rid) + "\"");
_response->setHeader("etag", 4, "\"" + rid + "\"");
if (generateBody) {
_response->body().appendText(TRI_BeginStringBuffer(&buffer), TRI_LengthStringBuffer(&buffer));
@ -447,7 +447,8 @@ void RestVocbaseBaseHandler::generateTransactionError (const string& collectionN
/// @brief extracts the revision
////////////////////////////////////////////////////////////////////////////////
TRI_voc_rid_t RestVocbaseBaseHandler::extractRevision (char const* header, char const* parameter) {
TRI_voc_rid_t RestVocbaseBaseHandler::extractRevision (char const* header,
char const* parameter) {
bool found;
char const* etag = _request->header(header, found);

View File

@ -292,7 +292,7 @@ namespace triagens {
/// @note @FA{header} must be lowercase.
////////////////////////////////////////////////////////////////////////////////
TRI_voc_rid_t extractRevision (char const* header, char const* parameter);
TRI_voc_rid_t extractRevision (char const*, char const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief extracts the update policy

View File

@ -486,7 +486,7 @@ void ArangoServer::buildApplicationServer () {
if (absoluteFile != 0) {
_pidFile = string(absoluteFile);
TRI_Free(TRI_CORE_MEM_ZONE, absoluteFile);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, absoluteFile);
LOGGER_DEBUG("using absolute pid file '" << _pidFile << "'");
}

View File

@ -101,10 +101,12 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
const TRI_vocbase_col_t* getCollectionStruct (const string& name) const {
map<string, const TRI_vocbase_col_t*>::iterator it = _resolvedNames.find(name);
if (it != _resolvedNames.end()) {
return (*it).second;
if (_resolvedNames.size() > 0) {
map<string, const TRI_vocbase_col_t*>::const_iterator it = _resolvedNames.find(name);
if (it != _resolvedNames.end()) {
return (*it).second;
}
}
const TRI_vocbase_col_t* collection = TRI_LookupCollectionByNameVocBase(_vocbase, name.c_str());
@ -120,11 +122,13 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
string getCollectionName (const TRI_voc_cid_t cid) const {
map<TRI_voc_cid_t, string>::iterator it = _resolvedIds.find(cid);
if (_resolvedIds.size() > 0) {
map<TRI_voc_cid_t, string>::const_iterator it = _resolvedIds.find(cid);
if (it != _resolvedIds.end()) {
return (*it).second;
}
if (it != _resolvedIds.end()) {
return (*it).second;
}
}
char* n = TRI_GetCollectionNameByIdVocBase(_vocbase, cid);
if (n == 0) {

View File

@ -120,7 +120,7 @@ namespace triagens {
/// @brief get the underlying collection's id
////////////////////////////////////////////////////////////////////////////////
inline TRI_voc_cid_t cid () {
inline TRI_voc_cid_t cid () const {
return this->_cid;
}

View File

@ -1,3 +1,13 @@
#newCollection {
margin-right: 22px;
margin-top: 11px;
opacity: 0.3;
}
#newCollection:hover {
opacity: 1;
}
.thumbnail, .navbar-inner, .btn, .btn-primary {
border-radius:0;
box-shadow: 0;
@ -34,9 +44,10 @@
height: 18px;
line-height: 18px;
margin-top: 3px;
margin-left: 20px;
margin-left: 18px;
padding: 2px;
vertical-align: middle;
width: 435px;
}
#searchSubmit {

View File

@ -124,13 +124,13 @@ table.dataTable tr.even td.sorting_3 { background-color: #F9F9FF; }
margin-left: 10px;
}
.paginate_enabled_previous { background: url('../images/back_enabled.png') no-repeat top left; }
.paginate_enabled_previous:hover { background: url('../images/back_enabled_hover.png') no-repeat top left; }
.paginate_disabled_previous { background: url('../images/back_disabled.png') no-repeat top left; }
.paginate_enabled_previous { background: url('../img/back_enabled.png') no-repeat top left; }
.paginate_enabled_previous:hover { background: url('../img/back_enabled_hover.png') no-repeat top left; }
.paginate_disabled_previous { background: url('../img/back_disabled.png') no-repeat top left; }
.paginate_enabled_next { background: url('../images/forward_enabled.png') no-repeat top right; }
.paginate_enabled_next:hover { background: url('../images/forward_enabled_hover.png') no-repeat top right; }
.paginate_disabled_next { background: url('../images/forward_disabled.png') no-repeat top right; }
.paginate_enabled_next { background: url('../img/forward_enabled.png') no-repeat top right; }
.paginate_enabled_next:hover { background: url('../img/forward_enabled_hover.png') no-repeat top right; }
.paginate_disabled_next { background: url('../img/forward_disabled.png') no-repeat top right; }
/* Full number pagination */
.paging_full_numbers {
@ -194,12 +194,12 @@ table.dataTable tr.even td.sorting_3 { background-color: #F9F9FF; }
/*
* Sorting
*/
.sorting { background: url('../images/sort_both.png') no-repeat center right; }
.sorting_asc { background: url('../images/sort_asc.png') no-repeat center right; }
.sorting_desc { background: url('../images/sort_desc.png') no-repeat center right; }
.sorting { background: url('../img/sort_both.png') no-repeat center right; }
.sorting_asc { background: url('../img/sort_asc.png') no-repeat center right; }
.sorting_desc { background: url('../img/sort_desc.png') no-repeat center right; }
.sorting_asc_disabled { background: url('../images/sort_asc_disabled.png') no-repeat center right; }
.sorting_desc_disabled { background: url('../images/sort_desc_disabled.png') no-repeat center right; }
.sorting_asc_disabled { background: url('../img/sort_asc_disabled.png') no-repeat center right; }
.sorting_desc_disabled { background: url('../img/sort_desc_disabled.png') no-repeat center right; }
table.dataTable thead th:active,
table.dataTable thead td:active {

View File

@ -27,14 +27,6 @@
#logNav {
padding-bottom: 0;
margin-bottom: 0;
background-color: rgba(0, 0, 0, 0.00);
}
#logToolbar {
}
.tab-content {
background-color: rgba(0, 0, 0, 0.05);
}
.odd {

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
frontend/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
frontend/img/sort_asc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
frontend/img/sort_both.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
frontend/img/sort_desc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -35,7 +35,6 @@ $(document).ready(function() {
window.arangoLogsStore.fetch({
success: function () {
if (!window.logsView) {
console.log("not existing");
}
window.logsView = new window.logsView({
collection: window.arangoLogsStore
@ -127,7 +126,6 @@ $(document).ready(function() {
window.arangoLogsStore.fetch({
success: function () {
if (!window.logsView) {
console.log("not exits");
}
window.logsView.render();
$('#logNav a[href="#all"]').tab('show');

View File

@ -4,7 +4,7 @@
<input type="text" id="searchInput" class="searchInput" placeholder="Search..."><img id="searchSubmit" width="16" height="16" src="/_admin/html/img/enter_icon.png">
<a href="#new">
<img id="plusIcon" src="/_admin/html/img/plus_icon.png"class="pull-right"></img>
<img id="newCollection" src="/_admin/html/img/plus_icon.png"class="pull-right"></img>
</a>
</div>
</div>

View File

@ -8,9 +8,9 @@
<table cellpadding="0" cellspacing="0" border="0" class="display" id="documentsTableID">
<thead>
<tr>
<th style="width:10px"></th>
<th style="width: 100px">_id</th>
<th style="width: 490px">Content</th>
<th style="width:10px"></th>
</tr>
</thead>
</table>

View File

@ -1,8 +1,10 @@
<div id="add-collection" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display:none">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>New Collection</h3>
</div>
<div class="modal-body">
<table>
<tr>
@ -10,31 +12,52 @@
<th><input type="text" id="new-collection-name" name="name" value=""/></th>
</tr>
<tr>
<th class="collectionTh">Size:</th>
<th><input type="text" id="new-collection-size" name="size" value=""/></th>
</tr>
<tr>
<th class="collectionTh">Sync:</th>
<th class="collectionTh">Type:</th>
<th>
<select id="new-collection-sync">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
</th>
</tr>
<tr>
<th class="collectionTh">Type:</th>
<th>
<select id="new-collection-type">
<option value="3">Document</option>
<option value="2">Edge</option>
</select>
</th>
</tr>
</table>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Abort</button>
<button id="save-new-collection" class="btn btn-primary">Create Collection</button>
</div>
<select id="new-collection-type">
<option value="2">Document</option>
<option value="3">Edge</option>
</select>
</th>
</tr>
</table>
<div class="accordion" id="accordion2">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapseOne">
Advanced
</a>
</div>
<div id="collapseOne" class="accordion-body collapse out">
<div class="accordion-inner">
<table>
<tr>
<th class="collectionTh">Size:</th>
<th><input type="text" id="new-collection-size" name="size" value=""/></th>
</tr>
<tr>
<th class="collectionTh">Sync:</th>
<th>
<select id="new-collection-sync">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
</th>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Abort</button>
<button id="save-new-collection" class="btn btn-primary">Create Collection</button>
</div>
</div>

View File

@ -72,7 +72,6 @@ var collectionView = Backbone.View.extend({
else if (status === 'unloaded') {
var newname = $('#change-collection-name').val();
if (this.myCollection.name !== newname) {
console.log("different name");
window.arangoCollectionsStore.renameCollection(collid, newname );
this.hideModal();
}

View File

@ -11,8 +11,19 @@ var documentSourceView = Backbone.View.extend({
render: function() {
$(this.el).html(this.template.text);
this.breadcrumb();
return this;
},
breadcrumb: function () {
var name = window.location.hash.split("/");
$('#transparentHeader').append(
'<a href="#" class="activeBread">Collections</a>'+
' > '+
'<a class="activeBread" href="#collection/'+name[1]+'/documents/1">'+name[1]+'</a>'+
' > '+
'<a class="disabledBread">'+name[2]+'</a>'
);
},
saveSourceDoc: function() {
window.arangoDocumentStore.saveDocument("source");
},

View File

@ -64,7 +64,7 @@ var documentView = Backbone.View.extend({
initTable: function () {
var documentTable = $(this.table).dataTable({
"aaSorting": [[ 1, "desc" ]],
"aaSorting": [[ 1, "asc" ]],
"bAutoWidth": false,
"bFilter": false,
"bPaginate":false,

View File

@ -38,7 +38,7 @@ var documentsView = Backbone.View.extend({
remove: function (a) {
this.target = a.currentTarget;
var thiselement = a.currentTarget.parentElement;
this.idelement = $(thiselement).next().text();
this.idelement = $(thiselement).prev().prev();
this.alreadyClicked = true;
$('#docDeleteModal').modal('show');
@ -49,11 +49,12 @@ var documentsView = Backbone.View.extend({
},
reallyDelete: function () {
var self = this;
var todelete = $(self.idelement).text();
try {
$.ajax({
type: 'DELETE',
contentType: "application/json",
url: "/_api/document/" + self.idelement,
url: "/_api/document/" + todelete,
success: function () {
var row = $(self.target).closest("tr").get(0);
$('#documentsTableID').dataTable().fnDeleteRow($('#documentsTableID').dataTable().fnGetPosition(row));
@ -80,7 +81,7 @@ var documentsView = Backbone.View.extend({
var self = a.currentTarget;
var aPos = $(this.table).dataTable().fnGetPosition(self);
var rowContent = $(this.table).dataTable().fnGetData(aPos);
window.location.hash = "#collection/" + rowContent[1];
window.location.hash = "#collection/" + rowContent[0];
},
initTable: function (colid, pageid) {
@ -111,11 +112,11 @@ var documentsView = Backbone.View.extend({
var self = this;
$.each(window.arangoDocumentsStore.models, function(key, value) {
$(self.table).dataTable().fnAddData([
'<button class="enabled" id="deleteDoc"><img src="/_admin/html/img/doc_delete_icon16.png" width="16" height="16"></button>',
value.attributes.id,
//value.attributes.key,
//value.attributes.rev,
'<pre class=prettify>' + self.cutByResolution(JSON.stringify(value.attributes.content)) + '</pre>'
'<pre class=prettify>' + self.cutByResolution(JSON.stringify(value.attributes.content)) + '</pre>',
'<button class="enabled" id="deleteDoc"><img src="/_admin/html/img/doc_delete_icon16.png" width="16" height="16"></button>'
]);
});
$(".prettify").snippet("javascript", {style: "nedit", menu: false, startText: false, transparent: true, showNum: false});

View File

@ -49,6 +49,7 @@ var newCollectionView = Backbone.View.extend({
return 0;
}
$.ajax({
type: "POST",
url: "/_api/collection",

View File

@ -18,7 +18,6 @@ var queryView = Backbone.View.extend({
return this;
},
submitQuery: function() {
console.log("submit");
var self = this;
var data = {query:$('#queryInput').val()};
var formattedJSON;

View File

@ -66,7 +66,6 @@ var shellView = Backbone.View.extend({
var client = "arangosh> " + escapeHTML(data) + "<br>";
$(varContent).append('<b class="shellClient">' + client + '</b>');
this.evaloutput(command);
console.log(command);
$(varContent).animate({scrollTop:$(varContent)[0].scrollHeight}, 1);
$(varInput).val('');
return false;

View File

@ -1,48 +0,0 @@
<div style="background-color: rgba(0, 0, 0, 0.15); border-radius: 0;">
<div class="row">
<div class="span12">
<img class="about-icon" src="img/download.png" width="50">
<h3>Download the source code</h3>
<p>The source code for this application is available in this repository on GitHub.</p>
</div>
</div>
<br/>
<div class="row">
<div class="span12">
<img class="about-icon" src="img/discuss.png" width="50" style="float: left;">
<h3>Comments and questions</h3>
<p>We love to hear your feedback. Post your questions and comments on the blog post associated with this
application.</p>
</div>
</div>
<br/>
<div class="row">
<div class="span12">
<img class="about-icon" src="img/twitter.png" width="50" style="float: left;">
<h3>Follow us on Twitter</h3>
<p><a href="http://twitter.com/arangodb">@arangodb</a></p>
</div>
</div>
<br/>
<div class="row">
<div class="span12">
<img class="about-icon" src="img/blog.png" width="50" style="float: left;">
<h3>Check out our blog</h3>
<p><a href="http://arangodb.org">http://arangodb.org</a></p>
</div>
</div>
</div>

View File

@ -1,10 +0,0 @@
<a href="#collections,<%= name %>" class="thumbnail plain">
<img src="pics/<%= picture %>" height="48" width="48" alt="" style="float:left; padding-right:20px; padding-left: 5px; padding-top: 5px; ">
<h5 class="collectionName"><%= name %></h5>
<p><br/>
<%= status %>
</p></a>
<a role="button" data-toggle="modal" class="thumbnail plain pull-right" style="width:14px; height:14px;">
<i class="icon-info-sign" id="<%= name %>"></i>
</a>

View File

@ -1,108 +0,0 @@
<ul class="thumbnails2">
<div id="transparentHeader">
<div id="transparentPlaceholder">
<input type="text" class="searchInput"><img id="searchSubmit" width="16" height="16" src="/_admin/html/pics/enter_icon.png">
<a href="#add-collection" role="button" data-toggle="modal">
<img id="plusIcon" src="/_admin/html/pics/plus_icon.png"class="pull-right"></img></a>
</div>
</div>
<br/>
</ul><ul class="thumbnails">
</ul>
<div id="add-collection" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display:none">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>New Collection</h3>
</div>
<div class="modal-body">
<table>
<tr>
<th class="collectionTh">Name:</th>
<th><input type="text" id="new-collection-name" name="name" value=""/></th>
</tr>
<tr>
<th class="collectionTh">Size:</th>
<th><input type="text" id="new-collection-size" name="size" value=""/></th>
</tr>
<tr>
<th class="collectionTh">Sync:</th>
<th>
<select id="new-collection-sync">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
</th>
</tr>
<tr>
<th class="collectionTh">Type:</th>
<th>
<select id="new-collection-type">
<option value="3">Document</option>
<option value="2">Edge</option>
</select>
</th>
</tr>
</table>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Abort</button>
<button id="save-new-collection" class="btn btn-primary">Create Collection</button>
</div>
</div>
<div id="change-collection" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display:none">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>Modify Collection</h3>
</div>
<div class="modal-body">
<table>
<tr>
<th class="collectionTh">Name:</th>
<th><input type="text" id="change-collection-name" name="name" value=""/></th>
</tr>
<tr id="collectionSizeBox" style="display:none">
<th class="collectionTh">Size:</th>
<th><input type="text" id="change-collection-size" name="size" value=""/></th>
</tr>
<tr id="collectionSyncBox" style="display:none">
<th class="collectionTh">Sync:</th>
<th>
<select id="change-collection-sync">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
</th>
</tr>
<tr>
<th class="collectionTh">ID:</th>
<th>
<input id="change-collection-id" name="id" type="text" class="span1" disabled/>
<th>
</tr>
<tr>
<th class="collectionTh">Type:</th>
<th>
<input type="text" id="change-collection-type" name="size" value="" disabled/>
</th>
</tr>
<tr>
<th class="collectionTh">Status:</th>
<th>
<input id="change-collection-status" name="id" type="text" class="span1" disabled/>
</th>
</tr>
</table>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Abort</button>
<button id="save-modified-collection" class="btn btn-primary">Modify Collection</button>
</div>
</div>

View File

@ -1,99 +0,0 @@
<div class="row blackbackground collectionDiv">
<form class="form-horizontal span12 collectionDiv2">
<fieldset class="">
<legend> Collection Details</legend>
<br/>
<div class="row">
<div class="span9">
<div class="control-group">
<label for="name" class="control-label">Name:</label>
<div class="controls">
<input type="text" id="collectionName" name="name" value="<%= name %>"/>
<span class="help-inline"></span>
</div>
</div>
<div class="control-group" id="collectionSizeBox" style="display:none">
<label for="size" class="control-label">Size:</label>
<div class="controls">
<input type="text" id="update-collection-size" name="size" value=""/>
<span class="help-inline"></span>
</div>
</div>
<div class="control-group" id="collectionSyncBox" style="display:none">
<label for="sync" class="control-label">Sync:</label>
<div class="controls">
<select id="update-collection-sync">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
</div>
</div>
<div class="control-group">
<label for="id" class="control-label">Id:</label>
<div class="controls">
<input id="collectionId" name="id" type="text" value="<%= id === null ? '' : id %>" class="span1"
disabled/>
</div>
</div>
<div class="control-group">
<label for="status" class="control-label">Status:</label>
<div class="controls">
<input id="status" name="status" type="text" value="<%= status === null ? '' : status %>" class="span1"
disabled/>
</div>
</div>
<div class="control-group">
<label for="type" class="control-label">Type:</label>
<div class="controls">
<input id="type" name="type" type="text" value="<%= type === null ? '' : type %>" class="span1"
disabled/>
</div>
</div>
</div>
<div class="span3">
<div class="well" id="collectionBox">
<img id="picture" width="48" src="<%= picture === null ? 'pics/generic.jpg' : 'pics/' + picture %>"/>
<a href="#" class="btn load">Load</a>
</div>
</div>
</div>
</fieldset>
<div class="form-actions">
<a href="#" class="btn btn-primary save pull-right collectionViewBtn">Save</a>
<a href="#" class="btn delete btn-delete pull-right">Delete</a>
<a href="#" class="btn">Cancel</a>
</div>
</form>
</div>
<div class="row status-bar">
<div class="span12">
<div class="alert alert-success" style="display: none">
<b>Success!</b> Collection saved successfully
</div>
</div>
</div>

View File

@ -1,35 +0,0 @@
<button class="minimal enabled" id="addDocumentButton">
<img src="/_admin/html/pics/doc_plus_icon16.png" width="16" height="16">
<a>Add Document</a>
</button>
<table cellpadding="0" cellspacing="0" border="0" class="display" id="documentsTableID" width="100%" style="table-layout:fixed">
<thead>
<tr>
<th></th>
<th>_id</th>
<th>_key</th>
<th>_rev</th>
<th>Content</th>
</tr>
</thead>
</table>
<div id="documentsToolbar">
<div class="pull-left">
</div>
<div class="pull-right">
<form id="docPageForm">
Jump to: <input type="text" id="docPageInput" placeholder="Page"></input>
<button id="submitDocPageInput">Ok</button>
</form>
<button class="enabled" id="documents_first"><img src="/_admin/html/pics/rnd_br_last_icon16.png"></button>
<button class="enabled" id="documents_prev"><img src="/_admin/html/pics/rnd_br_next_icon16.png"></button>
<button class="enabled" id="documents_next"><img src="/_admin/html/pics/rnd_br_prev_icon16.png"></button>
<button class="enabled" id="documents_last"><img src="/_admin/html/pics/rnd_br_first_icon16.png"></button>
</div>
</div>

View File

@ -1,41 +0,0 @@
<div class="navbar navbar-fixed-top" xmlns="http://www.w3.org/1999/html">
<div class="navbar-inner">
<div class="container">
<a class="brand navlogo" href="#"><img src="pics/logo_arangob_white.gif"/></a>
<div class="nav-collapse">
<ul class="nav">
</ul>
<ul class="nav pull-right">
<li class="dashboard-menu"><a href="#dashboard">Dashboard</a></li>
<li class="collections-menu"><a href="#">Collections</a></li>
<!-- <li class="statistics-menu"><a href="#statistics">Statistics</a></li> -->
<li class="query-menu"><a href="#query">Query</a></li>
<li class="shell-menu"><a href="#shell">Shell</a></li>
<li class="logs-menu"><a href="#logs">Logs</a></li>
<li class="about-menu"><a href="#about">About</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Links <b class="caret"></b></a>
<ul class="dropdown-menu">
<li class="nav-header">Documentation</li>
<li><a href="http://www.arangodb.org/manuals/current/UserManualArangosh.html" target="_blank">Shell Documentation</a></li>
<li><a href="http://www.arangodb.org/manuals/current/Aql.html" target="_blank">Query Documentation</a></li>
<li><a href="http://www.arangodb.org/manuals/current/" target="_blank">General Documentation</a></li>
<li class="divider"></li>
<li class="nav-header">This App</li>
<li><a href="https://github.com/triAGENS/ArangoDB" target="_blank">GitHub Repository</a></li>
<li><a href="http://www.arangodb.org" target="_blank">ArangoDB.org</a></li>
<li class="divider"></li>
<li class="nav-header">Social</li>
<li><a href="https://twitter.com/arangodb" target="_blank">Twitter</a></li>
<li><a href="http://www.arangodb.org/connect" target="_blank">Announcement List</a></li>
<li><a href="https://groups.google.com/group/arangodb" target="_blank">Google Groups</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>

View File

@ -1,110 +0,0 @@
<div id="logTabs">
<ul>
<li><a class="hoverClass" id="logAllTab" href="#All">All</a><li>
<li><a class="hoverClass" id="logCritTab" href="#Critical">Critical</a><li>
<li><a class="hoverClass" id="logWarnTab" href="#Warning">Warning</a><li>
<li><a class="hoverClass" id="logInfoTab" href="#Info">Info</a><li>
<li><a class="hoverClass" id="logDebuTab" href="#Debug">Debug</a><li>
</ul>
<div id="All">
<table cellpadding="0" cellspacing="0" border="0" class="display" id="logTableID" width="100%">
<thead>
<tr>
<th>Loglevel</th>
<th>Info</th>
</tr>
</thead>
</table>
<div id="logToolbar">
<div id="logToolbarAll">
<button class="enabled" id="logTableID_first"><img src="/_admin/html/media/icons/rnd_br_first_icon16.png"></button>
<button class="enabled" id="logTableID_prev"><img src="/_admin/html/media/icons/rnd_br_prev_icon16.png"></button>
<button class="enabled" id="logTableID_next"><img src="/_admin/html/media/icons/rnd_br_next_icon16.png"></button>
<button class="enabled" id="logTableID_last"><img src="/_admin/html/media/icons/rnd_br_last_icon16.png"></button>
</div>
<a id="logTableID_status">Showing</a>
</div>
</div>
<div id="Critical">
<table cellpadding="0" cellspacing="0" border="0" class="display" id="critLogTableID" width="100%">
<thead>
<tr>
<th>Loglevel</th>
<th>Info</th>
</tr>
</thead>
</table>
<div id="logToolbar">
<div id="logToolbarCrit">
<button class="enabled" id="critLogTableID_first"><img src="/_admin/html/media/icons/rnd_br_first_icon16.png"></button>
<button class="enabled" id="critLogTableID_prev"><img src="/_admin/html/media/icons/rnd_br_prev_icon16.png"></button>
<button class="enabled" id="critLogTableID_next"><img src="/_admin/html/media/icons/rnd_br_next_icon16.png"></button>
<button class="enabled" id="critLogTableID_last"><img src="/_admin/html/media/icons/rnd_br_last_icon16.png"></button>
<a id="critLogTableID_status">Showing</a>
</div>
</div>
</div>
<div id="Warning">
<table cellpadding="0" cellspacing="0" border="0" class="display" id="warnLogTableID" width="100%">
<thead>
<tr>
<th>Loglevel</th>
<th>Info</th>
</tr>
</thead>
</table>
<div id="logToolbar">
<div id="logToolbarWarn">
<button class="enabled" id="warnLogTableID_first"><img src="/_admin/html/media/icons/rnd_br_first_icon16.png"></button>
<button class="enabled" id="warnLogTableID_prev"><img src="/_admin/html/media/icons/rnd_br_prev_icon16.png"></button>
<button class="enabled" id="warnLogTableID_next"><img src="/_admin/html/media/icons/rnd_br_next_icon16.png"></button>
<button class="enabled" id="warnLogTableID_last"><img src="/_admin/html/media/icons/rnd_br_last_icon16.png"></button>
</div>
<a id="warnLogTableID_status">Showing</a>
</div>
</div>
<div id="Info">
<table cellpadding="0" cellspacing="0" border="0" class="display" id="infoLogTableID" width="100%">
<thead>
<tr>
<th>Loglevel</th>
<th>Info</th>
</tr>
</thead>
</table>
<div id="logToolbar">
<div id="logToolbarInfo">
<button class="enabled" id="infoLogTableID_first"><img src="/_admin/html/media/icons/rnd_br_first_icon16.png"></button>
<button class="enabled" id="infoLogTableID_prev"><img src="/_admin/html/media/icons/rnd_br_prev_icon16.png"></button>
<button class="enabled" id="infoLogTableID_next"><img src="/_admin/html/media/icons/rnd_br_next_icon16.png"></button>
<button class="enabled" id="infoLogTableID_last"><img src="/_admin/html/media/icons/rnd_br_last_icon16.png"></button>
</div>
<a id="infoLogTableID_status">Showing</a>
</div>
</div>
<div id="Debug">
<table cellpadding="0" cellspacing="0" border="0" class="display" id="debugLogTableID" width="100%">
<thead>
<tr>
<th>Loglevel</th>
<th>Info</th>
</tr>
</thead>
</table>
<div id="logToolbar">
<div id="logToolbarDebu">
<button class="enabled" id="debugLogTableID_first"><img src="/_admin/html/media/icons/rnd_br_first_icon16.png"></button>
<button class="enabled" id="debugLogTableID_prev"><img src="/_admin/html/media/icons/rnd_br_prev_icon16.png"></button>
<button class="enabled" id="debugLogTableID_next"><img src="/_admin/html/media/icons/rnd_br_next_icon16.png"></button>
<button class="enabled" id="debugLogTableID_last"><img src="/_admin/html/media/icons/rnd_br_last_icon16.png"></button>
</div>
<a id="debugLogTableID_status">Showing</a>
</div>
</div>
</div>
</div>

View File

@ -1,9 +0,0 @@
<!-- <div class="querynavbar navbar-inner navbar-inner-custom"><h3>Arango Shell</h3></div> -->
<div id="queryDiv" class="form-actions">
<div id="queryOutput" class="thumbnail"></div>
<form method="post" onsubmit="submitQuery()">
<i class="icon-trash clearicon"></i>
<textarea placeholder="Type in your query..." class="editBox" id="queryInput"></textarea>
</form>
<button id="submitQueryButton" class="btn btn-primary">Submit</button>
</div>

View File

@ -1,11 +0,0 @@
<!-- <div class="shellnavbar navbar-inner navbar-inner-custom"><h3>Arango Shell</h3></div> -->
<div id="shellFooter" class="form-actions">
<i class="icon-trash clearicon shelltrashicon"></i>
<i class="icon-refresh reloadicon"></i>
<div id="shellContent" class="thumbnail"></div>
<form id="shellForm" onsubmit="submitShell(); return false;">
<input id="shellInput" type="text" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true">
</form>
<img id="submitShellButton"src="/_admin/html/pics/enter_icon.png" width="16" height="16">
</div>

View File

@ -673,6 +673,13 @@ ArangoCollection.prototype.iterate = function (iterator, options) {
/// synchronisation for collections that have a default @LIT{waitForSync} value
/// of @LIT{true}.
///
/// @FUN{@FA{collection}.removeByExample(@FA{document}, @FA{waitForSync}, @FA{limit})}
///
/// The optional @FA{limit} parameter can be used to restrict the number of
/// removals to the specified value. If @FA{limit} is specified but less than the
/// number of documents in the collection, it is undefined which documents are
/// removed.
///
/// @EXAMPLES
///
/// @code
@ -680,10 +687,99 @@ ArangoCollection.prototype.iterate = function (iterator, options) {
/// @endcode
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
ArangoCollection.prototype.removeByExample = function (example, waitForSync, limit) {
throw "cannot call abstract removeByExample function";
};
////////////////////////////////////////////////////////////////////////////////
/// @brief replaces documents matching an example
///
/// @FUN{@FA{collection}.replaceByExample(@FA{example}, @FA{newValue})}
///
/// Replaces all documents matching an example with a new document body.
/// The entire document body of each document matching the @FA{example} will be
/// replaced with @FA{newValue}. The document meta-attributes such as @LIT{_id},
/// @LIT{_key}, @LIT{_from}, @LIT{_to} will not be replaced.
///
/// @FUN{@FA{collection}.replaceByExample(@FA{document}, @FA{newValue}, @FA{waitForSync})}
///
/// The optional @FA{waitForSync} parameter can be used to force synchronisation
/// of the document replacement operation to disk even in case that the
/// @LIT{waitForSync} flag had been disabled for the entire collection. Thus,
/// the @FA{waitForSync} parameter can be used to force synchronisation of just
/// specific operations. To use this, set the @FA{waitForSync} parameter to
/// @LIT{true}. If the @FA{waitForSync} parameter is not specified or set to
/// @LIT{false}, then the collection's default @LIT{waitForSync} behavior is
/// applied. The @FA{waitForSync} parameter cannot be used to disable
/// synchronisation for collections that have a default @LIT{waitForSync} value
/// of @LIT{true}.
///
/// @FUN{@FA{collection}.replaceByExample(@FA{document}, @FA{newValue}, @FA{waitForSync}, @FA{limit})}
///
/// The optional @FA{limit} parameter can be used to restrict the number of
/// replacements to the specified value. If @FA{limit} is specified but less than
/// the number of documents in the collection, it is undefined which documents are
/// replaced.
///
/// @EXAMPLES
///
/// @code
/// arangod> db.content.replaceByExample({ "domain": "de.celler" }, { "foo": "someValue }, false, 5)
/// @endcode
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.replaceByExample = function (example, newValue, waitForSync, limit) {
throw "cannot call abstract replaceByExample function";
};
////////////////////////////////////////////////////////////////////////////////
/// @brief partially updates documents matching an example
///
/// @FUN{@FA{collection}.updateByExample(@FA{example}, @FA{newValue})}
///
/// Partially updates all documents matching an example with a new document body.
/// Specific attributes in the document body of each document matching the
/// @FA{example} will be updated with the values from @FA{newValue}.
/// The document meta-attributes such as @LIT{_id}, @LIT{_key}, @LIT{_from},
/// @LIT{_to} cannot be updated.
///
/// @FUN{@FA{collection}.updateByExample(@FA{document}, @FA{newValue}, @FA{keepNull}, @FA{waitForSync})}
///
/// The optional @FA{keepNull} parameter can be used to modify the behavior when
/// handling @LIT{null} values. Normally, @LIT{null} values are stored in the
/// database. By setting the @FA{keepNull} parameter to @LIT{false}, this behavior
/// can be changed so that all attributes in @FA{data} with @LIT{null} values will
/// be removed from the target document.
///
/// The optional @FA{waitForSync} parameter can be used to force synchronisation
/// of the document replacement operation to disk even in case that the
/// @LIT{waitForSync} flag had been disabled for the entire collection. Thus,
/// the @FA{waitForSync} parameter can be used to force synchronisation of just
/// specific operations. To use this, set the @FA{waitForSync} parameter to
/// @LIT{true}. If the @FA{waitForSync} parameter is not specified or set to
/// @LIT{false}, then the collection's default @LIT{waitForSync} behavior is
/// applied. The @FA{waitForSync} parameter cannot be used to disable
/// synchronisation for collections that have a default @LIT{waitForSync} value
/// of @LIT{true}.
///
/// @FUN{@FA{collection}.updateByExample(@FA{document}, @FA{newValue}, @FA{keepNull}, @FA{waitForSync}, @FA{limit})}
///
/// The optional @FA{limit} parameter can be used to restrict the number of
/// updates to the specified value. If @FA{limit} is specified but less than
/// the number of documents in the collection, it is undefined which documents are
/// updated.
///
/// @EXAMPLES
///
/// @code
/// arangod> db.content.updateByExample({ "domain": "de.celler" }, { "foo": "someValue, "domain": null }, false)
/// @endcode
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.updateByExample = function (example, newValue, keepNull, waitForSync, limit) {
throw "cannot call abstract updateExample function";
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -1117,11 +1117,14 @@ ArangoCollection.prototype.outEdges = function (vertex) {
/// @brief removes documents matching an example
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
ArangoCollection.prototype.removeByExample = function (example,
waitForSync,
limit) {
var data = {
collection: this._name,
example: example,
waitForSync: waitForSync
waitForSync: waitForSync,
limit: limit
};
var requestResult = this._database._connection.PUT(
@ -1133,6 +1136,58 @@ ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
return requestResult.deleted;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief replaces documents matching an example
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.replaceByExample = function (example,
newValue,
waitForSync,
limit) {
var data = {
collection: this._name,
example: example,
newValue: newValue,
waitForSync: waitForSync,
limit: limit
};
var requestResult = this._database._connection.PUT(
"/_api/simple/replace-by-example",
JSON.stringify(data));
arangosh.checkRequestResult(requestResult);
return requestResult.replaced;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief updates documents matching an example
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.updateByExample = function (example,
newValue,
keepNull,
waitForSync,
limit) {
var data = {
collection: this._name,
example: example,
newValue: newValue,
keepNull: keepNull,
waitForSync: waitForSync,
limit: limit
};
var requestResult = this._database._connection.PUT(
"/_api/simple/update-by-example",
JSON.stringify(data));
arangosh.checkRequestResult(requestResult);
return requestResult.updated;
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -831,6 +831,11 @@ actions.defineHttp({
/// instantly be synchronised to disk. If this is not specified, then the
/// collection's default sync behavior will be applied.
///
/// - @LIT{limit}: an optional value that determines how many documents to
/// delete at most. If @LIT{limit} is specified but is less than the number
/// of documents in the collection, it is undefined which of the documents
/// will be deleted.
///
/// Returns the number of documents that were deleted
///
/// @EXAMPLES
@ -857,6 +862,7 @@ actions.defineHttp({
var example = body.example;
var name = body.collection;
var collection = db._collection(name);
var limit = body.limit || null;
if (collection === null) {
actions.collectionNotFound(req, res, name);
@ -865,7 +871,7 @@ actions.defineHttp({
actions.badParameter(req, res, "example");
}
else {
var result = collection.removeByExample(example, body.waitForSync);
var result = collection.removeByExample(example, body.waitForSync, limit);
actions.resultOk(req, res, actions.HTTP_OK, { deleted: result });
}
}
@ -876,6 +882,179 @@ actions.defineHttp({
}
});
////////////////////////////////////////////////////////////////////////////////
/// @fn JSA_PUT_api_simple_replace_by_example
/// @brief replaces the body of all documents of a collection that match an
/// example
///
/// @RESTHEADER{PUT /_api/simple/replace-by-example,replaces documents by example}
///
/// @REST{PUT /_api/simple/replace-by-example}
///
/// This will find all documents in the collection that match the specified
/// example object, and replace the entire document body with the new value
/// specified. Note that document meta-attributes such as @LIT{_id}, @LIT{_key},
/// @LIT{_from}, @LIT{_to} etc. cannot be replaced.
///
/// The call expects a JSON hash array as body with the following attributes:
///
/// - @LIT{collection}: The name of the collection to replace within.
///
/// - @LIT{example}: An example object that all collection objects are compared
/// against.
///
/// - @LIT{newValue}: The replacement document that will get inserted in place
/// of the "old" documents.
///
/// - @LIT{waitForSync}: if set to true, then all removal operations will
/// instantly be synchronised to disk. If this is not specified, then the
/// collection's default sync behavior will be applied.
///
/// - @LIT{limit}: an optional value that determines how many documents to
/// replace at most. If @LIT{limit} is specified but is less than the number
/// of documents in the collection, it is undefined which of the documents
/// will be replaced.
///
/// Returns the number of documents that were replaced
///
/// @EXAMPLES
///
/// @verbinclude api-simple-replace-by-example
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url : API + "replace-by-example",
context : "api",
callback : function (req, res) {
try {
var body = actions.getJsonBody(req, res);
if (body === undefined) {
return;
}
if (req.requestType != actions.PUT) {
actions.resultUnsupported(req, res);
}
else {
var example = body.example;
var newValue = body.newValue;
var name = body.collection;
var collection = db._collection(name);
var limit = body.limit || null;
if (collection === null) {
actions.collectionNotFound(req, res, name);
}
else if (typeof example !== "object") {
actions.badParameter(req, res, "example");
}
else if (typeof newValue !== "object") {
actions.badParameter(req, res, "newValue");
}
else {
var result = collection.replaceByExample(example, newValue, body.waitForSync, limit);
actions.resultOk(req, res, actions.HTTP_OK, { replaced: result });
}
}
}
catch (err) {
actions.resultException(req, res, err);
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @fn JSA_PUT_api_simple_update_by_example
/// @brief partially updates the body of all documents of a collection that
/// match an example
///
/// @RESTHEADER{PUT /_api/simple/update-by-example,updates documents by example}
///
/// @REST{PUT /_api/simple/update-by-example}
///
/// This will find all documents in the collection that match the specified
/// example object, and partially update the document body with the new value
/// specified. Note that document meta-attributes such as @LIT{_id}, @LIT{_key},
/// @LIT{_from}, @LIT{_to} etc. cannot be replaced.
///
/// The call expects a JSON hash array as body with the following attributes:
///
/// - @LIT{collection}: The name of the collection to update within.
///
/// - @LIT{example}: An example object that all collection objects are compared
/// against.
///
/// - @LIT{newValue}: The update value that will get inserted in place
/// of the "old" version of the found documents.
///
/// - @LIT{keepNull}: This parameter can be used to modify the behavior when
/// handling @LIT{null} values. Normally, @LIT{null} values are stored in the
/// database. By setting the @FA{keepNull} parameter to @LIT{false}, this
/// behavior can be changed so that all attributes in @FA{data} with @LIT{null}
/// values will be removed from the updated document.
///
/// - @LIT{waitForSync}: if set to true, then all removal operations will
/// instantly be synchronised to disk. If this is not specified, then the
/// collection's default sync behavior will be applied.
///
/// - @LIT{limit}: an optional value that determines how many documents to
/// update at most. If @LIT{limit} is specified but is less than the number
/// of documents in the collection, it is undefined which of the documents
/// will be updated.
///
/// Returns the number of documents that were updated
///
/// @EXAMPLES
///
/// @verbinclude api-simple-update-by-example
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url : API + "update-by-example",
context : "api",
callback : function (req, res) {
try {
var body = actions.getJsonBody(req, res);
if (body === undefined) {
return;
}
if (req.requestType != actions.PUT) {
actions.resultUnsupported(req, res);
}
else {
var example = body.example;
var newValue = body.newValue;
var name = body.collection;
var collection = db._collection(name);
var limit = body.limit || null;
var keepNull = body.keepNull || null;
if (collection === null) {
actions.collectionNotFound(req, res, name);
}
else if (typeof example !== "object") {
actions.badParameter(req, res, "example");
}
else if (typeof newValue !== "object") {
actions.badParameter(req, res, "newValue");
}
else {
var result = collection.updateByExample(example, newValue, keepNull, body.waitForSync, limit);
actions.resultOk(req, res, actions.HTTP_OK, { updated: result });
}
}
}
catch (err) {
actions.resultException(req, res, err);
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,409 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief user management
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2012 triagens GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Achim Brandt
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var arangodb = require("org/arangodb");
var actions = require("org/arangodb/actions");
var users = require("org/arangodb/users");
var ArangoError = require("org/arangodb/arango-error").ArangoError;
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoAPI
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief fetch a user
///
/// @RESTHEADER{GET /_api/user,fetches a user}
///
/// @REST{GET /_api/user/@FA{username}}
///
/// Fetches data about the specified user.
///
/// The call will return a JSON document with at least the following attributes
/// on success:
///
/// - @LIT{username}: The name of the user as a string.
///
/// - @LIT{active}: an optional flag that specifies whether the user is active.
///
/// - @LIT{extra}: an optional JSON object with arbitrary extra data about the
/// user.
////////////////////////////////////////////////////////////////////////////////
function GET_api_user (req, res) {
if (req.suffix.length != 1) {
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
return;
}
var username = decodeURIComponent(req.suffix[0]);
try {
var result = users.document(username);
actions.resultOk(req, res, actions.HTTP_OK, result)
}
catch (err) {
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
actions.resultNotFound(req, res, arangodb.errors.ERROR_USER_NOT_FOUND.code);
}
else {
throw err;
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a new user
///
/// @RESTHEADER{POST /_api/user,creates user}
///
/// @REST{POST /_api/user}
///
/// The following data need to be passed in a JSON representation in the body of
/// the POST request:
///
/// - @LIT{username}: The name of the user as a string. This is mandatory.
///
/// - @LIT{passwd}: The user password as a string. If no password is specified,
/// the empty string will be used.
///
/// - @LIT{active}: an optional flag that specifies whether the user is active.
/// If not specified, this will default to @LIT{true}.
///
/// - @LIT{extra}: an optional JSON object with arbitrary extra data about the
/// user.
///
/// If the user can be added by the server, the server will respond with
/// @LIT{HTTP 201}.
///
/// In case of success, the returned JSON object has the following properties:
///
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{false}
/// in this case)
///
/// - @LIT{code}: the HTTP status code
///
/// If the JSON representation is malformed or mandatory data is missing from the
/// request, the server will respond with @LIT{HTTP 400}.
///
/// The body of the response will contain a JSON object with additional error
/// details. The object has the following attributes:
///
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{true} in this case)
///
/// - @LIT{code}: the HTTP status code
///
/// - @LIT{errorNum}: the server error number
///
/// - @LIT{errorMessage}: a descriptive error message
////////////////////////////////////////////////////////////////////////////////
function POST_api_user (req, res) {
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
if (json === undefined) {
return;
}
var result = users.save(json.username, json.passwd, json.active, json.extra);
users.reload();
actions.resultOk(req, res, actions.HTTP_CREATED, { });
}
////////////////////////////////////////////////////////////////////////////////
/// @brief replace an existing user
///
/// @RESTHEADER{PUT /_api/user,replaces user}
///
/// @REST{PUT /_api/user/@FA{username}}
///
/// Replaces the data of an existing user. The name of an existing user must
/// be specified in @FA{username}.
///
/// The following data can to be passed in a JSON representation in the body of
/// the POST request:
///
/// - @LIT{passwd}: The user password as a string. Specifying a password is
/// mandatory, but the empty string is allowed for passwords.
///
/// - @LIT{active}: an optional flag that specifies whether the user is active.
/// If not specified, this will default to @LIT{true}.
///
/// - @LIT{extra}: an optional JSON object with arbitrary extra data about the
/// user.
///
/// If the user can be replaced by the server, the server will respond with
/// @LIT{HTTP 200}.
///
/// In case of success, the returned JSON object has the following properties:
///
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{false}
/// in this case)
///
/// - @LIT{code}: the HTTP status code
///
/// If the JSON representation is malformed or mandatory data is missing from the
/// request, the server will respond with @LIT{HTTP 400}. If the specified user
/// does not exist, the server will respond with @LIT{HTTP 404}.
///
/// The body of the response will contain a JSON object with additional error
/// details. The object has the following attributes:
///
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{true} in this case)
///
/// - @LIT{code}: the HTTP status code
///
/// - @LIT{errorNum}: the server error number
///
/// - @LIT{errorMessage}: a descriptive error message
////////////////////////////////////////////////////////////////////////////////
function PUT_api_user (req, res) {
if (req.suffix.length != 1) {
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
return;
}
var username = decodeURIComponent(req.suffix[0]);
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
if (json === undefined) {
return;
}
try {
var result = users.replace(username, json.passwd, json.active, json.extra);
users.reload();
actions.resultOk(req, res, actions.HTTP_OK, { });
}
catch (err) {
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
actions.resultNotFound(req, res, arangodb.errors.ERROR_USER_NOT_FOUND.code);
}
else {
throw err;
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief partially update an existing user
///
/// @RESTHEADER{PATCH /_api/user,updates user}
///
/// @REST{PATCH /_api/user/@FA{username}}
///
/// Partially updates the data of an existing user. The name of an existing user
/// must be specified in @FA{username}.
///
/// The following data can be passed in a JSON representation in the body of
/// the POST request:
///
/// - @LIT{passwd}: The user password as a string. Specifying a password is
/// optional. If not specified, the previously existing value will not be
/// modified.
///
/// - @LIT{active}: an optional flag that specifies whether the user is active.
/// If not specified, the previously existing value will not be modified.
///
/// - @LIT{extra}: an optional JSON object with arbitrary extra data about the
/// user. If not specified, the previously existing value will not be modified.
///
/// If the user can be updated by the server, the server will respond with
/// @LIT{HTTP 200}.
///
/// In case of success, the returned JSON object has the following properties:
///
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{false}
/// in this case)
///
/// - @LIT{code}: the HTTP status code
///
/// If the JSON representation is malformed or mandatory data is missing from the
/// request, the server will respond with @LIT{HTTP 400}. If the specified user
/// does not exist, the server will respond with @LIT{HTTP 404}.
///
/// The body of the response will contain a JSON object with additional error
/// details. The object has the following attributes:
///
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{true} in this case)
///
/// - @LIT{code}: the HTTP status code
///
/// - @LIT{errorNum}: the server error number
///
/// - @LIT{errorMessage}: a descriptive error message
////////////////////////////////////////////////////////////////////////////////
function PATCH_api_user (req, res) {
if (req.suffix.length != 1) {
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
return;
}
var username = decodeURIComponent(req.suffix[0]);
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
if (json === undefined) {
return;
}
try {
var result = users.update(username, json.passwd, json.active, json.extra);
users.reload();
actions.resultOk(req, res, actions.HTTP_OK, { });
}
catch (err) {
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
actions.resultNotFound(req, res, arangodb.errors.ERROR_USER_NOT_FOUND.code);
}
else {
throw err;
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief remove an existing user
///
/// @RESTHEADER{DELETE /_api/user,removes a user}
///
/// @REST{DELETE /_api/user/@FA{username}}
///
/// Removes an existing user, identified by @FA{username}.
///
/// If the user can be removed by the server, the server will respond with
/// @LIT{HTTP 202}.
///
/// In case of success, the returned JSON object has the following properties:
///
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{false}
/// in this case)
///
/// - @LIT{code}: the HTTP status code
///
/// If the JSON representation is malformed or mandatory data is missing from the
/// request, the server will respond with @LIT{HTTP 400}. If the specified user
/// does not exist, the server will respond with @LIT{HTTP 404}.
///
/// The body of the response will contain a JSON object with additional error
/// details. The object has the following attributes:
///
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{true} in this case)
///
/// - @LIT{code}: the HTTP status code
///
/// - @LIT{errorNum}: the server error number
///
/// - @LIT{errorMessage}: a descriptive error message
////////////////////////////////////////////////////////////////////////////////
function DELETE_api_user (req, res) {
if (req.suffix.length != 1) {
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
return;
}
var username = decodeURIComponent(req.suffix[0]);
try {
var result = users.remove(username);
users.reload();
actions.resultOk(req, res, actions.HTTP_ACCEPTED, { });
}
catch (err) {
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
actions.resultNotFound(req, res, arangodb.errors.ERROR_USER_NOT_FOUND.code);
}
else {
throw err;
}
}
}
// -----------------------------------------------------------------------------
// --SECTION-- initialiser
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief user actions gateway
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url : "_api/user",
context : "api",
callback : function (req, res) {
try {
switch (req.requestType) {
case actions.GET:
GET_api_user(req, res);
break;
case actions.POST:
POST_api_user(req, res);
break;
case actions.PUT:
PUT_api_user(req, res);
break;
case actions.PATCH:
PATCH_api_user(req, res);
break;
case actions.DELETE:
DELETE_api_user(req, res);
break;
default:
actions.resultUnsupported(req, res);
}
}
catch (err) {
actions.resultException(req, res, err);
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -1116,11 +1116,14 @@ ArangoCollection.prototype.outEdges = function (vertex) {
/// @brief removes documents matching an example
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
ArangoCollection.prototype.removeByExample = function (example,
waitForSync,
limit) {
var data = {
collection: this._name,
example: example,
waitForSync: waitForSync
waitForSync: waitForSync,
limit: limit
};
var requestResult = this._database._connection.PUT(
@ -1132,6 +1135,58 @@ ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
return requestResult.deleted;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief replaces documents matching an example
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.replaceByExample = function (example,
newValue,
waitForSync,
limit) {
var data = {
collection: this._name,
example: example,
newValue: newValue,
waitForSync: waitForSync,
limit: limit
};
var requestResult = this._database._connection.PUT(
"/_api/simple/replace-by-example",
JSON.stringify(data));
arangosh.checkRequestResult(requestResult);
return requestResult.replaced;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief updates documents matching an example
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.updateByExample = function (example,
newValue,
keepNull,
waitForSync,
limit) {
var data = {
collection: this._name,
example: example,
newValue: newValue,
keepNull: keepNull,
waitForSync: waitForSync,
limit: limit
};
var requestResult = this._database._connection.PUT(
"/_api/simple/update-by-example",
JSON.stringify(data));
arangosh.checkRequestResult(requestResult);
return requestResult.updated;
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -1,12 +1,13 @@
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true, nonpropdel: true, proto: true */
/*global require, module, Module, FS_MOVE, FS_REMOVE, FS_EXISTS, FS_IS_DIRECTORY, FS_LIST_TREE,
SYS_EXECUTE, SYS_LOAD, SYS_LOG, SYS_LOG_LEVEL, SYS_OUTPUT, SYS_PROCESS_STAT, SYS_READ,
SYS_SPRINTF, SYS_TIME, SYS_START_PAGER, SYS_STOP_PAGER, SYS_SHA256, SYS_WAIT, SYS_GETLINE,
SYS_PARSE, SYS_SAVE, SYS_IMPORT_CSV_FILE, SYS_IMPORT_JSON_FILE, SYS_PROCESS_CSV_FILE,
SYS_PROCESS_JSON_FILE, ARANGO_QUIET, MODULES_PATH, COLORS, COLOR_OUTPUT, COLOR_OUTPUT_RESET,
COLOR_BRIGHT, COLOR_BLACK, COLOR_BOLD_BLACK, COLOR_BLINK, COLOR_BLUE, COLOR_BOLD_BLUE,
COLOR_BOLD_GREEN, COLOR_RED, COLOR_BOLD_RED, COLOR_GREEN, COLOR_WHITE, COLOR_BOLD_WHITE,
COLOR_YELLOW, COLOR_BOLD_YELLOW, PRETTY_PRINT, VALGRIND, HAS_ICU, VERSION, UPGRADE */
SYS_EXECUTE, SYS_LOAD, SYS_LOG, SYS_LOG_LEVEL, SYS_MD5, SYS_OUTPUT, SYS_PROCESS_STAT, SYS_RAND,
SYS_READ, SYS_SPRINTF, SYS_TIME, SYS_START_PAGER, SYS_STOP_PAGER, SYS_SHA256, SYS_WAIT,
SYS_GETLINE, SYS_PARSE, SYS_SAVE, SYS_IMPORT_CSV_FILE, SYS_IMPORT_JSON_FILE,
SYS_PROCESS_CSV_FILE, SYS_PROCESS_JSON_FILE, ARANGO_QUIET, MODULES_PATH, COLORS, COLOR_OUTPUT,
COLOR_OUTPUT_RESET, COLOR_BRIGHT, COLOR_BLACK, COLOR_BOLD_BLACK, COLOR_BLINK, COLOR_BLUE,
COLOR_BOLD_BLUE, COLOR_BOLD_GREEN, COLOR_RED, COLOR_BOLD_RED, COLOR_GREEN, COLOR_WHITE,
COLOR_BOLD_WHITE, COLOR_YELLOW, COLOR_BOLD_YELLOW, PRETTY_PRINT, VALGRIND, HAS_ICU, VERSION,
UPGRADE */
////////////////////////////////////////////////////////////////////////////////
/// @brief module "internal"
@ -76,6 +77,11 @@
internal.logLevel = SYS_LOG_LEVEL;
delete SYS_LOG_LEVEL;
}
if (typeof SYS_MD5 !== "undefined") {
internal.md5 = SYS_MD5;
delete SYS_MD5;
}
if (typeof SYS_OUTPUT !== "undefined") {
internal.stdOutput = SYS_OUTPUT;
@ -92,6 +98,11 @@
internal.processStat = SYS_PROCESS_STAT;
delete SYS_PROCESS_STAT;
}
if (typeof SYS_RAND !== "undefined") {
internal.rand = SYS_RAND;
delete SYS_RAND;
}
if (typeof SYS_READ !== "undefined") {
internal.read = SYS_READ;
@ -798,22 +809,6 @@
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief encode password using SHA256
////////////////////////////////////////////////////////////////////////////////
internal.encodePassword = function (password) {
var salt;
var encoded;
salt = internal.sha256("time:" + internal.time());
salt = salt.substr(0,8);
encoded = "$1$" + salt + "$" + internal.sha256(salt + password);
return encoded;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief extends a prototype
////////////////////////////////////////////////////////////////////////////////

View File

@ -672,6 +672,13 @@ ArangoCollection.prototype.iterate = function (iterator, options) {
/// synchronisation for collections that have a default @LIT{waitForSync} value
/// of @LIT{true}.
///
/// @FUN{@FA{collection}.removeByExample(@FA{document}, @FA{waitForSync}, @FA{limit})}
///
/// The optional @FA{limit} parameter can be used to restrict the number of
/// removals to the specified value. If @FA{limit} is specified but less than the
/// number of documents in the collection, it is undefined which documents are
/// removed.
///
/// @EXAMPLES
///
/// @code
@ -679,10 +686,99 @@ ArangoCollection.prototype.iterate = function (iterator, options) {
/// @endcode
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
ArangoCollection.prototype.removeByExample = function (example, waitForSync, limit) {
throw "cannot call abstract removeByExample function";
};
////////////////////////////////////////////////////////////////////////////////
/// @brief replaces documents matching an example
///
/// @FUN{@FA{collection}.replaceByExample(@FA{example}, @FA{newValue})}
///
/// Replaces all documents matching an example with a new document body.
/// The entire document body of each document matching the @FA{example} will be
/// replaced with @FA{newValue}. The document meta-attributes such as @LIT{_id},
/// @LIT{_key}, @LIT{_from}, @LIT{_to} will not be replaced.
///
/// @FUN{@FA{collection}.replaceByExample(@FA{document}, @FA{newValue}, @FA{waitForSync})}
///
/// The optional @FA{waitForSync} parameter can be used to force synchronisation
/// of the document replacement operation to disk even in case that the
/// @LIT{waitForSync} flag had been disabled for the entire collection. Thus,
/// the @FA{waitForSync} parameter can be used to force synchronisation of just
/// specific operations. To use this, set the @FA{waitForSync} parameter to
/// @LIT{true}. If the @FA{waitForSync} parameter is not specified or set to
/// @LIT{false}, then the collection's default @LIT{waitForSync} behavior is
/// applied. The @FA{waitForSync} parameter cannot be used to disable
/// synchronisation for collections that have a default @LIT{waitForSync} value
/// of @LIT{true}.
///
/// @FUN{@FA{collection}.replaceByExample(@FA{document}, @FA{newValue}, @FA{waitForSync}, @FA{limit})}
///
/// The optional @FA{limit} parameter can be used to restrict the number of
/// replacements to the specified value. If @FA{limit} is specified but less than
/// the number of documents in the collection, it is undefined which documents are
/// replaced.
///
/// @EXAMPLES
///
/// @code
/// arangod> db.content.replaceByExample({ "domain": "de.celler" }, { "foo": "someValue }, false, 5)
/// @endcode
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.replaceByExample = function (example, newValue, waitForSync, limit) {
throw "cannot call abstract replaceByExample function";
};
////////////////////////////////////////////////////////////////////////////////
/// @brief partially updates documents matching an example
///
/// @FUN{@FA{collection}.updateByExample(@FA{example}, @FA{newValue})}
///
/// Partially updates all documents matching an example with a new document body.
/// Specific attributes in the document body of each document matching the
/// @FA{example} will be updated with the values from @FA{newValue}.
/// The document meta-attributes such as @LIT{_id}, @LIT{_key}, @LIT{_from},
/// @LIT{_to} cannot be updated.
///
/// @FUN{@FA{collection}.updateByExample(@FA{document}, @FA{newValue}, @FA{keepNull}, @FA{waitForSync})}
///
/// The optional @FA{keepNull} parameter can be used to modify the behavior when
/// handling @LIT{null} values. Normally, @LIT{null} values are stored in the
/// database. By setting the @FA{keepNull} parameter to @LIT{false}, this behavior
/// can be changed so that all attributes in @FA{data} with @LIT{null} values will
/// be removed from the target document.
///
/// The optional @FA{waitForSync} parameter can be used to force synchronisation
/// of the document replacement operation to disk even in case that the
/// @LIT{waitForSync} flag had been disabled for the entire collection. Thus,
/// the @FA{waitForSync} parameter can be used to force synchronisation of just
/// specific operations. To use this, set the @FA{waitForSync} parameter to
/// @LIT{true}. If the @FA{waitForSync} parameter is not specified or set to
/// @LIT{false}, then the collection's default @LIT{waitForSync} behavior is
/// applied. The @FA{waitForSync} parameter cannot be used to disable
/// synchronisation for collections that have a default @LIT{waitForSync} value
/// of @LIT{true}.
///
/// @FUN{@FA{collection}.updateByExample(@FA{document}, @FA{newValue}, @FA{keepNull}, @FA{waitForSync}, @FA{limit})}
///
/// The optional @FA{limit} parameter can be used to restrict the number of
/// updates to the specified value. If @FA{limit} is specified but less than
/// the number of documents in the collection, it is undefined which documents are
/// updated.
///
/// @EXAMPLES
///
/// @code
/// arangod> db.content.updateByExample({ "domain": "de.celler" }, { "foo": "someValue, "domain": null }, false)
/// @endcode
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.updateByExample = function (example, newValue, keepNull, waitForSync, limit) {
throw "cannot call abstract updateExample function";
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,101 @@
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
/*global require, exports */
////////////////////////////////////////////////////////////////////////////////
/// @brief Some crypto functions
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2012 triagens GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var internal = require("internal");
// -----------------------------------------------------------------------------
// --SECTION-- RANDOM
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- public methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Random
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief generate a random number
///
/// the value returned might be positive or negative or even 0.
/// if random number generation fails, then undefined is returned
////////////////////////////////////////////////////////////////////////////////
exports.rand = function (value) {
return internal.rand();
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- HASHES
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- public methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Hashes
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief apply an MD5 hash
////////////////////////////////////////////////////////////////////////////////
exports.md5 = function (value) {
return internal.md5(value);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief apply an SHA 256 hash
////////////////////////////////////////////////////////////////////////////////
exports.sha256 = function (value) {
return internal.sha256(value);
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @}\\|/\\*jslint"
// End:

View File

@ -28,11 +28,11 @@
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var internal = require("internal"); // OK: encodePassword, reloadAuth
var internal = require("internal"); // OK: reloadAuth, time
var encodePassword = internal.encodePassword;
var reloadAuth = internal.reloadAuth;
var arangodb = require("org/arangodb");
var crypto = require("org/arangodb/crypto");
var db = arangodb.db;
var ArangoError = require("org/arangodb/arango-error").ArangoError;
@ -49,6 +49,30 @@ var ArangoError = require("org/arangodb/arango-error").ArangoError;
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief encode password using SHA256
////////////////////////////////////////////////////////////////////////////////
var encodePassword = function (password) {
var salt;
var encoded;
var random = crypto.rand();
if (random === undefined) {
random = "time:" + internal.time();
}
else {
random = "random:" + random;
}
salt = crypto.sha256(random);
salt = salt.substr(0,8);
encoded = "$1$" + salt + "$" + crypto.sha256(salt + password);
return encoded;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief validate a username
////////////////////////////////////////////////////////////////////////////////
@ -112,14 +136,16 @@ var getStorage = function () {
/// @fn JSF_saveUser
/// @brief create a new user
///
/// @FUN{users.save(@FA{username}, @FA{passwd})}
/// @FUN{users.save(@FA{username}, @FA{passwd}, @FA{active}, @FA{extra})}
///
/// This will create a new ArangoDB user. The username must be a string and
/// contain only the letters a-z (lower or upper case), the digits 0-9, the dash
/// or the underscore symbol.
/// This will create a new ArangoDB user. The username must be specified and
/// must not be empty.
///
/// The password must be given as a string, too, but can be left empty if
/// required.
///
/// If the @FA{active} attribute is not specified, it defaults to @LIT{true}.
/// The @FA{extra} attribute can be used to save custom data with the user.
///
/// This method will fail if either the username or the passwords are not
/// specified or given in a wrong format, or there already exists a user with
@ -138,7 +164,7 @@ var getStorage = function () {
/// @endcode
////////////////////////////////////////////////////////////////////////////////
exports.save = function (username, passwd) {
exports.save = function (username, passwd, active, extra) {
if (passwd === null || passwd === undefined) {
passwd = "";
}
@ -150,9 +176,24 @@ exports.save = function (username, passwd) {
var users = getStorage();
var user = users.firstExample({ user: username });
if (active === undefined || active === null) {
// this is the default value for active
active = true;
}
if (user === null) {
var hash = encodePassword(passwd);
return users.save({ user: username, password: hash, active: true });
var data = {
user: username,
password: hash,
active: active
};
if (extra !== undefined) {
data.extra = extra;
}
return users.save(data);
}
var err = new ArangoError();
@ -164,25 +205,27 @@ exports.save = function (username, passwd) {
////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_replaceUser
/// @brief update an existing user
/// @brief replace an existing user
///
/// @FUN{users.replace(@FA{username}, @FA{passwd})}
/// @FUN{users.replace(@FA{username}, @FA{passwd}, @FA{active}, @FA{extra})}
///
/// This will update an existing ArangoDB user with a new password.
/// This will look up an existing ArangoDB user and replace its user data.
///
/// The username must be a string and contain only the letters a-z (lower or
/// upper case), the digits 0-9, the dash or the underscore symbol. The user
/// must already exist in the database.
/// The username must be specified, and a user with the specified name must
/// already exist in the database.
///
/// The password must be given as a string, too, but can be left empty if
/// required.
///
/// If the @FA{active} attribute is not specified, it defaults to @LIT{true}.
/// The @FA{extra} attribute can be used to save custom data with the user.
///
/// This method will fail if either the username or the passwords are not
/// specified or given in a wrong format, or if the specified user cannot be
/// found in the database.
///
/// The update is effective only after the server is either restarted
/// or the server authentication cache is @ref UserManagementReload "reloaded".
/// The replace is effective only after the server is either restarted
/// or the server authentication cache is reloaded (see @ref JSF_reloadUsers).
///
/// Note: this function will not work from within the web interface
///
@ -194,8 +237,7 @@ exports.save = function (username, passwd) {
/// @endcode
////////////////////////////////////////////////////////////////////////////////
exports.replace =
exports.update = function (username, passwd) {
exports.replace = function (username, passwd, active, extra) {
if (passwd === null || passwd === undefined) {
passwd = "";
}
@ -207,6 +249,11 @@ exports.update = function (username, passwd) {
var users = getStorage();
var user = users.firstExample({ user: username });
if (active === undefined || active === null) {
// this is the default
active = true;
}
if (user === null) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_USER_NOT_FOUND.code;
@ -216,11 +263,84 @@ exports.update = function (username, passwd) {
}
var hash = encodePassword(passwd);
var data = user.shallowCopy;
data.password = hash;
var data = {
user: username,
password: hash,
active: active
};
if (extra !== undefined) {
data.extra = extra;
}
return users.replace(user, data);
};
////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_updateUser
/// @brief update an existing user
///
/// @FUN{@FA{users}.update(@FA{username}, @FA{passwd}, @FA{active}, @FA{extra})}
///
/// This will update an existing ArangoDB user with a new password and other
/// data.
///
/// The username must be specified and the user must already exist in the
/// database.
///
/// The password must be given as a string, too, but can be left empty if
/// required.
///
/// If the @FA{active} attribute is not specified, the current value saved for
/// the user will not be changed. The same is true for the @FA{extra} attribute.
///
/// This method will fail if either the username or the passwords are not
/// specified or given in a wrong format, or if the specified user cannot be
/// found in the database.
///
/// The update is effective only after the server is either restarted
/// or the server authentication cache is reloaded (see @ref JSF_reloadUsers).
///
/// Note: this function will not work from within the web interface
///
/// @EXAMPLES
///
/// @TINYEXAMPLE{user-replace,replacing an existing user}
////////////////////////////////////////////////////////////////////////////////
exports.update = function (username, passwd, active, extra) {
// validate input
validateName(username);
if (passwd !== undefined) {
validatePassword(passwd);
}
var users = getStorage();
var user = users.firstExample({ user: username });
if (user === null) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_USER_NOT_FOUND.code;
err.errorMessage = arangodb.errors.ERROR_USER_NOT_FOUND.message;
throw err;
}
var data = user.shallowCopy;
if (passwd !== undefined) {
var hash = encodePassword(passwd);
data.password = hash;
}
if (active !== undefined && active !== null) {
data.active = active;
}
if (extra !== undefined) {
data.extra = extra;
}
return users.update(user, data);
};
////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_removeUser
/// @brief delete an existing user
@ -229,9 +349,8 @@ exports.update = function (username, passwd) {
///
/// Removes an existing ArangoDB user from the database.
///
/// The username must be a string and contain only the letters a-z (lower or
/// upper case), the digits 0-9, the dash or the underscore symbol. The user
/// must already exist in the database.
/// The username must be a string and the specified user must exist in the
/// database.
///
/// This method will fail if the user cannot be found in the database.
///
@ -266,6 +385,39 @@ exports.remove = function (username) {
return users.remove(user._id);
};
////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_documentUser
/// @brief get an existing user
///
/// @FUN{users.document(@FA{username})}
///
/// Fetches an existing ArangoDB user from the database.
///
/// This method will fail if the user cannot be found in the database.
///
/// Note: this function will not work from within the web interface
////////////////////////////////////////////////////////////////////////////////
exports.document = function (username) {
// validate name
validateName(username);
var users = getStorage();
var user = users.firstExample({ user: username });
if (user === null) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_USER_NOT_FOUND.code;
err.errorMessage = arangodb.errors.ERROR_USER_NOT_FOUND.message;
throw err;
}
delete user.passwd;
return user;
};
////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_reloadUsers
/// @brief reloads the user authentication data

View File

@ -0,0 +1,139 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief test the crypto interface
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var crypto = require("org/arangodb/crypto");
// -----------------------------------------------------------------------------
// --SECTION-- crypto methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function CryptoSuite () {
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief test md5, invalid values
////////////////////////////////////////////////////////////////////////////////
testMd5Invalid : function () {
[ undefined, null, true, false, 0, 1, -1, 32.5, [ ], { } ].forEach(function (value) {
try {
crypto.md5(value);
fail();
}
catch (err) {
}
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test md5
////////////////////////////////////////////////////////////////////////////////
testMd5 : function () {
var data = [
[ "", "d41d8cd98f00b204e9800998ecf8427e" ],
[ " ", "7215ee9c7d9dc229d2921a40e899ec5f" ],
[ "arangodb", "335889e69e03cefd041babef1b02a0b8" ],
[ "Arangodb", "0862bc79ec789143f75e3282df98c8f4" ],
[ "ArangoDB is a database", "b88ddc26cfa3a652fdd8bf8e8c069540" ]
];
data.forEach(function (value) {
assertEqual(value[1], crypto.md5(value[0]));
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test sha256, invalid values
////////////////////////////////////////////////////////////////////////////////
testSha256Invalid : function () {
[ undefined, null, true, false, 0, 1, -1, 32.5, [ ], { } ].forEach(function (value) {
try {
crypto.sha256(value);
fail();
}
catch (err) {
}
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test sha256
////////////////////////////////////////////////////////////////////////////////
testSha256 : function () {
var data = [
[ "", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ],
[ " ", "36a9e7f1c95b82ffb99743e0c5c4ce95d83c9a430aac59f84ef3cbfab6145068"],
[ "arangodb", "d0a274654772fa104df32ff457ff0a432f2dfad18e7c3146fab3c807f2ed86e5" ],
[ "Arangodb", "38579e73fd7435de7db93028a1b340be77445b46c94dff013c05c696ccae259c" ],
[ "ArangoDB is a database", "00231e8f9c0a617426ae51e4e230a1b25f6d5b82c10fccc835b514142f235f31" ]
];
data.forEach(function (value) {
assertEqual(value[1], crypto.sha256(value[0]));
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test random
////////////////////////////////////////////////////////////////////////////////
testRandom : function () {
var i;
for (i = 0; i < 100; ++i) {
assertTrue(crypto.rand() != 0);
}
}
};
}
// -----------------------------------------------------------------------------
// --SECTION-- main
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(CryptoSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -886,19 +886,22 @@ function CollectionDocumentSuite () {
assertEqual(3, figures.alive.count);
assertEqual(0, figures.dead.count);
// now remove some
// now remove some documents
collection.remove("a2");
collection.remove("a3");
// we should see two live docs less
figures = collection.figures();
assertEqual(1, figures.alive.count);
assertEqual(2, figures.dead.count);
// replacing one document does not change alive, but increases dead!
collection.replace("a1", { });
figures = collection.figures();
assertEqual(1, figures.alive.count);
assertEqual(3, figures.dead.count);
// this doc does not exist. should not change the figures
try {
collection.replace("a2", { });
fail();

View File

@ -468,7 +468,249 @@ function SimpleQueryByExampleSuite () {
collection.truncate();
deleted = collection.removeByExample({ value1: 3 });
assertEqual(0, deleted);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: removeByExample
////////////////////////////////////////////////////////////////////////////////
testRemoveByExampleLimit : function () {
var deleted;
for (var i = 0; i < 50; ++i) {
collection.save({ value : 1 });
}
deleted = collection.removeByExample({ value : 1 }, false, 10);
assertEqual(10, deleted);
assertEqual(40, collection.count());
deleted = collection.removeByExample({ value : 1 }, false, 20);
assertEqual(20, deleted);
assertEqual(20, collection.count());
deleted = collection.removeByExample({ value : 1 }, false, 20);
assertEqual(20, deleted);
assertEqual(0, collection.count());
deleted = collection.removeByExample({ value : 1 }, false, 20);
assertEqual(0, deleted);
assertEqual(0, collection.count());
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: replaceByExample
////////////////////////////////////////////////////////////////////////////////
testReplaceByExample : function () {
var replaced;
for (var i = 0; i < 50; ++i) {
collection.save({ value : i });
}
replaced = collection.replaceByExample({ value : 2 }, { foo : "bar", bar : "baz" });
assertEqual(1, replaced);
assertEqual(50, collection.count());
var doc = collection.firstExample({ foo : "bar", bar : "baz" });
assertEqual("bar", doc.foo);
assertEqual("baz", doc.bar);
assertEqual(undefined, doc.value);
// not existing documents
replaced = collection.replaceByExample({ meow : true }, { });
assertEqual(0, replaced);
replaced = collection.replaceByExample({ value : null }, { });
assertEqual(0, replaced);
collection.truncate();
replaced = collection.replaceByExample({ value : 6 }, { });
assertEqual(0, replaced);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: replaceByExample
////////////////////////////////////////////////////////////////////////////////
testReplaceByExampleLimit : function () {
var replaced, docs;
for (var i = 0; i < 50; ++i) {
collection.save({ value : 1 });
}
replaced = collection.replaceByExample({ value : 1 }, { foo : "bar", bar : "baz" }, false, 10);
assertEqual(10, replaced);
assertEqual(50, collection.count());
docs = collection.byExample({ foo : "bar", bar : "baz" }).toArray();
assertEqual(10, docs.length);
docs = collection.byExample({ value : 1 }).toArray();
assertEqual(40, docs.length);
replaced = collection.replaceByExample({ value : 1 }, { meow : false }, false, 15);
assertEqual(15, replaced);
assertEqual(50, collection.count());
docs = collection.byExample({ foo : "bar", bar : "baz" }).toArray();
assertEqual(10, docs.length);
docs = collection.byExample({ meow : false }).toArray();
assertEqual(15, docs.length);
docs = collection.byExample({ value : 1 }).toArray();
assertEqual(25, docs.length);
// not existing documents
replaced = collection.replaceByExample({ meow : true }, { }, false, 99);
assertEqual(0, replaced);
replaced = collection.replaceByExample({ value : null }, { }, false, 99);
assertEqual(0, replaced);
// check counts
docs = collection.byExample({ foo : "bar", bar : "baz" }).toArray();
assertEqual(10, docs.length);
docs = collection.byExample({ meow : false }).toArray();
assertEqual(15, docs.length);
docs = collection.byExample({ value : 1 }).toArray();
assertEqual(25, docs.length);
collection.truncate();
replaced = collection.replaceByExample({ value : 1 }, { }, false);
assertEqual(0, replaced);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: updateByExample
////////////////////////////////////////////////////////////////////////////////
testUpdateByExample : function () {
var updated;
for (var i = 0; i < 50; ++i) {
collection.save({ value : i });
}
// update and keep old values
updated = collection.updateByExample({ value : 2 }, { foo : "bar", bar : "baz" });
assertEqual(1, updated);
assertEqual(50, collection.count());
var doc = collection.firstExample({ foo : "bar", bar : "baz" });
assertEqual("bar", doc.foo);
assertEqual("baz", doc.bar);
assertEqual(2, doc.value);
// update and remove old values
updated = collection.updateByExample({ value : 5 }, { foo : "bart", bar : "baz", value : null }, false);
assertEqual(1, updated);
var doc = collection.firstExample({ foo : "bart", bar : "baz" });
assertEqual("bart", doc.foo);
assertEqual("baz", doc.bar);
assertEqual(undefined, doc.value);
// update and overwrite old values
updated = collection.updateByExample({ value : 17 }, { foo : "barw", bar : "baz", value : 9 }, false);
assertEqual(1, updated);
var doc = collection.firstExample({ foo : "barw", bar : "baz" });
assertEqual("barw", doc.foo);
assertEqual("baz", doc.bar);
assertEqual(9, doc.value);
// not existing documents
updated = collection.updateByExample({ meow : true }, { });
assertEqual(0, updated);
updated = collection.updateByExample({ value : null }, { });
assertEqual(0, updated);
collection.truncate();
updated = collection.updateByExample({ value : 6 }, { });
assertEqual(0, updated);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: updateByExample
////////////////////////////////////////////////////////////////////////////////
testUpdateByExampleLimit : function () {
var updated, docs;
for (var i = 0; i < 50; ++i) {
collection.save({ value : 1 });
}
// update some, keep old values
updated = collection.updateByExample({ value : 1 }, { foo : "bar", bar : "baz" }, false, false, 10);
assertEqual(10, updated);
assertEqual(50, collection.count());
docs = collection.byExample({ foo : "bar", bar : "baz" }).toArray();
assertEqual(10, docs.length);
docs = collection.byExample({ value : 1 }).toArray();
assertEqual(50, docs.length);
// update some others
updated = collection.updateByExample({ value : 1 }, { meow : false }, false, false, 15);
assertEqual(15, updated);
assertEqual(50, collection.count());
docs = collection.byExample({ foo : "bar", bar : "baz" }).toArray();
assertEqual(10, docs.length);
docs = collection.byExample({ meow : false }).toArray();
assertEqual(15, docs.length);
docs = collection.byExample({ value : 1 }).toArray();
assertEqual(50, docs.length);
// update some, remove old values
updated = collection.updateByExample({ value : 1 }, { value : null, fox : true }, false, false, 5);
assertEqual(5, updated);
assertEqual(50, collection.count());
docs = collection.byExample({ fox : true }).toArray();
assertEqual(5, docs.length);
docs = collection.byExample({ value : 1 }).toArray();
assertEqual(45, docs.length);
// update some, overwrite old values
updated = collection.updateByExample({ value : 1 }, { value : 16 }, false, false, 10);
assertEqual(10, updated);
assertEqual(50, collection.count());
docs = collection.byExample({ value : 16 }).toArray();
assertEqual(10, docs.length);
docs = collection.byExample({ fox : true }).toArray();
assertEqual(5, docs.length);
docs = collection.byExample({ value : 1 }).toArray();
assertEqual(35, docs.length);
// not existing documents
updated = collection.updateByExample({ meow : true }, { }, false, false, 99);
assertEqual(0, updated);
updated = collection.updateByExample({ value : null }, { }, false, false, 99);
assertEqual(0, updated);
collection.truncate();
updated = collection.updateByExample({ value : 1 }, { });
assertEqual(0, updated);
}
};
}

View File

@ -252,11 +252,20 @@ ArangoCollection.prototype.firstExample = function (example) {
/// @brief removes documents matching an example
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
ArangoCollection.prototype.removeByExample = function (example,
waitForSync,
limit) {
var deleted = 0;
var documents;
if (limit === 0) {
return 0;
}
documents = this.byExample(example);
if (limit > 0) {
documents = documents.limit(limit);
}
while (documents.hasNext()) {
var document = documents.next();
@ -269,6 +278,85 @@ ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
return deleted;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief replaces documents matching an example
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.replaceByExample = function (example,
newValue,
waitForSync,
limit) {
var replaced = 0;
var documents;
if (limit === 0) {
return 0;
}
if (typeof newValue !== "object") {
var err = new ArangoError();
err.errorNum = internal.errors.ERROR_BAD_PARAMETER.code;
err.errorMessage = "invalid value for parameter 'newValue'";
throw err;
}
documents = this.byExample(example);
if (limit > 0) {
documents = documents.limit(limit);
}
while (documents.hasNext()) {
var document = documents.next();
if (this.replace(document, newValue, true, waitForSync)) {
replaced++;
}
}
return replaced;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief partially updates documents matching an example
////////////////////////////////////////////////////////////////////////////////
ArangoCollection.prototype.updateByExample = function (example,
newValue,
keepNull,
waitForSync,
limit) {
var updated = 0;
var documents;
if (limit === 0) {
return 0;
}
if (typeof newValue !== "object") {
var err = new ArangoError();
err.errorNum = internal.errors.ERROR_BAD_PARAMETER.code;
err.errorMessage = "invalid value for parameter 'newValue'";
throw err;
}
documents = this.byExample(example);
if (limit > 0) {
documents = documents.limit(limit);
}
while (documents.hasNext()) {
var document = documents.next();
if (this.update(document, newValue, true, keepNull, waitForSync)) {
updated++;
}
}
return updated;
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -45,6 +45,7 @@
(function() {
var internal = require("internal");
var console = require("console");
var userManager = require("org/arangodb/users");
var db = internal.db;
// path to the VERSION file
@ -147,7 +148,7 @@
if (users.count() === 0) {
// only add account if user has not created his/her own accounts already
users.save({ user: "root", password: internal.encodePassword(""), active: true });
userManager.save("root", "", true);
}
return true;

View File

@ -452,6 +452,17 @@ void TRI_InitArrayJson (TRI_memory_zone_t* zone, TRI_json_t* result) {
TRI_InitVector(&result->_value._objects, zone, sizeof(TRI_json_t));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief initialises an array, using a specific initial size
////////////////////////////////////////////////////////////////////////////////
void TRI_Init2ArrayJson (TRI_memory_zone_t* zone,
TRI_json_t* result,
size_t initialSize) {
result->_type = TRI_JSON_ARRAY;
TRI_InitVector2(&result->_value._objects, zone, sizeof(TRI_json_t), initialSize);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroys a json object, but does not free the pointer
////////////////////////////////////////////////////////////////////////////////

View File

@ -173,6 +173,12 @@ TRI_json_t* TRI_CreateArrayJson (TRI_memory_zone_t*);
void TRI_InitArrayJson (TRI_memory_zone_t*, TRI_json_t*);
////////////////////////////////////////////////////////////////////////////////
/// @brief initialises an array, using a specific initial size
////////////////////////////////////////////////////////////////////////////////
void TRI_Init2ArrayJson (TRI_memory_zone_t*, TRI_json_t*, size_t);
////////////////////////////////////////////////////////////////////////////////
/// @brief destroys a json object, but does not free the pointer
////////////////////////////////////////////////////////////////////////////////

View File

@ -71,7 +71,6 @@ void TRI_InitVector (TRI_vector_t* vector, TRI_memory_zone_t* zone, size_t eleme
vector->_buffer = NULL;
vector->_length = 0;
vector->_capacity = 0;
vector->_growthFactor = GROW_FACTOR;
}
////////////////////////////////////////////////////////////////////////////////
@ -81,15 +80,10 @@ void TRI_InitVector (TRI_vector_t* vector, TRI_memory_zone_t* zone, size_t eleme
int TRI_InitVector2 (TRI_vector_t* vector,
TRI_memory_zone_t* zone,
size_t elementSize,
size_t initialCapacity,
double growthFactor) {
size_t initialCapacity) {
// init vector as usual
TRI_InitVector(vector, zone, elementSize);
if (growthFactor > 1.0) {
vector->_growthFactor = growthFactor;
}
if (initialCapacity != 0) {
vector->_buffer = (char*) TRI_Allocate(vector->_memoryZone, (initialCapacity * vector->_elementSize), false);
if (vector->_buffer == NULL) {
@ -252,7 +246,7 @@ int TRI_ResizeVector (TRI_vector_t* vector, size_t n) {
int TRI_PushBackVector (TRI_vector_t* vector, void const* element) {
if (vector->_length == vector->_capacity) {
char* newBuffer;
size_t newSize = (size_t) (1 + (vector->_growthFactor * vector->_capacity));
size_t newSize = (size_t) (1 + (GROW_FACTOR * vector->_capacity));
newBuffer = (char*) TRI_Reallocate(vector->_memoryZone, vector->_buffer, newSize * vector->_elementSize);
@ -312,7 +306,7 @@ void TRI_InsertVector (TRI_vector_t* vector, void const* element, size_t positio
// ...........................................................................
if (vector->_length >= vector->_capacity || position >= vector->_length) {
size_t newSize = (size_t) (1 + (vector->_growthFactor * vector->_capacity));
size_t newSize = (size_t) (1 + (GROW_FACTOR * vector->_capacity));
if (position >= newSize) {
newSize = position + 1;

View File

@ -57,7 +57,6 @@ typedef struct TRI_vector_s {
char * _buffer;
size_t _length;
size_t _capacity;
double _growthFactor;
}
TRI_vector_t;
@ -87,8 +86,7 @@ void TRI_InitVector (TRI_vector_t*, TRI_memory_zone_t*, size_t elementSize);
int TRI_InitVector2 (TRI_vector_t*,
TRI_memory_zone_t*,
size_t elementSize,
size_t initialCapacity,
double growthFactor);
size_t initialCapacity);
////////////////////////////////////////////////////////////////////////////////
/// @brief destroys a vector, but does not free the pointer

View File

@ -89,7 +89,6 @@ typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
typedef uint64_t flex_uint64_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
@ -213,11 +212,6 @@ typedef void* yyscan_t;
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#define EOB_ACT_CONTINUE_SCAN 0
#define EOB_ACT_END_OF_FILE 1
#define EOB_ACT_LAST_MATCH 2
@ -240,6 +234,11 @@ typedef size_t yy_size_t;
#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#ifndef YY_STRUCT_YY_BUFFER_STATE
#define YY_STRUCT_YY_BUFFER_STATE
struct yy_buffer_state
@ -257,7 +256,7 @@ struct yy_buffer_state
/* Number of characters read into yy_ch_buf, not including EOB
* characters.
*/
yy_size_t yy_n_chars;
int yy_n_chars;
/* Whether we "own" the buffer - i.e., we know we created it,
* and can realloc() it to grow it, and should free() it to
@ -336,7 +335,7 @@ static void tri_jsp__init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscann
YY_BUFFER_STATE tri_jsp__scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
YY_BUFFER_STATE tri_jsp__scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
void *tri_jsp_alloc (yy_size_t ,yyscan_t yyscanner );
void *tri_jsp_realloc (void *,yy_size_t ,yyscan_t yyscanner );
@ -387,7 +386,7 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
*/
#define YY_DO_BEFORE_ACTION \
yyg->yytext_ptr = yy_bp; \
yyleng = (yy_size_t) (yy_cp - yy_bp); \
yyleng = (size_t) (yy_cp - yy_bp); \
yyg->yy_hold_char = *yy_cp; \
*yy_cp = '\0'; \
yyg->yy_c_buf_p = yy_cp;
@ -534,8 +533,8 @@ static yyconst flex_int16_t yy_chk[152] =
#define STRING_CONSTANT_ASCII 13
struct jsonData {
char const* _message;
TRI_memory_zone_t* _memoryZone;
char const* _message;
};
#define YY_FATAL_ERROR(a) \
@ -566,8 +565,8 @@ struct yyguts_t
size_t yy_buffer_stack_max; /**< capacity of stack. */
YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
char yy_hold_char;
yy_size_t yy_n_chars;
yy_size_t yyleng_r;
int yy_n_chars;
int yyleng_r;
char *yy_c_buf_p;
int yy_init;
int yy_start;
@ -614,7 +613,7 @@ FILE *tri_jsp_get_out (yyscan_t yyscanner );
void tri_jsp_set_out (FILE * out_str ,yyscan_t yyscanner );
yy_size_t tri_jsp_get_leng (yyscan_t yyscanner );
int tri_jsp_get_leng (yyscan_t yyscanner );
char *tri_jsp_get_text (yyscan_t yyscanner );
@ -673,7 +672,7 @@ static int input (yyscan_t yyscanner );
if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
{ \
int c = '*'; \
yy_size_t n; \
int n; \
for ( n = 0; n < max_size && \
(c = getc( yyin )) != EOF && c != '\n'; ++n ) \
buf[n] = (char) c; \
@ -1129,7 +1128,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
else
{
yy_size_t num_to_read =
int num_to_read =
YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
while ( num_to_read <= 0 )
@ -1143,7 +1142,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
if ( b->yy_is_our_buffer )
{
yy_size_t new_size = b->yy_buf_size * 2;
int new_size = b->yy_buf_size * 2;
if ( new_size <= 0 )
b->yy_buf_size += b->yy_buf_size / 8;
@ -1174,7 +1173,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
/* Read in more data. */
YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
yyg->yy_n_chars, num_to_read );
yyg->yy_n_chars, (int) num_to_read );
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
}
@ -1299,7 +1298,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
else
{ /* need more input */
yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
++yyg->yy_c_buf_p;
switch ( yy_get_next_buffer( yyscanner ) )
@ -1323,7 +1322,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
case EOB_ACT_END_OF_FILE:
{
if ( tri_jsp_wrap(yyscanner ) )
return 0;
return EOF;
if ( ! yyg->yy_did_buffer_switch_on_eof )
YY_NEW_FILE;
@ -1585,7 +1584,7 @@ void tri_jsp_pop_buffer_state (yyscan_t yyscanner)
*/
static void tri_jsp_ensure_buffer_stack (yyscan_t yyscanner)
{
yy_size_t num_to_alloc;
int num_to_alloc;
struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
if (!yyg->yy_buffer_stack) {
@ -1683,11 +1682,12 @@ YY_BUFFER_STATE tri_jsp__scan_string (yyconst char * yystr , yyscan_t yyscanner)
* @param yyscanner The scanner object.
* @return the newly allocated buffer state object.
*/
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner)
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
{
YY_BUFFER_STATE b;
char *buf;
yy_size_t n, i;
yy_size_t n;
int i;
/* Get memory for full buffer, including space for trailing EOB's. */
n = _yybytes_len + 2;
@ -1797,7 +1797,7 @@ FILE *tri_jsp_get_out (yyscan_t yyscanner)
/** Get the length of the current token.
* @param yyscanner The scanner object.
*/
yy_size_t tri_jsp_get_leng (yyscan_t yyscanner)
int tri_jsp_get_leng (yyscan_t yyscanner)
{
struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
return yyleng;

View File

@ -70,8 +70,8 @@ PLUS [+]
#define STRING_CONSTANT_ASCII 13
struct jsonData {
char const* _message;
TRI_memory_zone_t* _memoryZone;
char const* _message;
};
#define YY_FATAL_ERROR(a) \

View File

@ -31,6 +31,7 @@
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <Basics/RandomGenerator.h>
#include <Basics/StringUtils.h>
@ -130,7 +131,6 @@ namespace triagens {
}
void sslSHA256 (char const* inputStr, char*& outputStr, size_t& outputLen) {
sslSHA256(inputStr, strlen(inputStr), outputStr, outputLen);
}
@ -180,9 +180,8 @@ namespace triagens {
void sslBASE64 (char const* inputStr, char*& outputStr, size_t& outputLen) {
sslBASE64(inputStr, strlen(inputStr), outputStr, outputLen);
}
string sslHMAC (char const* key, char const* message, size_t messageLen) {
const EVP_MD * evp_md = EVP_sha256();
unsigned char* md = (unsigned char*) TRI_SystemAllocate(EVP_MAX_MD_SIZE + 1, false);
@ -212,7 +211,30 @@ namespace triagens {
return false;
}
int sslRand (uint64_t* value) {
if (! RAND_pseudo_bytes((unsigned char *) value, sizeof(uint64_t))) {
return 1;
}
return 0;
}
int sslRand (int64_t* value) {
if (! RAND_pseudo_bytes((unsigned char *) value, sizeof(int64_t))) {
return 1;
}
return 0;
}
int sslRand (int32_t* value) {
if (! RAND_pseudo_bytes((unsigned char *) value, sizeof(int32_t))) {
return 1;
}
return 0;
}
void salt64 (uint64_t& result) {
string salt = SaltGenerator.random(8);

View File

@ -99,12 +99,12 @@ namespace triagens {
//////////////////////////////////////////////////////////////////////////
void sslHEX (char const* inputStr, char*& outputStr, size_t& outputLen);
//////////////////////////////////////////////////////////////////////////
/// @brief BASE64
//////////////////////////////////////////////////////////////////////////
void sslBAE64 (char const* inputStr, const size_t length, char*& outputStr, size_t& outputLen);
void sslBASE64 (char const* inputStr, const size_t length, char*& outputStr, size_t& outputLen);
//////////////////////////////////////////////////////////////////////////
/// @brief BASE64
@ -123,12 +123,36 @@ namespace triagens {
//////////////////////////////////////////////////////////////////////////
bool verifyHMAC (char const* challenge, char const* secret, size_t secretLen, char const* response, size_t responseLen);
//////////////////////////////////////////////////////////////////////////
/// @brief generate a random number using OpenSsl
///
/// will return 0 on success, and != 0 on error
//////////////////////////////////////////////////////////////////////////
int sslRand (uint64_t*);
//////////////////////////////////////////////////////////////////////////
/// @brief generate a random number using OpenSsl
///
/// will return 0 on success, and != 0 on error
//////////////////////////////////////////////////////////////////////////
int sslRand (int64_t*);
//////////////////////////////////////////////////////////////////////////
/// @brief generate a random number using OpenSsl
///
/// will return 0 on success, and != 0 on error
//////////////////////////////////////////////////////////////////////////
int sslRand (int32_t*);
//////////////////////////////////////////////////////////////////////////
/// @brief salt
//////////////////////////////////////////////////////////////////////////
void salt64 (uint64_t& saltInt);
void salt64 (uint64_t& saltInt);
//////////////////////////////////////////////////////////////////////////
/// @brief salt

View File

@ -619,6 +619,46 @@ static v8::Handle<v8::Value> JS_LogLevel (v8::Arguments const& argv) {
return scope.Close(v8::String::New(TRI_LogLevelLogging()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief md5 sum
///
/// @FUN{internal.md5(@FA{text})}
///
/// Computes an md5 for the @FA{text}.
////////////////////////////////////////////////////////////////////////////////
static v8::Handle<v8::Value> JS_Md5 (v8::Arguments const& argv) {
v8::HandleScope scope;
// extract arguments
if (argv.Length() != 1 || ! argv[0]->IsString()) {
return scope.Close(v8::ThrowException(v8::String::New("usage: md5(<text>)")));
}
string key = TRI_ObjectToString(argv[0]);
// create md5
char* hash = 0;
size_t hashLen;
SslInterface::sslMD5(key.c_str(), key.size(), hash, hashLen);
// as hex
char* hex = 0;
size_t hexLen;
SslInterface::sslHEX(hash, hashLen, hex, hexLen);
delete[] hash;
// and return
v8::Handle<v8::String> hashStr = v8::String::New(hex, hexLen);
delete[] hex;
return scope.Close(hashStr);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief renames a file
///
@ -742,6 +782,46 @@ static v8::Handle<v8::Value> JS_ProcessStat (v8::Arguments const& argv) {
return scope.Close(result);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate a random number using OpenSSL
///
/// @FUN{internal.rand()}
///
/// Generates a random number
////////////////////////////////////////////////////////////////////////////////
static v8::Handle<v8::Value> JS_Rand (v8::Arguments const& argv) {
v8::HandleScope scope;
// check arguments
if (argv.Length() != 0) {
return scope.Close(v8::ThrowException(v8::String::New("usage: rand()")));
}
int iterations = 0;
while (iterations++ < 5) {
int32_t value;
int result = SslInterface::sslRand(&value);
if (result != 0) {
// error
break;
}
// no error, now check what random number was produced
if (value != 0) {
// a number != 0 was produced. that is sufficient
return scope.Close(v8::Number::New(value));
}
// we don't want to return 0 as the result, so we try again
}
// we failed to produce a valid random number
return scope.Close(v8::Undefined());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief reads in a file
///
@ -962,14 +1042,14 @@ static v8::Handle<v8::Value> JS_SPrintF (v8::Arguments const& argv) {
///
/// @FUN{internal.sha256(@FA{text})}
///
/// Computes a sha256 for the @FA{text}.
/// Computes an sha256 for the @FA{text}.
////////////////////////////////////////////////////////////////////////////////
static v8::Handle<v8::Value> JS_Sha256 (v8::Arguments const& argv) {
v8::HandleScope scope;
// extract arguments
if (argv.Length() != 1) {
if (argv.Length() != 1 || ! argv[0]->IsString()) {
return scope.Close(v8::ThrowException(v8::String::New("usage: sha256(<text>)")));
}
@ -1398,9 +1478,11 @@ void TRI_InitV8Utils (v8::Handle<v8::Context> context, string const& path) {
TRI_AddGlobalFunctionVocbase(context, "SYS_LOAD", JS_Load);
TRI_AddGlobalFunctionVocbase(context, "SYS_LOG", JS_Log);
TRI_AddGlobalFunctionVocbase(context, "SYS_LOG_LEVEL", JS_LogLevel);
TRI_AddGlobalFunctionVocbase(context, "SYS_MD5", JS_Md5);
TRI_AddGlobalFunctionVocbase(context, "SYS_OUTPUT", JS_Output);
TRI_AddGlobalFunctionVocbase(context, "SYS_PARSE", JS_Parse);
TRI_AddGlobalFunctionVocbase(context, "SYS_PROCESS_STAT", JS_ProcessStat);
TRI_AddGlobalFunctionVocbase(context, "SYS_RAND", JS_Rand);
TRI_AddGlobalFunctionVocbase(context, "SYS_READ", JS_Read);
TRI_AddGlobalFunctionVocbase(context, "SYS_SAVE", JS_Save);
TRI_AddGlobalFunctionVocbase(context, "SYS_SHA256", JS_Sha256);