mirror of https://gitee.com/bigwinds/arangodb
added changePassword attribute for users
This commit is contained in:
parent
cc533c5df5
commit
d34832bbcf
|
@ -27,6 +27,8 @@ v2.1.0 (XXXX-XX-XX)
|
|||
v2.0.1 (XXXX-XX-XX)
|
||||
-------------------
|
||||
|
||||
* added `changePassword` attribute for users
|
||||
|
||||
* fixed non-working "save" button in collection edit view of web interface
|
||||
clicking the save button did nothing. one had to press enter in one of the input
|
||||
fields to send modified form data
|
||||
|
|
|
@ -29,39 +29,162 @@ Command-Line Options for the Authentication and Authorisation{#DbaManualAuthenti
|
|||
Introduction to User Management{#UserManagementIntro}
|
||||
=====================================================
|
||||
|
||||
ArangoDB provides basic functionality to add, modify and remove
|
||||
database users programmatically. The following functionality is
|
||||
provided by the `users` module and can be used from inside arangosh
|
||||
and arangod.
|
||||
ArangoDB provides basic functionality to add, modify and remove database users
|
||||
programmatically. The following functionality is provided by the `users` module
|
||||
and can be used from inside arangosh and arangod.
|
||||
|
||||
Please note that this functionality is not available from within the
|
||||
web interface.
|
||||
Please note that this functionality is not available from within the web
|
||||
interface.
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@anchor UserManagementSave
|
||||
@copydetails JSF_saveUser
|
||||
|
||||
@FUN{users.save(@FA{user}, @FA{passwd}, @FA{active}, @FA{extra}, @FA{changePassword})}
|
||||
|
||||
This will create a new ArangoDB user. The username must be specified in
|
||||
@FA{user} 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 `true`. The
|
||||
@FA{extra} attribute can be used to save custom data with the user.
|
||||
|
||||
If the @FA{changePassword} attribute is not specified, it defaults to `false`.
|
||||
The @FA{changePassword} attribute can be used to indicate that the user must
|
||||
change has password before logging in.
|
||||
|
||||
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 the specified
|
||||
name.
|
||||
|
||||
The new user account can only be used after the server is either restarted or
|
||||
the server authentication cache is @ref UserManagementReload "reloaded".
|
||||
|
||||
Note: this function will not work from within the web interface
|
||||
|
||||
@EXAMPLES
|
||||
|
||||
arangosh> require("org/arangodb/users").save("my-user", "my-secret-password");
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementDocument
|
||||
@copydetails JSF_documentUser
|
||||
@FUN{users.document(@FA{user})}
|
||||
|
||||
Fetches an existing ArangoDB user from the database.
|
||||
|
||||
The username must be specified in @FA{user}.
|
||||
|
||||
This method will fail if the user cannot be found in the database.
|
||||
|
||||
Note: this function will not work from within the web interface
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementReplace
|
||||
@copydetails JSF_replaceUser
|
||||
@FUN{users.replace(@FA{user}, @FA{passwd}, @FA{active}, @FA{extra}, @FA{changePassword})}
|
||||
|
||||
This will look up an existing ArangoDB user and replace its user data.
|
||||
|
||||
The username must be specified in @FA{user}, 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 `true`. The
|
||||
@FA{extra} attribute can be used to save custom data with the user.
|
||||
|
||||
If the @FA{changePassword} attribute is not specified, it defaults to `false`.
|
||||
The @FA{changePassword} attribute can be used to indicate that the user must
|
||||
change has password before logging in.
|
||||
|
||||
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.
|
||||
|
||||
Note: this function will not work from within the web interface
|
||||
|
||||
@EXAMPLES
|
||||
|
||||
arangosh> require("org/arangodb/users").replace("my-user", "my-changed-password");
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementUpdate
|
||||
@copydetails JSF_updateUser
|
||||
@FUN{@FA{users}.update(@FA{user}, @FA{passwd}, @FA{active}, @FA{extra}, @FA{changePassword})}
|
||||
|
||||
This will update an existing ArangoDB user with a new password and other data.
|
||||
|
||||
The username must be specified in @FA{user} 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} and the
|
||||
@FA{changePassword} 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.
|
||||
|
||||
Note: this function will not work from within the web interface
|
||||
|
||||
@EXAMPLES
|
||||
|
||||
arangosh> require("org/arangodb/users").update("my-user", "my-secret-password");
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementRemove
|
||||
@copydetails JSF_removeUser
|
||||
@FUN{users.remove(@FA{user})}
|
||||
|
||||
Removes an existing ArangoDB user from the database.
|
||||
|
||||
The username must be specified in @FA{user} and the specified user must exist in
|
||||
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
|
||||
|
||||
@EXAMPLES
|
||||
|
||||
arangosh> require("org/arangodb/users").remove("my-user");
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementReload
|
||||
@copydetails JSF_reloadUsers
|
||||
@FUN{users.reload()}
|
||||
|
||||
Reloads the user authentication data on the server
|
||||
|
||||
All user authentication data is loaded by the server once on startup only and is
|
||||
cached after that. When users get added or deleted, a cache flush is required,
|
||||
and this can be performed by called this method.
|
||||
|
||||
Note: this function will not work from within the web interface
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementIsValid
|
||||
@copydetails JSF_isValidUsers
|
||||
@FUN{users.isvalid(@FA{user}, @FA{password})}
|
||||
|
||||
Checks whether the given combination of username and password is valid. The
|
||||
function will return a boolean value if the combination of username and password
|
||||
is valid.
|
||||
|
||||
Each call to this function is penalized by the server sleeping a random
|
||||
amount of time.
|
||||
|
||||
Note: this function will not work from within the web interface
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementAll
|
||||
@FUN{users.all()}
|
||||
|
||||
Fetches all existing ArangoDB users from the database.
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@BNAVIGATE_DbaManualAuthentication
|
||||
|
|
|
@ -8,6 +8,8 @@ TOC {#DbaManualAuthenticationTOC}
|
|||
- @ref UserManagementSave "users.save"
|
||||
- @ref UserManagementDocument "users.document"
|
||||
- @ref UserManagementReplace "users.replace"
|
||||
- @ref UserManagementUpdate "users.update"
|
||||
- @ref UserManagementRemove "users.remove"
|
||||
- @ref UserManagementReload "users.reload"
|
||||
- @ref UserManagementIsValid "users.isValid"
|
||||
- @ref UserManagementAll "users.all"
|
||||
|
|
|
@ -1,44 +1,300 @@
|
|||
HTTP Interface for User Management {#HttpUser}
|
||||
==============================================
|
||||
HTTP Interface for User Management{#HttpUser}
|
||||
=============================================
|
||||
|
||||
@NAVIGATE_HttpUser
|
||||
@EMBEDTOC{HttpUserTOC}
|
||||
|
||||
User Management {#HttpUserIntro}
|
||||
================================
|
||||
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`.
|
||||
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.
|
||||
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 privileges and security security checks may be introduced in the
|
||||
Operations on users may become more restricted than regular document operations,
|
||||
and extra privileges and security security checks may be introduced in the
|
||||
future for this interface.
|
||||
|
||||
Please note that user operations are not included in ArangoDB's replication.
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@anchor HttpUserSave
|
||||
@copydetails JSF_post_api_user
|
||||
@RESTHEADER{POST /_api/user,creates user}
|
||||
|
||||
@RESTBODYPARAM{body,json,required}
|
||||
|
||||
@RESTDESCRIPTION
|
||||
|
||||
The following data need to be passed in a JSON representation in the body of the
|
||||
POST request:
|
||||
|
||||
- `user`: The name of the user as a string. This is mandatory.
|
||||
|
||||
- `passwd`: The user password as a string. If no password is specified, the
|
||||
empty string will be used.
|
||||
|
||||
- `active`: an optional flag that specifies whether the user is active. If not
|
||||
specified, this will default to `true`.
|
||||
|
||||
- `extra`: an optional JSON object with arbitrary extra data about the user.
|
||||
|
||||
- `changePassword`: an optional flag that specifies whethers the user must
|
||||
change the password or not. If not specified, this will default to `false`.
|
||||
If set to `true`, the only operations allowed are `PUT /_api/user` or
|
||||
`PATCH /_api/user`. All other operations will result in a `HTTP 403`.
|
||||
|
||||
If the user can be added by the server, the server will respond with `HTTP 201`.
|
||||
|
||||
In case of success, the returned JSON object has the following properties:
|
||||
|
||||
- `error`: boolean flag to indicate that an error occurred (`false` in this
|
||||
case)
|
||||
|
||||
- `code`: the HTTP status code
|
||||
|
||||
If the JSON representation is malformed or mandatory data is missing from the
|
||||
request, the server will respond with `HTTP 400`.
|
||||
|
||||
The body of the response will contain a JSON object with additional error
|
||||
details. The object has the following attributes:
|
||||
|
||||
- `error`: boolean flag to indicate that an error occurred (`true` in this case)
|
||||
|
||||
- `code`: the HTTP status code
|
||||
|
||||
- `errorNum`: the server error number
|
||||
|
||||
- `errorMessage`: a descriptive error message
|
||||
|
||||
@RESTRETURNCODES
|
||||
|
||||
@RESTRETURNCODE{201}
|
||||
returned if the user can be added by the server.
|
||||
|
||||
@RESTRETURNCODE{400}
|
||||
If the JSON representation is malformed or mandatory data is missing from the
|
||||
request.
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor HttpUserReplace
|
||||
@copydetails JSF_put_api_user
|
||||
@RESTHEADER{PUT /_api/user/{user},replaces user}
|
||||
|
||||
@RESTURLPARAMETERS
|
||||
|
||||
@RESTURLPARAM{user,string,required}
|
||||
The name of the user.
|
||||
|
||||
@RESTBODYPARAM{body,json,required}
|
||||
|
||||
@RESTDESCRIPTION
|
||||
|
||||
Replaces the data of an existing user. The name of an existing user must be
|
||||
specified in `user`.
|
||||
|
||||
The following data can to be passed in a JSON representation in the body of the
|
||||
POST request:
|
||||
|
||||
- `passwd`: The user password as a string. Specifying a password is mandatory,
|
||||
but the empty string is allowed for passwords.
|
||||
|
||||
- `active`: an optional flag that specifies whether the user is active. If not
|
||||
specified, this will default to `true`.
|
||||
|
||||
- `extra`: an optional JSON object with arbitrary extra data about the user.
|
||||
|
||||
- `changePassword`: an optional flag that specifies whether the user must change
|
||||
the password or not. If not specified, this will default to `false`.
|
||||
|
||||
If the user can be replaced by the server, the server will respond with `HTTP
|
||||
200`.
|
||||
|
||||
In case of success, the returned JSON object has the following properties:
|
||||
|
||||
- `error`: boolean flag to indicate that an error occurred (`false` in this
|
||||
case)
|
||||
|
||||
- `code`: the HTTP status code
|
||||
|
||||
If the JSON representation is malformed or mandatory data is missing from the
|
||||
request, the server will respond with `HTTP 400`. If the specified user does not
|
||||
exist, the server will respond with `HTTP 404`.
|
||||
|
||||
The body of the response will contain a JSON object with additional error
|
||||
details. The object has the following attributes:
|
||||
|
||||
- `error`: boolean flag to indicate that an error occurred (`true` in this case)
|
||||
|
||||
- `code`: the HTTP status code
|
||||
|
||||
- `errorNum`: the server error number
|
||||
|
||||
- `errorMessage`: a descriptive error message
|
||||
|
||||
@RESTRETURNCODES
|
||||
|
||||
@RESTRETURNCODE{200}
|
||||
Is returned if the user data can be replaced by the server.
|
||||
|
||||
@RESTRETURNCODE{400}
|
||||
The JSON representation is malformed or mandatory data is missing from the
|
||||
request.
|
||||
|
||||
@RESTRETURNCODE{404}
|
||||
The specified user does not exist.
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor HttpUserUpdate
|
||||
@copydetails JSF_patch_api_user
|
||||
@RESTHEADER{PATCH /_api/user/{user},updates user}
|
||||
|
||||
@RESTURLPARAMETERS
|
||||
|
||||
@RESTURLPARAM{user,string,required}
|
||||
The name of the user.
|
||||
|
||||
@RESTBODYPARAM{body,json,required}
|
||||
|
||||
@RESTDESCRIPTION
|
||||
|
||||
Partially updates the data of an existing user. The name of an existing user
|
||||
must be specified in `user`.
|
||||
|
||||
The following data can be passed in a JSON representation in the body of the
|
||||
POST request:
|
||||
|
||||
- `passwd`: The user password as a string. Specifying a password is optional. If
|
||||
not specified, the previously existing value will not be modified.
|
||||
|
||||
- `active`: an optional flag that specifies whether the user is active. If not
|
||||
specified, the previously existing value will not be modified.
|
||||
|
||||
- `extra`: an optional JSON object with arbitrary extra data about the user. If
|
||||
not specified, the previously existing value will not be modified.
|
||||
|
||||
- `changePassword`: an optional flag that specifies whether the user must change
|
||||
the password or not. 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 `HTTP
|
||||
200`.
|
||||
|
||||
In case of success, the returned JSON object has the following properties:
|
||||
|
||||
- `error`: boolean flag to indicate that an error occurred (`false` in this
|
||||
case)
|
||||
|
||||
- `code`: the HTTP status code
|
||||
|
||||
If the JSON representation is malformed or mandatory data is missing from the
|
||||
request, the server will respond with `HTTP 400`. If the specified user does not
|
||||
exist, the server will respond with `HTTP 404`.
|
||||
|
||||
The body of the response will contain a JSON object with additional error
|
||||
details. The object has the following attributes:
|
||||
|
||||
- `error`: boolean flag to indicate that an error occurred (`true` in this case)
|
||||
|
||||
- `code`: the HTTP status code
|
||||
|
||||
- `errorNum`: the server error number
|
||||
|
||||
- `errorMessage`: a descriptive error message
|
||||
|
||||
@RESTRETURNCODES
|
||||
|
||||
@RESTRETURNCODE{200}
|
||||
Is returned if the user data can be replaced by the server.
|
||||
|
||||
@RESTRETURNCODE{400}
|
||||
The JSON representation is malformed or mandatory data is missing from the
|
||||
request.
|
||||
|
||||
@RESTRETURNCODE{404}
|
||||
The specified user does not exist.
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor HttpUserRemove
|
||||
@copydetails JSF_delete_api_user
|
||||
@RESTHEADER{DELETE /_api/user/{user},removes a user}
|
||||
|
||||
@RESTURLPARAMETERS
|
||||
|
||||
@RESTURLPARAM{user,string,required}
|
||||
The name of the user.
|
||||
|
||||
@RESTDESCRIPTION
|
||||
|
||||
Removes an existing user, identified by `user`.
|
||||
|
||||
If the user can be removed, the server will respond with `HTTP 202`.
|
||||
|
||||
In case of success, the returned JSON object has the following properties:
|
||||
|
||||
- `error`: boolean flag to indicate that an error occurred (`false` in this
|
||||
case)
|
||||
|
||||
- `code`: the HTTP status code
|
||||
|
||||
If the specified user does not exist, the server will respond with `HTTP 404`.
|
||||
|
||||
The body of the response will contain a JSON object with additional error
|
||||
details. The object has the following attributes:
|
||||
|
||||
- `error`: boolean flag to indicate that an error occurred (`true` in this case)
|
||||
|
||||
- `code`: the HTTP status code
|
||||
|
||||
- `errorNum`: the server error number
|
||||
|
||||
- `errorMessage`: a descriptive error message
|
||||
|
||||
@RESTRETURNCODES
|
||||
|
||||
@RESTRETURNCODE{202}
|
||||
Is returned if the user was removed by the server.
|
||||
|
||||
@RESTRETURNCODE{404}
|
||||
The specified user does not exist.
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@CLEARPAGE
|
||||
@anchor HttpUserDocument
|
||||
@copydetails JSF_get_api_user
|
||||
@RESTHEADER{GET /_api/user/{user},fetches a user}
|
||||
|
||||
@RESTURLPARAMETERS
|
||||
|
||||
@RESTURLPARAM{user,string,required}
|
||||
The name of the user.
|
||||
|
||||
@RESTDESCRIPTION
|
||||
|
||||
Fetches data about the specified user.
|
||||
|
||||
The call will return a JSON document with at least the following attributes on
|
||||
success:
|
||||
|
||||
- `user`: The name of the user as a string.
|
||||
|
||||
- `active`: an optional flag that specifies whether the user is active.
|
||||
|
||||
- `extra`: an optional JSON object with arbitrary extra data about the user.
|
||||
|
||||
- `changePassword`: an optional flag that specifies whether the user must change
|
||||
the password or not.
|
||||
|
||||
@RESTRETURNCODES
|
||||
|
||||
@RESTRETURNCODE{200}
|
||||
The user was found.
|
||||
|
||||
@RESTRETURNCODE{404}
|
||||
The user with `user` does not exist.
|
||||
|
||||
@COMMENT{######################################################################}
|
||||
@BNAVIGATE_HttpUser
|
||||
|
|
|
@ -270,6 +270,7 @@ ALIASES += \
|
|||
# other aliases
|
||||
ALIASES += \
|
||||
"VERSION=@PACKAGE_VERSION@" \
|
||||
"COMMENT{1}=" \
|
||||
"LIT{1}=<tt>\1</tt>" \
|
||||
"LIT{2}=<tt>\1, \2</tt>" \
|
||||
"LIT{3}=<tt>\1, \2, \3</tt>" \
|
||||
|
|
|
@ -42,18 +42,13 @@ using namespace triagens::arango;
|
|||
using namespace triagens::rest;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- class ArangoServer
|
||||
// --SECTION-- class VocbaseContext
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- constructors and destructors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup ArangoDB
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief constructor
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -77,19 +72,10 @@ VocbaseContext::~VocbaseContext () {
|
|||
TRI_ReleaseVocBase(_vocbase);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup ArangoDB
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not to use special cluster authentication
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -175,6 +161,7 @@ HttpResponse::HttpResponseCode VocbaseContext::authenticate () {
|
|||
|
||||
string const up = StringUtils::decodeBase64(auth);
|
||||
std::string::size_type n = up.find(':', 0);
|
||||
|
||||
if (n == std::string::npos || n == 0 || n + 1 > up.size()) {
|
||||
LOG_TRACE("invalid authentication data found, cannot extract username/password");
|
||||
|
||||
|
@ -188,50 +175,54 @@ HttpResponse::HttpResponseCode VocbaseContext::authenticate () {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
// look up the info in the cache first
|
||||
char* cached = TRI_CheckCacheAuthInfo(_vocbase, auth);
|
||||
|
||||
bool mustChange;
|
||||
char* cached = TRI_CheckCacheAuthInfo(_vocbase, auth, &mustChange);
|
||||
string username;
|
||||
|
||||
// found a cached entry, access must be granted
|
||||
if (cached != 0) {
|
||||
// found a cached entry, access must be granted
|
||||
_request->setUser(string(cached));
|
||||
username = string(cached);
|
||||
TRI_Free(TRI_CORE_MEM_ZONE, cached);
|
||||
|
||||
return HttpResponse::OK;
|
||||
}
|
||||
|
||||
// no entry found in cache, decode the basic auth info and look it up
|
||||
else {
|
||||
string const up = StringUtils::decodeBase64(auth);
|
||||
std::string::size_type n = up.find(':', 0);
|
||||
|
||||
string const up = StringUtils::decodeBase64(auth);
|
||||
|
||||
std::string::size_type n = up.find(':', 0);
|
||||
if (n == std::string::npos || n == 0 || n + 1 > up.size()) {
|
||||
LOG_TRACE("invalid authentication data found, cannot extract username/password");
|
||||
|
||||
return HttpResponse::BAD;
|
||||
}
|
||||
if (n == std::string::npos || n == 0 || n + 1 > up.size()) {
|
||||
LOG_TRACE("invalid authentication data found, cannot extract username/password");
|
||||
return HttpResponse::BAD;
|
||||
}
|
||||
|
||||
string const username = up.substr(0, n);
|
||||
username = up.substr(0, n);
|
||||
|
||||
LOG_TRACE("checking authentication for user '%s'", username.c_str());
|
||||
|
||||
bool res = TRI_CheckAuthenticationAuthInfo(_vocbase, auth, username.c_str(), up.substr(n + 1).c_str());
|
||||
LOG_TRACE("checking authentication for user '%s'", username.c_str());
|
||||
bool res = TRI_CheckAuthenticationAuthInfo(
|
||||
_vocbase, auth, username.c_str(), up.substr(n + 1).c_str(), &mustChange);
|
||||
|
||||
if (! res) {
|
||||
return HttpResponse::UNAUTHORIZED;
|
||||
if (! res) {
|
||||
return HttpResponse::UNAUTHORIZED;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: create a user object for the VocbaseContext
|
||||
_request->setUser(username);
|
||||
|
||||
if (mustChange) {
|
||||
if ((_request->requestType() == HttpRequest::HTTP_REQUEST_PUT
|
||||
|| _request->requestType() == HttpRequest::HTTP_REQUEST_PATCH)
|
||||
&& TRI_EqualString2(_request->requestPath(), "/_api/user/", 11)) {
|
||||
return HttpResponse::OK;
|
||||
}
|
||||
|
||||
return HttpResponse::FORBIDDEN;
|
||||
}
|
||||
|
||||
return HttpResponse::OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -40,11 +40,6 @@
|
|||
// --SECTION-- private functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup VocBase
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief hashes a string
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -242,6 +237,7 @@ static TRI_vocbase_auth_t* ConvertAuthInfo (TRI_vocbase_t* vocbase,
|
|||
char* password;
|
||||
bool active;
|
||||
bool found;
|
||||
bool mustChange;
|
||||
TRI_vocbase_auth_t* result;
|
||||
|
||||
shaper = primary->_shaper;
|
||||
|
@ -273,6 +269,13 @@ static TRI_vocbase_auth_t* ConvertAuthInfo (TRI_vocbase_t* vocbase,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// extract must-change-password flag
|
||||
mustChange = ExtractBooleanShapedJson(shaper, document, "changePassword", &found);
|
||||
|
||||
if (! found) {
|
||||
mustChange = false;
|
||||
}
|
||||
|
||||
result = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_vocbase_auth_t), true);
|
||||
|
||||
if (result == NULL) {
|
||||
|
@ -286,13 +289,15 @@ static TRI_vocbase_auth_t* ConvertAuthInfo (TRI_vocbase_t* vocbase,
|
|||
result->_username = user;
|
||||
result->_password = password;
|
||||
result->_active = active;
|
||||
result->_mustChange = mustChange;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief clears the authentication info
|
||||
/// the caller must acquire the lock itself
|
||||
///
|
||||
/// @note the caller must acquire the lock itself
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void ClearAuthInfo (TRI_vocbase_t* vocbase) {
|
||||
|
@ -333,19 +338,10 @@ static void ClearAuthInfo (TRI_vocbase_t* vocbase) {
|
|||
vocbase->_authCache._nrUsed = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup VocBase
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief initialises the authentication info
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -596,7 +592,8 @@ void TRI_ClearAuthInfo (TRI_vocbase_t* vocbase) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
char* TRI_CheckCacheAuthInfo (TRI_vocbase_t* vocbase,
|
||||
char const* hash) {
|
||||
char const* hash,
|
||||
bool* mustChange) {
|
||||
TRI_vocbase_auth_cache_t* cached;
|
||||
char* username;
|
||||
|
||||
|
@ -607,6 +604,7 @@ char* TRI_CheckCacheAuthInfo (TRI_vocbase_t* vocbase,
|
|||
|
||||
if (cached != NULL) {
|
||||
username = TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, cached->_username);
|
||||
*mustChange = cached->_mustChange;
|
||||
}
|
||||
|
||||
TRI_ReadUnlockReadWriteLock(&vocbase->_authInfoLock);
|
||||
|
@ -622,7 +620,8 @@ char* TRI_CheckCacheAuthInfo (TRI_vocbase_t* vocbase,
|
|||
bool TRI_CheckAuthenticationAuthInfo (TRI_vocbase_t* vocbase,
|
||||
char const* hash,
|
||||
char const* username,
|
||||
char const* password) {
|
||||
char const* password,
|
||||
bool* mustChange) {
|
||||
TRI_vocbase_auth_t* auth;
|
||||
bool res;
|
||||
char* hex;
|
||||
|
@ -642,9 +641,12 @@ bool TRI_CheckAuthenticationAuthInfo (TRI_vocbase_t* vocbase,
|
|||
return false;
|
||||
}
|
||||
|
||||
*mustChange = auth->_mustChange;
|
||||
|
||||
// convert password
|
||||
res = false;
|
||||
|
||||
// salted password
|
||||
if (TRI_IsPrefixString(auth->_password, "$1$")) {
|
||||
if (strlen(auth->_password) < 12 || auth->_password[11] != '$') {
|
||||
LOG_WARNING("found corrupted password for user '%s'", username);
|
||||
|
@ -673,6 +675,8 @@ bool TRI_CheckAuthenticationAuthInfo (TRI_vocbase_t* vocbase,
|
|||
TRI_FreeString(TRI_CORE_MEM_ZONE, hex);
|
||||
}
|
||||
}
|
||||
|
||||
// unsalted password
|
||||
else {
|
||||
len = strlen(password);
|
||||
sha256 = TRI_SHA256String(password, len, &sha256Len);
|
||||
|
@ -700,8 +704,9 @@ bool TRI_CheckAuthenticationAuthInfo (TRI_vocbase_t* vocbase,
|
|||
if (cached != NULL) {
|
||||
void* old;
|
||||
|
||||
cached->_hash = TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, hash);
|
||||
cached->_username = TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, username);
|
||||
cached->_hash = TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, hash);
|
||||
cached->_username = TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, username);
|
||||
cached->_mustChange = auth->_mustChange;
|
||||
|
||||
if (cached->_hash == NULL || cached->_username == NULL) {
|
||||
FreeAuthCacheInfo(cached);
|
||||
|
@ -723,10 +728,6 @@ bool TRI_CheckAuthenticationAuthInfo (TRI_vocbase_t* vocbase,
|
|||
return res;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -45,11 +45,6 @@ struct TRI_vocbase_s;
|
|||
// --SECTION-- public types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup VocBase
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief authentication and authorisation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -58,6 +53,7 @@ typedef struct TRI_vocbase_auth_s {
|
|||
char* _username;
|
||||
char* _password;
|
||||
bool _active;
|
||||
bool _mustChange;
|
||||
}
|
||||
TRI_vocbase_auth_t;
|
||||
|
||||
|
@ -67,23 +63,15 @@ TRI_vocbase_auth_t;
|
|||
|
||||
typedef struct TRI_vocbase_auth_cache_s {
|
||||
char* _hash;
|
||||
char* _username;
|
||||
char* _username;
|
||||
bool _mustChange;
|
||||
}
|
||||
TRI_vocbase_auth_cache_t;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup VocBase
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief initialises the authentication info
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -133,20 +121,18 @@ void TRI_ClearAuthInfo (struct TRI_vocbase_s*);
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
char* TRI_CheckCacheAuthInfo (struct TRI_vocbase_s*,
|
||||
char const*);
|
||||
char const* hash,
|
||||
bool* mustChange);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief checks the authentication
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool TRI_CheckAuthenticationAuthInfo (struct TRI_vocbase_s*,
|
||||
char const*,
|
||||
char const*,
|
||||
char const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
char const* hash,
|
||||
char const* username,
|
||||
char const* password,
|
||||
bool* mustChange);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -459,12 +459,14 @@ static int WriteShutdownInfo (TRI_server_t* server) {
|
|||
static bool CanUseDatabase (TRI_vocbase_t* vocbase,
|
||||
char const* username,
|
||||
char const* password) {
|
||||
bool mustChange;
|
||||
|
||||
if (! vocbase->_settings.requireAuthentication) {
|
||||
// authentication is turned off
|
||||
return true;
|
||||
}
|
||||
|
||||
return TRI_CheckAuthenticationAuthInfo(vocbase, NULL, username, password);
|
||||
return TRI_CheckAuthenticationAuthInfo(vocbase, NULL, username, password, &mustChange);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*jslint indent: 2, nomen: true, maxlen: 120, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*global require */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -8,7 +8,7 @@
|
|||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2012 triagens GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 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.
|
||||
|
@ -25,7 +25,7 @@
|
|||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
/// @author Copyright 2012-2014, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var arangodb = require("org/arangodb");
|
||||
|
@ -37,42 +37,7 @@ var users = require("org/arangodb/users");
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup ArangoAPI
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief fetch a user
|
||||
///
|
||||
/// @RESTHEADER{GET /_api/user/{user},fetches a user}
|
||||
///
|
||||
/// @RESTURLPARAMETERS
|
||||
///
|
||||
/// @RESTURLPARAM{user,string,required}
|
||||
/// The name of the user.
|
||||
///
|
||||
/// @RESTDESCRIPTION
|
||||
///
|
||||
/// Fetches data about the specified user.
|
||||
///
|
||||
/// The call will return a JSON document with at least the following attributes
|
||||
/// on success:
|
||||
///
|
||||
/// - `user`: The name of the user as a string.
|
||||
///
|
||||
/// - `active`: an optional flag that specifies whether the user is active.
|
||||
///
|
||||
/// - `extra`: an optional JSON object with arbitrary extra data about the
|
||||
/// user.
|
||||
///
|
||||
/// @RESTRETURNCODES
|
||||
///
|
||||
/// @RESTRETURNCODE{200}
|
||||
/// The user was found.
|
||||
///
|
||||
/// @RESTRETURNCODE{404}
|
||||
/// The user with `user` does not exist.
|
||||
///
|
||||
/// @brief fetches a user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function get_api_user (req, res) {
|
||||
|
@ -89,8 +54,7 @@ function get_api_user (req, res) {
|
|||
var user = decodeURIComponent(req.suffix[0]);
|
||||
|
||||
try {
|
||||
var result = users.document(user);
|
||||
actions.resultOk(req, res, actions.HTTP_OK, result);
|
||||
actions.resultOk(req, res, actions.HTTP_OK, users.document(user));
|
||||
}
|
||||
catch (err) {
|
||||
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
|
||||
|
@ -103,61 +67,7 @@ function get_api_user (req, res) {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a new user
|
||||
///
|
||||
/// @RESTHEADER{POST /_api/user,creates user}
|
||||
///
|
||||
/// @RESTBODYPARAM{body,json,required}
|
||||
///
|
||||
/// @RESTDESCRIPTION
|
||||
///
|
||||
/// The following data need to be passed in a JSON representation in the body of
|
||||
/// the POST request:
|
||||
///
|
||||
/// - `user`: The name of the user as a string. This is mandatory.
|
||||
///
|
||||
/// - `passwd`: The user password as a string. If no password is specified,
|
||||
/// the empty string will be used.
|
||||
///
|
||||
/// - `active`: an optional flag that specifies whether the user is active.
|
||||
/// If not specified, this will default to `true`.
|
||||
///
|
||||
/// - `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
|
||||
/// `HTTP 201`.
|
||||
///
|
||||
/// In case of success, the returned JSON object has the following properties:
|
||||
///
|
||||
/// - `error`: boolean flag to indicate that an error occurred (`false`
|
||||
/// in this case)
|
||||
///
|
||||
/// - `code`: the HTTP status code
|
||||
///
|
||||
/// If the JSON representation is malformed or mandatory data is missing from the
|
||||
/// request, the server will respond with `HTTP 400`.
|
||||
///
|
||||
/// The body of the response will contain a JSON object with additional error
|
||||
/// details. The object has the following attributes:
|
||||
///
|
||||
/// - `error`: boolean flag to indicate that an error occurred (`true` in this case)
|
||||
///
|
||||
/// - `code`: the HTTP status code
|
||||
///
|
||||
/// - `errorNum`: the server error number
|
||||
///
|
||||
/// - `errorMessage`: a descriptive error message
|
||||
///
|
||||
/// @RESTRETURNCODES
|
||||
///
|
||||
/// @RESTRETURNCODE{201}
|
||||
/// returned if the user can be added by the server.
|
||||
///
|
||||
/// @RESTRETURNCODE{400}
|
||||
/// If the JSON representation is malformed or mandatory data is missing from the
|
||||
/// request.
|
||||
///
|
||||
/// @brief creates a new user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function post_api_user (req, res) {
|
||||
|
@ -166,8 +76,9 @@ function post_api_user (req, res) {
|
|||
if (json === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var user;
|
||||
|
||||
var user;
|
||||
|
||||
if (req.suffix.length === 1) {
|
||||
// validate if a combination or username / password is valid
|
||||
user = decodeURIComponent(req.suffix[0]);
|
||||
|
@ -189,82 +100,20 @@ function post_api_user (req, res) {
|
|||
}
|
||||
|
||||
user = json.user;
|
||||
|
||||
if (user === undefined && json.hasOwnProperty("username")) {
|
||||
// deprecated usage
|
||||
user = json.username;
|
||||
}
|
||||
|
||||
users.save(user, json.passwd, json.active, json.extra);
|
||||
var doc = users.save(user, json.passwd, json.active, json.extra, json.changePassword);
|
||||
users.reload();
|
||||
|
||||
actions.resultOk(req, res, actions.HTTP_CREATED, { });
|
||||
actions.resultOk(req, res, actions.HTTP_CREATED, doc);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replace an existing user
|
||||
///
|
||||
/// @RESTHEADER{PUT /_api/user/{user},replaces user}
|
||||
///
|
||||
/// @RESTURLPARAMETERS
|
||||
///
|
||||
/// @RESTURLPARAM{user,string,required}
|
||||
/// The name of the user.
|
||||
///
|
||||
/// @RESTBODYPARAM{body,json,required}
|
||||
///
|
||||
/// @RESTDESCRIPTION
|
||||
///
|
||||
/// Replaces the data of an existing user. The name of an existing user must
|
||||
/// be specified in `user`.
|
||||
///
|
||||
/// The following data can to be passed in a JSON representation in the body of
|
||||
/// the POST request:
|
||||
///
|
||||
/// - `passwd`: The user password as a string. Specifying a password is
|
||||
/// mandatory, but the empty string is allowed for passwords.
|
||||
///
|
||||
/// - `active`: an optional flag that specifies whether the user is active.
|
||||
/// If not specified, this will default to `true`.
|
||||
///
|
||||
/// - `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
|
||||
/// `HTTP 200`.
|
||||
///
|
||||
/// In case of success, the returned JSON object has the following properties:
|
||||
///
|
||||
/// - `error`: boolean flag to indicate that an error occurred (`false`
|
||||
/// in this case)
|
||||
///
|
||||
/// - `code`: the HTTP status code
|
||||
///
|
||||
/// If the JSON representation is malformed or mandatory data is missing from the
|
||||
/// request, the server will respond with `HTTP 400`. If the specified user
|
||||
/// does not exist, the server will respond with `HTTP 404`.
|
||||
///
|
||||
/// The body of the response will contain a JSON object with additional error
|
||||
/// details. The object has the following attributes:
|
||||
///
|
||||
/// - `error`: boolean flag to indicate that an error occurred (`true` in this case)
|
||||
///
|
||||
/// - `code`: the HTTP status code
|
||||
///
|
||||
/// - `errorNum`: the server error number
|
||||
///
|
||||
/// - `errorMessage`: a descriptive error message
|
||||
///
|
||||
/// @RESTRETURNCODES
|
||||
///
|
||||
/// @RESTRETURNCODE{200}
|
||||
/// Is returned if the user data can be replaced by the server.
|
||||
///
|
||||
/// @RESTRETURNCODE{400}
|
||||
/// The JSON representation is malformed or mandatory data is missing from the
|
||||
/// request.
|
||||
///
|
||||
/// @RESTRETURNCODE{404}
|
||||
/// The specified user does not exist.
|
||||
/// @brief replaces an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function put_api_user (req, res) {
|
||||
|
@ -276,16 +125,16 @@ function put_api_user (req, res) {
|
|||
var user = decodeURIComponent(req.suffix[0]);
|
||||
|
||||
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
|
||||
|
||||
|
||||
if (json === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
users.replace(user, json.passwd, json.active, json.extra);
|
||||
var doc = users.replace(user, json.passwd, json.active, json.extra, json.changePassword);
|
||||
users.reload();
|
||||
|
||||
actions.resultOk(req, res, actions.HTTP_OK, { });
|
||||
|
||||
actions.resultOk(req, res, actions.HTTP_OK, doc);
|
||||
}
|
||||
catch (err) {
|
||||
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
|
||||
|
@ -298,72 +147,7 @@ function put_api_user (req, res) {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief partially update an existing user
|
||||
///
|
||||
/// @RESTHEADER{PATCH /_api/user/{user},updates user}
|
||||
///
|
||||
/// @RESTURLPARAMETERS
|
||||
///
|
||||
/// @RESTURLPARAM{user,string,required}
|
||||
/// The name of the user.
|
||||
///
|
||||
/// @RESTBODYPARAM{body,json,required}
|
||||
///
|
||||
/// @RESTDESCRIPTION
|
||||
///
|
||||
/// Partially updates the data of an existing user. The name of an existing user
|
||||
/// must be specified in `user`.
|
||||
///
|
||||
/// The following data can be passed in a JSON representation in the body of
|
||||
/// the POST request:
|
||||
///
|
||||
/// - `passwd`: The user password as a string. Specifying a password is
|
||||
/// optional. If not specified, the previously existing value will not be
|
||||
/// modified.
|
||||
///
|
||||
/// - `active`: an optional flag that specifies whether the user is active.
|
||||
/// If not specified, the previously existing value will not be modified.
|
||||
///
|
||||
/// - `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
|
||||
/// `HTTP 200`.
|
||||
///
|
||||
/// In case of success, the returned JSON object has the following properties:
|
||||
///
|
||||
/// - `error`: boolean flag to indicate that an error occurred (`false`
|
||||
/// in this case)
|
||||
///
|
||||
/// - `code`: the HTTP status code
|
||||
///
|
||||
/// If the JSON representation is malformed or mandatory data is missing from the
|
||||
/// request, the server will respond with `HTTP 400`. If the specified user
|
||||
/// does not exist, the server will respond with `HTTP 404`.
|
||||
///
|
||||
/// The body of the response will contain a JSON object with additional error
|
||||
/// details. The object has the following attributes:
|
||||
///
|
||||
/// - `error`: boolean flag to indicate that an error occurred (`true` in this case)
|
||||
///
|
||||
/// - `code`: the HTTP status code
|
||||
///
|
||||
/// - `errorNum`: the server error number
|
||||
///
|
||||
/// - `errorMessage`: a descriptive error message
|
||||
///
|
||||
/// @RESTRETURNCODES
|
||||
///
|
||||
/// @RESTRETURNCODE{200}
|
||||
/// Is returned if the user data can be replaced by the server.
|
||||
///
|
||||
/// @RESTRETURNCODE{400}
|
||||
/// The JSON representation is malformed or mandatory data is missing from the
|
||||
/// request.
|
||||
///
|
||||
/// @RESTRETURNCODE{404}
|
||||
/// The specified user does not exist.
|
||||
///
|
||||
/// @brief partially updates an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function patch_api_user (req, res) {
|
||||
|
@ -374,15 +158,16 @@ function patch_api_user (req, res) {
|
|||
|
||||
var user = decodeURIComponent(req.suffix[0]);
|
||||
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
|
||||
|
||||
|
||||
if (json === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
users.update(user, json.passwd, json.active, json.extra);
|
||||
var doc = users.update(user, json.passwd, json.active, json.extra, json.changePassword);
|
||||
users.reload();
|
||||
actions.resultOk(req, res, actions.HTTP_OK, { });
|
||||
|
||||
actions.resultOk(req, res, actions.HTTP_OK, doc);
|
||||
}
|
||||
catch (err) {
|
||||
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
|
||||
|
@ -395,50 +180,7 @@ function patch_api_user (req, res) {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief remove an existing user
|
||||
///
|
||||
/// @RESTHEADER{DELETE /_api/user/{user},removes a user}
|
||||
///
|
||||
/// @RESTURLPARAMETERS
|
||||
///
|
||||
/// @RESTURLPARAM{user,string,required}
|
||||
/// The name of the user.
|
||||
///
|
||||
/// @RESTDESCRIPTION
|
||||
///
|
||||
/// Removes an existing user, identified by `user`.
|
||||
///
|
||||
/// If the user can be removed, the server will respond with `HTTP 202`.
|
||||
///
|
||||
/// In case of success, the returned JSON object has the following properties:
|
||||
///
|
||||
/// - `error`: boolean flag to indicate that an error occurred (`false`
|
||||
/// in this case)
|
||||
///
|
||||
/// - `code`: the HTTP status code
|
||||
///
|
||||
/// If the specified user does not exist, the server will respond with
|
||||
/// `HTTP 404`.
|
||||
///
|
||||
/// The body of the response will contain a JSON object with additional error
|
||||
/// details. The object has the following attributes:
|
||||
///
|
||||
/// - `error`: boolean flag to indicate that an error occurred (`true` in this case)
|
||||
///
|
||||
/// - `code`: the HTTP status code
|
||||
///
|
||||
/// - `errorNum`: the server error number
|
||||
///
|
||||
/// - `errorMessage`: a descriptive error message
|
||||
///
|
||||
/// @RESTRETURNCODES
|
||||
///
|
||||
/// @RESTRETURNCODE{202}
|
||||
/// Is returned if the user was removed by the server.
|
||||
///
|
||||
/// @RESTRETURNCODE{404}
|
||||
/// The specified user does not exist.
|
||||
///
|
||||
/// @brief removes an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function delete_api_user (req, res) {
|
||||
|
@ -449,9 +191,10 @@ function delete_api_user (req, res) {
|
|||
|
||||
var user = decodeURIComponent(req.suffix[0]);
|
||||
try {
|
||||
users.remove(user);
|
||||
var doc = users.remove(user);
|
||||
users.reload();
|
||||
actions.resultOk(req, res, actions.HTTP_ACCEPTED, { });
|
||||
|
||||
actions.resultOk(req, res, actions.HTTP_ACCEPTED, doc);
|
||||
}
|
||||
catch (err) {
|
||||
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
|
||||
|
@ -468,7 +211,7 @@ function delete_api_user (req, res) {
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief user actions gateway
|
||||
/// @brief user actions gateway
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
actions.defineHttp({
|
||||
|
@ -478,24 +221,24 @@ actions.defineHttp({
|
|||
callback : function (req, res) {
|
||||
try {
|
||||
switch (req.requestType) {
|
||||
case actions.GET:
|
||||
get_api_user(req, res);
|
||||
case actions.GET:
|
||||
get_api_user(req, res);
|
||||
break;
|
||||
|
||||
case actions.POST:
|
||||
post_api_user(req, res);
|
||||
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);
|
||||
case actions.PUT:
|
||||
put_api_user(req, res);
|
||||
break;
|
||||
|
||||
case actions.DELETE:
|
||||
delete_api_user(req, res);
|
||||
case actions.PATCH:
|
||||
patch_api_user(req, res);
|
||||
break;
|
||||
|
||||
case actions.DELETE:
|
||||
delete_api_user(req, res);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -508,11 +251,11 @@ actions.defineHttp({
|
|||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
|
||||
// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint"
|
||||
// End:
|
||||
|
|
|
@ -172,6 +172,7 @@
|
|||
"ERROR_USER_INVALID_PASSWORD" : { "code" : 1701, "message" : "invalid password" },
|
||||
"ERROR_USER_DUPLICATE" : { "code" : 1702, "message" : "duplicate user" },
|
||||
"ERROR_USER_NOT_FOUND" : { "code" : 1703, "message" : "user not found" },
|
||||
"ERROR_USER_CHANGE_PASSWORD" : { "code" : 1704, "message" : "user must change his password" },
|
||||
"ERROR_APPLICATION_INVALID_NAME" : { "code" : 1750, "message" : "invalid application name" },
|
||||
"ERROR_APPLICATION_INVALID_MOUNT" : { "code" : 1751, "message" : "invalid mount" },
|
||||
"ERROR_APPLICATION_DOWNLOAD_FAILED" : { "code" : 1752, "message" : "application download failed" },
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*jslint indent: 2, nomen: true, maxlen: 120, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*global require, exports */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -8,7 +8,7 @@
|
|||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2012 triagens GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 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.
|
||||
|
@ -25,19 +25,12 @@
|
|||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
/// @author Copyright 2012-2014, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var internal = require("internal");
|
||||
var internal = require("internal");
|
||||
var arangodb = require("org/arangodb");
|
||||
var arangosh = require("org/arangodb/arangosh");
|
||||
var base = require("org/arangodb/users-common");
|
||||
|
||||
var i;
|
||||
for (i in base) {
|
||||
if (base.hasOwnProperty(i)) {
|
||||
exports[i] = base[i];
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- module "org/arangodb/users"
|
||||
|
@ -48,14 +41,150 @@ for (i in base) {
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup ArangoShell
|
||||
/// @{
|
||||
/// @brief creates a new user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.save = function (user, passwd, active, extra, changePassword) {
|
||||
var db = internal.db;
|
||||
|
||||
var uri = "_api/user/";
|
||||
var data = {user: user};
|
||||
|
||||
if (passwd !== undefined) {
|
||||
data.passwd = passwd;
|
||||
}
|
||||
|
||||
if (active !== undefined) {
|
||||
data.active = active;
|
||||
}
|
||||
|
||||
if (extra !== undefined) {
|
||||
data.extra = extra;
|
||||
}
|
||||
|
||||
if (changePassword !== undefined) {
|
||||
data.changePassword = changePassword;
|
||||
}
|
||||
|
||||
var requestResult = db._connection.POST(uri, JSON.stringify(data));
|
||||
return arangosh.checkRequestResult(requestResult);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replaces an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.replace = function (user, passwd, active, extra, changePassword) {
|
||||
var db = internal.db;
|
||||
|
||||
var uri = "_api/user/" + encodeURIComponent(user);
|
||||
var data = {
|
||||
passwd: passwd,
|
||||
active: active,
|
||||
extra: extra,
|
||||
changePassword: changePassword
|
||||
};
|
||||
|
||||
var requestResult = db._connection.PUT(uri, JSON.stringify(data));
|
||||
return arangosh.checkRequestResult(requestResult);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief updates an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.update = function (user, passwd, active, extra, changePassword) {
|
||||
var db = internal.db;
|
||||
|
||||
var uri = "_api/user/" + encodeURIComponent(user);
|
||||
var data = {};
|
||||
|
||||
if (passwd !== undefined) {
|
||||
data.passwd = passwd;
|
||||
}
|
||||
|
||||
if (active !== undefined) {
|
||||
data.active = active;
|
||||
}
|
||||
|
||||
if (extra !== undefined) {
|
||||
data.extra = extra;
|
||||
}
|
||||
|
||||
if (changePassword !== undefined) {
|
||||
data.changePassword = changePassword;
|
||||
}
|
||||
|
||||
var requestResult = db._connection.PATCH(uri, JSON.stringify(data));
|
||||
return arangosh.checkRequestResult(requestResult);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief deletes an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.remove = function (user) {
|
||||
var db = internal.db;
|
||||
|
||||
var uri = "_api/user/" + encodeURIComponent(user);
|
||||
|
||||
var requestResult = db._connection.DELETE(uri);
|
||||
return arangosh.checkRequestResult(requestResult);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief gets an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.document = function (user) {
|
||||
var db = internal.db;
|
||||
|
||||
var uri = "_api/user/" + encodeURIComponent(user);
|
||||
|
||||
var requestResult = db._connection.GET(uri);
|
||||
return arangosh.checkRequestResult(requestResult);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief checks whether a combination of username / password is valid.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.isValid = function (user, password) {
|
||||
var db = internal.db;
|
||||
|
||||
var uri = "_api/user/" + encodeURIComponent(user);
|
||||
var data = { passwd: password };
|
||||
|
||||
var requestResult = db._connection.POST(uri, JSON.stringify(data));
|
||||
|
||||
if (requestResult.error !== undefined && requestResult.error) {
|
||||
if (requestResult.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return arangosh.checkRequestResult(requestResult);
|
||||
}
|
||||
|
||||
return requestResult.result;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief gets all existing users
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.all = function () {
|
||||
var db = internal.db;
|
||||
|
||||
var uri = "_api/user";
|
||||
|
||||
var requestResult = db._connection.GET(uri);
|
||||
return arangosh.checkRequestResult(requestResult).result;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief reloads the user authentication data
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
exports.reload = function () {
|
||||
var db = internal.db;
|
||||
|
||||
|
@ -63,16 +192,12 @@ exports.reload = function () {
|
|||
arangosh.checkRequestResult(requestResult);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\|/\\*jslint"
|
||||
// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint"
|
||||
// End:
|
||||
|
||||
|
|
|
@ -172,6 +172,7 @@
|
|||
"ERROR_USER_INVALID_PASSWORD" : { "code" : 1701, "message" : "invalid password" },
|
||||
"ERROR_USER_DUPLICATE" : { "code" : 1702, "message" : "duplicate user" },
|
||||
"ERROR_USER_NOT_FOUND" : { "code" : 1703, "message" : "user not found" },
|
||||
"ERROR_USER_CHANGE_PASSWORD" : { "code" : 1704, "message" : "user must change his password" },
|
||||
"ERROR_APPLICATION_INVALID_NAME" : { "code" : 1750, "message" : "invalid application name" },
|
||||
"ERROR_APPLICATION_INVALID_MOUNT" : { "code" : 1751, "message" : "invalid mount" },
|
||||
"ERROR_APPLICATION_DOWNLOAD_FAILED" : { "code" : 1752, "message" : "application download failed" },
|
||||
|
|
|
@ -1,534 +0,0 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*global require, exports */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @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 Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var internal = require("internal"); // OK: time
|
||||
var arangodb = require("org/arangodb");
|
||||
var crypto = require("org/arangodb/crypto");
|
||||
var _ = require("underscore");
|
||||
|
||||
var db = arangodb.db;
|
||||
var ArangoError = arangodb.ArangoError;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- module "org/arangodb/users"
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup ArangoShell
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var validateName = function (username) {
|
||||
if (typeof username !== 'string' || username === '') {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_USER_INVALID_NAME.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_USER_INVALID_NAME.message;
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief validate password
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var validatePassword = function (passwd) {
|
||||
if (typeof passwd !== 'string') {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_USER_INVALID_PASSWORD.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_USER_INVALID_PASSWORD.message;
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the users collection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var getStorage = function () {
|
||||
var users = db._collection("_users");
|
||||
|
||||
if (users === null) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code;
|
||||
err.errorMessage = "collection _users not found";
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
return users;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup ArangoShell
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_saveUser
|
||||
/// @brief create a new user
|
||||
///
|
||||
/// @FUN{users.save(@FA{user}, @FA{passwd}, @FA{active}, @FA{extra})}
|
||||
///
|
||||
/// This will create a new ArangoDB user. The username must be specified in
|
||||
/// @FA{user} 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
|
||||
/// the specified name.
|
||||
///
|
||||
/// The new user account can only be used after the server is either restarted
|
||||
/// or the server authentication cache is @ref UserManagementReload "reloaded".
|
||||
///
|
||||
/// Note: this function will not work from within the web interface
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("org/arangodb/users").save("my-user", "my-secret-password");
|
||||
/// arangosh> require("org/arangodb/users").reload();
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.save = function (user, passwd, active, extra) {
|
||||
if (passwd === null || passwd === undefined) {
|
||||
passwd = "";
|
||||
}
|
||||
|
||||
// validate input
|
||||
validateName(user);
|
||||
validatePassword(passwd);
|
||||
|
||||
if (active === undefined || active === null) {
|
||||
// this is the default value for active
|
||||
active = true;
|
||||
}
|
||||
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === null) {
|
||||
var hash = encodePassword(passwd);
|
||||
var data = {
|
||||
user: user,
|
||||
password: hash,
|
||||
active: active
|
||||
};
|
||||
|
||||
if (extra !== undefined) {
|
||||
data.extra = extra;
|
||||
}
|
||||
|
||||
var doc = users.save(data);
|
||||
// not exports.reload() as this is an abstract method...
|
||||
require("org/arangodb/users").reload();
|
||||
return doc;
|
||||
}
|
||||
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_USER_DUPLICATE.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_USER_DUPLICATE.message;
|
||||
|
||||
throw err;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_replaceUser
|
||||
/// @brief replace an existing user
|
||||
///
|
||||
/// @FUN{users.replace(@FA{user}, @FA{passwd}, @FA{active}, @FA{extra})}
|
||||
///
|
||||
/// This will look up an existing ArangoDB user and replace its user data.
|
||||
///
|
||||
/// The username must be specified in @FA{user}, 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 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
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("org/arangodb/users").replace("my-user", "my-changed-password");
|
||||
/// arangosh> require("org/arangodb/users").reload();
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.replace = function (user, passwd, active, extra) {
|
||||
if (passwd === null || passwd === undefined) {
|
||||
passwd = "";
|
||||
}
|
||||
|
||||
// validate input
|
||||
validateName(user);
|
||||
validatePassword(passwd);
|
||||
|
||||
if (active === undefined || active === null) {
|
||||
// this is the default
|
||||
active = true;
|
||||
}
|
||||
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === 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 hash = encodePassword(passwd);
|
||||
var data = {
|
||||
user: user,
|
||||
password: hash,
|
||||
active: active
|
||||
};
|
||||
if (extra !== undefined) {
|
||||
data.extra = extra;
|
||||
}
|
||||
|
||||
var doc = users.replace(previous, data);
|
||||
|
||||
// not exports.reload() as this is an abstract method...
|
||||
require("org/arangodb/users").reload();
|
||||
return doc;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_updateUser
|
||||
/// @brief update an existing user
|
||||
///
|
||||
/// @FUN{@FA{users}.update(@FA{user}, @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 in @FA{user} 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
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("org/arangodb/users").replace("my-user", "my-secret-password");
|
||||
/// arangosh> require("org/arangodb/users").reload();
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.update = function (user, passwd, active, extra) {
|
||||
// validate input
|
||||
validateName(user);
|
||||
if (passwd !== undefined) {
|
||||
validatePassword(passwd);
|
||||
}
|
||||
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === 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 = previous._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;
|
||||
}
|
||||
|
||||
var doc = users.update(previous, data);
|
||||
|
||||
// not exports.reload() as this is an abstract method...
|
||||
require("org/arangodb/users").reload();
|
||||
|
||||
return doc;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_removeUser
|
||||
/// @brief delete an existing user
|
||||
///
|
||||
/// @FUN{users.remove(@FA{user})}
|
||||
///
|
||||
/// Removes an existing ArangoDB user from the database.
|
||||
///
|
||||
/// The username must be specified in @FA{user} and the specified user must
|
||||
/// exist in the database.
|
||||
///
|
||||
/// This method will fail if the user cannot be found in the database.
|
||||
///
|
||||
/// The deletion is effective only after the server is either restarted
|
||||
/// or the server authentication cache is @ref UserManagementReload "reloaded".
|
||||
///
|
||||
/// Note: this function will not work from within the web interface
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("org/arangodb/users").remove("my-user");
|
||||
/// arangosh> require("org/arangodb/users").reload();
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.remove = function (user) {
|
||||
// validate input
|
||||
validateName(user);
|
||||
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === 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 doc = users.remove(previous);
|
||||
|
||||
// not exports.reload() as this is an abstract method...
|
||||
require("org/arangodb/users").reload();
|
||||
|
||||
return doc;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_documentUser
|
||||
/// @brief get an existing user
|
||||
///
|
||||
/// @FUN{users.document(@FA{user})}
|
||||
///
|
||||
/// Fetches an existing ArangoDB user from the database.
|
||||
///
|
||||
/// The username must be specified in @FA{user}.
|
||||
/// 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 (user) {
|
||||
// validate name
|
||||
validateName(user);
|
||||
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === 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;
|
||||
}
|
||||
|
||||
return {
|
||||
user: previous.user,
|
||||
active: previous.active,
|
||||
extra: previous.extra || { }
|
||||
};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_allUser
|
||||
/// @brief gets all existing users
|
||||
///
|
||||
/// @FUN{users.all()}
|
||||
///
|
||||
/// Fetches all existing ArangoDB users from the database.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.all = function () {
|
||||
var cursor = getStorage().all();
|
||||
var result = [ ];
|
||||
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
var user = {
|
||||
user: doc.user,
|
||||
active: doc.active,
|
||||
extra: doc.extra || { }
|
||||
};
|
||||
result.push(user);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_isValidUsers
|
||||
/// @brief checks whether a combination of username / password is valid.
|
||||
///
|
||||
/// @FUN{users.isvalid(@FA{user}, @FA{password})}
|
||||
///
|
||||
/// Checks whether the given combination of username and password is valid.
|
||||
/// The function will return a boolean value if the combination of username
|
||||
/// and password is valid.
|
||||
///
|
||||
/// Each call to this function is penalized by the server sleeping a random
|
||||
/// amount of time.
|
||||
///
|
||||
/// Note: this function will not work from within the web interface
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.isValid = function (user, password) {
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === null || ! previous.active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var salted = previous.password.substr(3, 8) + password;
|
||||
var hex = crypto.sha256(salted);
|
||||
|
||||
// penalize the call
|
||||
internal.sleep(Math.random());
|
||||
|
||||
return (previous.password.substr(12) === hex);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_reloadUsers
|
||||
/// @brief reloads the user authentication data
|
||||
///
|
||||
/// @FUN{users.reload()}
|
||||
///
|
||||
/// Reloads the user authentication data on the server
|
||||
///
|
||||
/// All user authentication data is loaded by the server once on startup only
|
||||
/// and is cached after that. When users get added or deleted, a cache flush is
|
||||
/// required, and this can be performed by called this method.
|
||||
///
|
||||
/// Note: this function will not work from within the web interface
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.reload = function () {
|
||||
throw "cannot use abstract reload function";
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\|/\\*jslint"
|
||||
// End:
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*jslint indent: 2, nomen: true, maxlen: 120, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*global require, exports, ArangoAgency */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -8,7 +8,7 @@
|
|||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2012 triagens GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 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.
|
||||
|
@ -25,37 +25,355 @@
|
|||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
/// @author Copyright 2012-2014, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var internal = require("internal"); // OK: reloadAuth
|
||||
var base = require("org/arangodb/users-common");
|
||||
var arangodb = require("org/arangodb");
|
||||
var crypto = require("org/arangodb/crypto");
|
||||
|
||||
// copy exports from base
|
||||
var i;
|
||||
for (i in base) {
|
||||
if (base.hasOwnProperty(i)) {
|
||||
exports[i] = base[i];
|
||||
}
|
||||
}
|
||||
var db = arangodb.db;
|
||||
var ArangoError = arangodb.ArangoError;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- module "org/arangodb/users"
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @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 validates a username
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var validateName = function (username) {
|
||||
if (typeof username !== 'string' || username === '') {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_USER_INVALID_NAME.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_USER_INVALID_NAME.message;
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief validates password
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var validatePassword = function (passwd) {
|
||||
if (typeof passwd !== 'string') {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_USER_INVALID_PASSWORD.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_USER_INVALID_PASSWORD.message;
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief returns the users collection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var getStorage = function () {
|
||||
var users = db._collection("_users");
|
||||
|
||||
if (users === null) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code;
|
||||
err.errorMessage = "collection _users not found";
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
return users;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup ArangoShell
|
||||
/// @{
|
||||
/// @brief creates a new user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.save = function (user, passwd, active, extra, changePassword) {
|
||||
if (passwd === null || passwd === undefined) {
|
||||
passwd = "";
|
||||
}
|
||||
|
||||
// validate input
|
||||
validateName(user);
|
||||
validatePassword(passwd);
|
||||
|
||||
if (active === undefined || active === null) {
|
||||
active = true; // this is the default value
|
||||
}
|
||||
|
||||
if (active === undefined || active === null) {
|
||||
active = true; // this is the default value
|
||||
}
|
||||
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === null) {
|
||||
var hash = encodePassword(passwd);
|
||||
var data = {
|
||||
user: user,
|
||||
password: hash,
|
||||
active: active,
|
||||
changePassword: changePassword
|
||||
};
|
||||
|
||||
if (extra !== undefined) {
|
||||
data.extra = extra;
|
||||
}
|
||||
|
||||
var doc = users.save(data);
|
||||
|
||||
// not exports.reload() as this is an abstract method...
|
||||
require("org/arangodb/users").reload();
|
||||
return users.document(doc._id);
|
||||
}
|
||||
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_USER_DUPLICATE.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_USER_DUPLICATE.message;
|
||||
|
||||
throw err;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replaces an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.replace = function (user, passwd, active, extra, changePassword) {
|
||||
if (passwd === null || passwd === undefined) {
|
||||
passwd = "";
|
||||
}
|
||||
|
||||
// validate input
|
||||
validateName(user);
|
||||
validatePassword(passwd);
|
||||
|
||||
if (active === undefined || active === null) {
|
||||
active = true; // this is the default
|
||||
}
|
||||
|
||||
if (changePassword === undefined || changePassword === null) {
|
||||
changePassword = false; // this is the default
|
||||
}
|
||||
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === 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 hash = encodePassword(passwd);
|
||||
var data = {
|
||||
user: user,
|
||||
password: hash,
|
||||
active: active,
|
||||
changePassword: changePassword
|
||||
};
|
||||
|
||||
if (extra !== undefined) {
|
||||
data.extra = extra;
|
||||
}
|
||||
|
||||
users.replace(previous, data);
|
||||
|
||||
// not exports.reload() as this is an abstract method...
|
||||
require("org/arangodb/users").reload();
|
||||
|
||||
return users.document(previous._id);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief updates an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.update = function (user, passwd, active, extra, changePassword) {
|
||||
|
||||
// validate input
|
||||
validateName(user);
|
||||
|
||||
if (passwd !== undefined) {
|
||||
validatePassword(passwd);
|
||||
}
|
||||
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === 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 = previous._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;
|
||||
}
|
||||
|
||||
if (changePassword !== undefined && changePassword !== null) {
|
||||
data.changePassword = changePassword;
|
||||
}
|
||||
|
||||
users.update(previous, data);
|
||||
|
||||
// not exports.reload() as this is an abstract method...
|
||||
require("org/arangodb/users").reload();
|
||||
|
||||
return users.document(previous._id);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief deletes an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.remove = function (user) {
|
||||
// validate input
|
||||
validateName(user);
|
||||
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === 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 doc = users.remove(previous);
|
||||
|
||||
// not exports.reload() as this is an abstract method...
|
||||
require("org/arangodb/users").reload();
|
||||
|
||||
return doc;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief gets an existing user
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.document = function (user) {
|
||||
|
||||
// validate name
|
||||
validateName(user);
|
||||
|
||||
var users = getStorage();
|
||||
var doc = users.firstExample({ user: user });
|
||||
|
||||
if (doc === 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;
|
||||
}
|
||||
|
||||
return {
|
||||
user: doc.user,
|
||||
active: doc.active,
|
||||
extra: doc.extra || {},
|
||||
changePassword: doc.changePassword
|
||||
};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief checks whether a combination of username / password is valid.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.isValid = function (user, password) {
|
||||
var users = getStorage();
|
||||
var previous = users.firstExample({ user: user });
|
||||
|
||||
if (previous === null || ! previous.active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var salted = previous.password.substr(3, 8) + password;
|
||||
var hex = crypto.sha256(salted);
|
||||
|
||||
// penalize the call
|
||||
internal.sleep(Math.random());
|
||||
|
||||
return (previous.password.substr(12) === hex);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief gets all existing users
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.all = function () {
|
||||
var cursor = getStorage().all();
|
||||
var result = [ ];
|
||||
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
var user = {
|
||||
user: doc.user,
|
||||
active: doc.active,
|
||||
extra: doc.extra || { },
|
||||
changePassword: doc.changePassword
|
||||
};
|
||||
|
||||
result.push(user);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief reloads the user authentication data
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
exports.reload = function () {
|
||||
internal.reloadAuth();
|
||||
if (require("org/arangodb/cluster").isCoordinator()) {
|
||||
|
@ -83,16 +401,12 @@ exports.reload = function () {
|
|||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\|/\\*jslint"
|
||||
// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint"
|
||||
// End:
|
||||
|
||||
|
|
|
@ -228,10 +228,11 @@ ERROR_TRANSACTION_DISALLOWED_OPERATION,1653,"disallowed operation inside transac
|
|||
## User management
|
||||
################################################################################
|
||||
|
||||
ERROR_USER_INVALID_NAME,1700,"invalid user name","Will be raised when an invalid user name is used"
|
||||
ERROR_USER_INVALID_PASSWORD,1701,"invalid password","Will be raised when an invalid password is used"
|
||||
ERROR_USER_DUPLICATE,1702,"duplicate user","Will be raised when a user name already exists"
|
||||
ERROR_USER_NOT_FOUND,1703,"user not found","Will be raised when a user name is updated that does not exist"
|
||||
ERROR_USER_INVALID_NAME,1700,"invalid user name","Will be raised when an invalid user name is used."
|
||||
ERROR_USER_INVALID_PASSWORD,1701,"invalid password","Will be raised when an invalid password is used."
|
||||
ERROR_USER_DUPLICATE,1702,"duplicate user","Will be raised when a user name already exists."
|
||||
ERROR_USER_NOT_FOUND,1703,"user not found","Will be raised when a user name is updated that does not exist."
|
||||
ERROR_USER_CHANGE_PASSWORD,1704,"user must change his password","Will be raised when the user must change his password."
|
||||
|
||||
################################################################################
|
||||
## Application management
|
||||
|
|
|
@ -168,6 +168,7 @@ void TRI_InitialiseErrorMessages (void) {
|
|||
REG_ERROR(ERROR_USER_INVALID_PASSWORD, "invalid password");
|
||||
REG_ERROR(ERROR_USER_DUPLICATE, "duplicate user");
|
||||
REG_ERROR(ERROR_USER_NOT_FOUND, "user not found");
|
||||
REG_ERROR(ERROR_USER_CHANGE_PASSWORD, "user must change his password");
|
||||
REG_ERROR(ERROR_APPLICATION_INVALID_NAME, "invalid application name");
|
||||
REG_ERROR(ERROR_APPLICATION_INVALID_MOUNT, "invalid mount");
|
||||
REG_ERROR(ERROR_APPLICATION_DOWNLOAD_FAILED, "application download failed");
|
||||
|
|
|
@ -393,13 +393,15 @@ extern "C" {
|
|||
/// Will be raised when a disallowed operation is carried out in a
|
||||
/// transaction.
|
||||
/// - 1700: @LIT{invalid user name}
|
||||
/// Will be raised when an invalid user name is used
|
||||
/// Will be raised when an invalid user name is used.
|
||||
/// - 1701: @LIT{invalid password}
|
||||
/// Will be raised when an invalid password is used
|
||||
/// Will be raised when an invalid password is used.
|
||||
/// - 1702: @LIT{duplicate user}
|
||||
/// Will be raised when a user name already exists
|
||||
/// Will be raised when a user name already exists.
|
||||
/// - 1703: @LIT{user not found}
|
||||
/// Will be raised when a user name is updated that does not exist
|
||||
/// Will be raised when a user name is updated that does not exist.
|
||||
/// - 1704: @LIT{user must change his password}
|
||||
/// Will be raised when the user must change his password.
|
||||
/// - 1750: @LIT{invalid application name}
|
||||
/// Will be raised when an invalid application name is specified.
|
||||
/// - 1751: @LIT{invalid mount}
|
||||
|
@ -2102,7 +2104,7 @@ void TRI_InitialiseErrorMessages (void);
|
|||
///
|
||||
/// invalid user name
|
||||
///
|
||||
/// Will be raised when an invalid user name is used
|
||||
/// Will be raised when an invalid user name is used.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRI_ERROR_USER_INVALID_NAME (1700)
|
||||
|
@ -2112,7 +2114,7 @@ void TRI_InitialiseErrorMessages (void);
|
|||
///
|
||||
/// invalid password
|
||||
///
|
||||
/// Will be raised when an invalid password is used
|
||||
/// Will be raised when an invalid password is used.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRI_ERROR_USER_INVALID_PASSWORD (1701)
|
||||
|
@ -2122,7 +2124,7 @@ void TRI_InitialiseErrorMessages (void);
|
|||
///
|
||||
/// duplicate user
|
||||
///
|
||||
/// Will be raised when a user name already exists
|
||||
/// Will be raised when a user name already exists.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRI_ERROR_USER_DUPLICATE (1702)
|
||||
|
@ -2132,11 +2134,21 @@ void TRI_InitialiseErrorMessages (void);
|
|||
///
|
||||
/// user not found
|
||||
///
|
||||
/// Will be raised when a user name is updated that does not exist
|
||||
/// Will be raised when a user name is updated that does not exist.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRI_ERROR_USER_NOT_FOUND (1703)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief 1704: ERROR_USER_CHANGE_PASSWORD
|
||||
///
|
||||
/// user must change his password
|
||||
///
|
||||
/// Will be raised when the user must change his password.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRI_ERROR_USER_CHANGE_PASSWORD (1704)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief 1750: ERROR_APPLICATION_INVALID_NAME
|
||||
///
|
||||
|
|
|
@ -592,7 +592,7 @@ namespace triagens {
|
|||
|
||||
// not found
|
||||
else if (authResult == HttpResponse::NOT_FOUND) {
|
||||
HttpResponse response(HttpResponse::NOT_FOUND);
|
||||
HttpResponse response(authResult);
|
||||
response.setContentType("application/json; charset=utf-8");
|
||||
|
||||
response.body().appendText("{\"error\":true,\"errorMessage\":\"")
|
||||
|
@ -607,6 +607,21 @@ namespace triagens {
|
|||
this->resetState();
|
||||
}
|
||||
|
||||
// forbidden
|
||||
else if (authResult == HttpResponse::FORBIDDEN) {
|
||||
HttpResponse response(authResult);
|
||||
response.setContentType("application/json; charset=utf-8");
|
||||
|
||||
response.body().appendText("{\"error\":true,\"errorMessage\":\"change password\",\"code\":")
|
||||
.appendInteger((int) authResult)
|
||||
.appendText(",\"errorNum\":")
|
||||
.appendInteger(TRI_ERROR_USER_CHANGE_PASSWORD)
|
||||
.appendText("}");
|
||||
|
||||
this->handleResponse(&response);
|
||||
this->resetState();
|
||||
}
|
||||
|
||||
// not authenticated
|
||||
else {
|
||||
const string realm = "basic realm=\"" + this->_server->getHandlerFactory()->authenticationRealm(this->_request) + "\"";
|
||||
|
|
Loading…
Reference in New Issue