Skip to content

Commit

Permalink
PCBC-468: Implement user management
Browse files Browse the repository at this point in the history
Change-Id: I1ef0dbb4ba59a4aa7ddd5159d69afca0797b0b39
Reviewed-on: http://review.couchbase.org/78664
Tested-by: Build Bot <[email protected]>
Reviewed-by: Sergey Avseyev <[email protected]>
  • Loading branch information
avsej committed May 26, 2017
1 parent 95dce91 commit 1cf072c
Show file tree
Hide file tree
Showing 8 changed files with 458 additions and 3 deletions.
72 changes: 72 additions & 0 deletions api/couchbase.php
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,78 @@ final public function removeBucket($name) {}
* Retrieving Cluster Information
*/
final public function info() {}

/**
* Lists all users on this cluster.
*
* @return array
*/
final public function listUsers() {}

/**
* Creates new user
*
* @param string $name Name of the user
* @param \Couchbase\UserSettings $settings settings (credentials and roles)
*
* @see https://developer.couchbase.com/documentation/server/5.0/rest-api/rbac.html
* More options and details
*/
final public function upsertUser($name, $settings) {}

/**
* Removes a user identified by its name.
*
* @param string $name name of the bucket
*
* @see https://developer.couchbase.com/documentation/server/5.0/rest-api/rbac.html
* More details
*/
final public function removeUser($name) {}
}

/**
* Represents settings for new/updated user.
*
* @see https://developer.couchbase.com/documentation/server/5.0/rest-api/rbac.html
*/
final class UserSettings {
/**
* Sets full name of the user (optional).
*
* @param string $fullName Full name of the user
*
* @return \Couchbase\UserSettings
*
* @see https://developer.couchbase.com/documentation/server/5.0/rest-api/rbac.html
* More details
*/
final public function fullName($fullName) {}

/**
* Sets password of the user.
*
* @param string $password Password of the user
*
* @return \Couchbase\UserSettings
*
* @see https://developer.couchbase.com/documentation/server/5.0/rest-api/rbac.html
* More details
*/
final public function password($password) {}

/**
* Adds role to the list of the accessible roles of the user.
*
* @param string $role identifier of the role
* @param string $bucket the bucket where this role applicable (or `*` for all buckets)
*
* @return \Couchbase\UserSettings
*
* @see https://developer.couchbase.com/documentation/server/5.0/rest-api/rbac.html
* More details
*/
final public function role($role, $bucket) {}
}

/**
Expand Down
1 change: 1 addition & 0 deletions config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ COUCHBASE_FILES=" \
src/couchbase/bucket_manager/n1ix_list.c \
src/couchbase/cluster.c \
src/couchbase/cluster_manager.c \
src/couchbase/cluster_manager/user_settings.c \
src/couchbase/document.c \
src/couchbase/document_fragment.c \
src/couchbase/lookup_in_builder.c \
Expand Down
3 changes: 3 additions & 0 deletions config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ if (PHP_COUCHBASE != "no") {
"n1ix_create.c " +
"n1ix_drop.c " +
"n1ix_list.c ";
src_couchbase_cluster_manager_sources =
"user_settings.c ";
src_couchbase_search_sources =
"boolean_field_query.c " +
"boolean_query.c " +
Expand Down Expand Up @@ -97,6 +99,7 @@ if (PHP_COUCHBASE != "no") {
ADD_SOURCES(configure_module_dirname + "\\src\\couchbase\\search", src_couchbase_search_sources, "couchbase");
ADD_SOURCES(configure_module_dirname + "\\src\\couchbase\\bucket", src_couchbase_bucket_sources, "couchbase");
ADD_SOURCES(configure_module_dirname + "\\src\\couchbase\\bucket_manager", src_couchbase_bucket_manager_sources, "couchbase");
ADD_SOURCES(configure_module_dirname + "\\src\\couchbase\\cluster_manager", src_couchbase_cluster_manager_sources, "couchbase");
AC_DEFINE('HAVE_COUCHBASELIB', 1, 'Have Couchbase library');
MESSAGE("\tlibcouchbase build");
} else {
Expand Down
1 change: 1 addition & 0 deletions couchbase.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ PHP_MINIT_FUNCTION(couchbase)
PHP_MINIT(DocumentFragment)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(Cluster)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(ClusterManager)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(UserSettings)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(Bucket)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(BucketManager)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(Authenticator)(INIT_FUNC_ARGS_PASSTHRU);
Expand Down
22 changes: 21 additions & 1 deletion couchbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ PHP_MINIT_FUNCTION(Document);
PHP_MINIT_FUNCTION(DocumentFragment);
PHP_MINIT_FUNCTION(Cluster);
PHP_MINIT_FUNCTION(ClusterManager);
PHP_MINIT_FUNCTION(UserSettings);
PHP_MINIT_FUNCTION(Bucket);
PHP_MINIT_FUNCTION(BucketManager);
PHP_MINIT_FUNCTION(Authenticator);
Expand Down Expand Up @@ -180,6 +181,7 @@ extern zend_class_entry *pcbc_search_query_part_ce;
extern zend_class_entry *pcbc_search_facet_ce;
extern zend_class_entry *pcbc_view_query_encodable_ce;
extern zend_class_entry *pcbc_json_serializable_ce;
extern zend_class_entry *pcbc_user_settings_ce;

#if PHP_VERSION_ID >= 70000
#define PCBC_ZVAL_ALLOC(__pcbc_val) ZVAL_UNDEF(&(__pcbc_val))
Expand Down Expand Up @@ -422,7 +424,7 @@ typedef zval *PCBC_ZVAL;
(__pcbc_receiver_length) = ZSTR_LEN((__pcbc_smart_str).s); \
} while (0)
#define PCBC_SMARTSTR_VAL(__pcbc_smart_str) (char *)ZSTR_VAL((__pcbc_smart_str).s)
#define PCBC_SMARTSTR_LEN(__pcbc_smart_str) (int)ZSTR_LEN((__pcbc_smart_str).s)
#define PCBC_SMARTSTR_LEN(__pcbc_smart_str) ((__pcbc_smart_str).s ? (int)(ZSTR_LEN((__pcbc_smart_str).s)) : 0)
#define PCBC_SMARTSTR_EMPTY(__pcbc_smart_str) ((__pcbc_smart_str).s == NULL || PCBC_SMARTSTR_LEN(__pcbc_smart_str) == 0)
#else
#define PCBC_SMARTSTR_DUP(__pcbc_smart_str, __pcbc_receiver_buf) \
Expand Down Expand Up @@ -658,6 +660,16 @@ typedef struct {
PCBC_ZEND_OBJECT_POST
} pcbc_spatial_view_query_t;

typedef struct {
PCBC_ZEND_OBJECT_PRE
char *full_name;
char *password;
int full_name_len;
int password_len;
smart_str roles;
PCBC_ZEND_OBJECT_POST
} pcbc_user_settings_t;

/* param parser */
#define PCBC_PP_MAX_ARGS 10

Expand Down Expand Up @@ -860,6 +872,10 @@ static inline pcbc_password_authenticator_t *pcbc_password_authenticator_fetch_o
{
return (pcbc_password_authenticator_t *)((char *)obj - XtOffsetOf(pcbc_password_authenticator_t, std));
}
static inline pcbc_user_settings_t *pcbc_user_settings_fetch_object(zend_object *obj)
{
return (pcbc_user_settings_t *)((char *)obj - XtOffsetOf(pcbc_user_settings_t, std));
}
#define Z_CLUSTER_OBJ(zo) (pcbc_cluster_fetch_object(zo))
#define Z_CLUSTER_OBJ_P(zv) (pcbc_cluster_fetch_object(Z_OBJ_P(zv)))
#define Z_CLUSTER_MANAGER_OBJ(zo) (pcbc_cluster_manager_fetch_object(zo))
Expand Down Expand Up @@ -888,6 +904,8 @@ static inline pcbc_password_authenticator_t *pcbc_password_authenticator_fetch_o
#define Z_CLASSIC_AUTHENTICATOR_OBJ_P(zv) (pcbc_classic_authenticator_fetch_object(Z_OBJ_P(zv)))
#define Z_PASSWORD_AUTHENTICATOR_OBJ(zo) (pcbc_password_authenticator_fetch_object(zo))
#define Z_PASSWORD_AUTHENTICATOR_OBJ_P(zv) (pcbc_password_authenticator_fetch_object(Z_OBJ_P(zv)))
#define Z_USER_SETTINGS_OBJ(zo) (pcbc_user_settings_fetch_object(zo))
#define Z_USER_SETTINGS_OBJ_P(zv) (pcbc_user_settings_fetch_object(Z_OBJ_P(zv)))
#else
#define Z_CLUSTER_OBJ(zo) ((pcbc_cluster_t *)zo)
#define Z_CLUSTER_OBJ_P(zv) ((pcbc_cluster_t *)zend_object_store_get_object(zv TSRMLS_CC))
Expand Down Expand Up @@ -917,6 +935,8 @@ static inline pcbc_password_authenticator_t *pcbc_password_authenticator_fetch_o
#define Z_CLASSIC_AUTHENTICATOR_OBJ_P(zv) ((pcbc_classic_authenticator_t *)zend_object_store_get_object(zv TSRMLS_CC))
#define Z_PASSWORD_AUTHENTICATOR_OBJ(zo) ((pcbc_password_authenticator_t *)zo)
#define Z_PASSWORD_AUTHENTICATOR_OBJ_P(zv) ((pcbc_password_authenticator_t *)zend_object_store_get_object(zv TSRMLS_CC))
#define Z_USER_SETTINGS_OBJ(zo) ((pcbc_user_settings_t *)zo)
#define Z_USER_SETTINGS_OBJ_P(zv) ((pcbc_user_settings_t *)zend_object_store_get_object(zv TSRMLS_CC))
#endif

typedef struct {
Expand Down
2 changes: 2 additions & 0 deletions package2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
* PCBC-487: Fix adhoc vs prepared query logic. It should be inversed manually before 2.3.3.
* PCBC-475: Add support for Term Range Query (full text search).
* PCBC-475: Add support for geo search queries.
* PCBC-468: Add support for user CRUD in ClusterManager
</notes>

<contents>
Expand Down Expand Up @@ -99,6 +100,7 @@
<file role="src" name="src/couchbase/bucket_manager/n1ix_list.c" />
<file role="src" name="src/couchbase/cluster.c" />
<file role="src" name="src/couchbase/cluster_manager.c" />
<file role="src" name="src/couchbase/cluster_manager/user_settings.c" />
<file role="src" name="src/couchbase/document.c" />
<file role="src" name="src/couchbase/document_fragment.c" />
<file role="src" name="src/couchbase/log_formatter.c" />
Expand Down
132 changes: 130 additions & 2 deletions src/couchbase/cluster_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,120 @@ PHP_METHOD(ClusterManager, info)
pcbc_http_request(return_value, obj->conn->lcb, &cmd, 1 TSRMLS_CC);
} /* }}} */

/* {{{ proto array ClusterManager::listUsers() */
PHP_METHOD(ClusterManager, listUsers)
{
pcbc_cluster_manager_t *obj;
lcb_CMDHTTP cmd = {0};
const char *path = "/settings/rbac/users";
int rv;

obj = Z_CLUSTER_MANAGER_OBJ_P(getThis());

rv = zend_parse_parameters_none();
if (rv == FAILURE) {
RETURN_NULL();
}

cmd.type = LCB_HTTP_TYPE_MANAGEMENT;
cmd.method = LCB_HTTP_METHOD_GET;
LCB_CMD_SET_KEY(&cmd, path, strlen(path));
cmd.content_type = PCBC_CONTENT_TYPE_FORM;
pcbc_http_request(return_value, obj->conn->lcb, &cmd, 1 TSRMLS_CC);
} /* }}} */

/* {{{ proto array ClusterManager::removeUser(string $name) */
PHP_METHOD(ClusterManager, removeUser)
{
pcbc_cluster_manager_t *obj;
const char *name = NULL;
pcbc_str_arg_size name_len = 0;
lcb_CMDHTTP cmd = {0};
char *path;
int rv, path_len;

obj = Z_CLUSTER_MANAGER_OBJ_P(getThis());

rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len);
if (rv == FAILURE) {
return;
}
path_len = spprintf(&path, 0, "/settings/rbac/users/local/%*s", name_len, name);
cmd.type = LCB_HTTP_TYPE_MANAGEMENT;
cmd.method = LCB_HTTP_METHOD_DELETE;
LCB_CMD_SET_KEY(&cmd, path, path_len);
cmd.content_type = PCBC_CONTENT_TYPE_FORM;
pcbc_http_request(return_value, obj->conn->lcb, &cmd, 0 TSRMLS_CC);
efree(path);
if (Z_STRLEN_P(return_value) == 0 || (Z_STRVAL_P(return_value)[0] == '"' && Z_STRVAL_P(return_value)[1] == '"')) {
RETURN_TRUE;
} else {
throw_pcbc_exception(Z_STRVAL_P(return_value), LCB_EINVAL);
RETURN_NULL();
}
} /* }}} */

/* {{{ proto array ClusterManager::upsertUser(string $name, \Couchbase\UserSettings $settings) */
PHP_METHOD(ClusterManager, upsertUser)
{
pcbc_cluster_manager_t *obj;
const char *name = NULL;
pcbc_str_arg_size name_len = 0;
zval *settings = NULL;
char *path;
int rv, path_len;
lcb_CMDHTTP cmd = {0};
smart_str buf = {0};
PCBC_ZVAL body;
pcbc_user_settings_t *user;

obj = Z_CLUSTER_MANAGER_OBJ_P(getThis());

rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sO", &name, &name_len, &settings, pcbc_user_settings_ce);
if (rv == FAILURE) {
return;
}

user = Z_USER_SETTINGS_OBJ_P(settings);
path_len = spprintf(&path, 0, "/settings/rbac/users/local/%*s", name_len, name);
cmd.type = LCB_HTTP_TYPE_MANAGEMENT;
cmd.method = LCB_HTTP_METHOD_PUT;
LCB_CMD_SET_KEY(&cmd, path, path_len);
cmd.content_type = PCBC_CONTENT_TYPE_FORM;

PCBC_ZVAL_ALLOC(body);
array_init_size(PCBC_P(body), 3);
if (user->full_name) {
ADD_ASSOC_STRINGL(PCBC_P(body), "name", user->full_name, user->full_name_len);
}
if (user->password) {
ADD_ASSOC_STRINGL(PCBC_P(body), "password", user->password, user->password_len);
}
if (PCBC_SMARTSTR_LEN(user->roles)) {
ADD_ASSOC_STRINGL(PCBC_P(body), "roles", PCBC_SMARTSTR_VAL(user->roles), PCBC_SMARTSTR_LEN(user->roles));
}
rv = php_url_encode_hash_ex(HASH_OF(PCBC_P(body)), &buf, NULL, 0, NULL, 0, NULL, 0, NULL, NULL,
PHP_QUERY_RFC1738 TSRMLS_CC);
zval_ptr_dtor(&body);
if (rv == FAILURE) {
pcbc_log(LOGARGS(obj->conn->lcb, WARN), "Failed to encode options as RFC1738 query");
smart_str_free(&buf);
RETURN_NULL();
} else {
smart_str_0(&buf);
PCBC_SMARTSTR_SET(buf, cmd.body, cmd.nbody);
}
pcbc_http_request(return_value, obj->conn->lcb, &cmd, 0 TSRMLS_CC);
smart_str_free(&buf);
efree(path);
if (Z_STRLEN_P(return_value) == 0 || (Z_STRVAL_P(return_value)[0] == '"' && Z_STRVAL_P(return_value)[1] == '"')) {
RETURN_TRUE;
} else {
throw_pcbc_exception(Z_STRVAL_P(return_value), LCB_EINVAL);
RETURN_NULL();
}
} /* }}} */

ZEND_BEGIN_ARG_INFO_EX(ai_ClusterManager_none, 0, 0, 0)
ZEND_END_ARG_INFO()

Expand All @@ -164,12 +278,24 @@ ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, options)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_ClusterManager_upsertUser, 0, 0, 2)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, settings)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(ai_ClusterManager_removeUser, 0, 0, 1)
ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()

// clang-format off
zend_function_entry cluster_manager_methods[] = {
PHP_ME(ClusterManager, __construct, ai_ClusterManager_none, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL | ZEND_ACC_CTOR)
PHP_ME(ClusterManager, listBuckets, ai_ClusterManager_none, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
PHP_ME(ClusterManager, createBucket, ai_ClusterManager_createBucket, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
PHP_ME(ClusterManager, removeBucket, ai_ClusterManager_removeBucket, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
PHP_ME(ClusterManager, listUsers, ai_ClusterManager_none, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
PHP_ME(ClusterManager, upsertUser, ai_ClusterManager_upsertUser, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
PHP_ME(ClusterManager, removeUser, ai_ClusterManager_removeUser, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
PHP_ME(ClusterManager, info, ai_ClusterManager_none, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
PHP_FE_END
};
Expand Down Expand Up @@ -225,9 +351,11 @@ void pcbc_cluster_manager_init(zval *return_value, pcbc_cluster_t *cluster, cons

if (!Z_ISUNDEF(cluster->auth)) {
if (instanceof_function(Z_OBJCE_P(PCBC_P(cluster->auth)), pcbc_classic_authenticator_ce TSRMLS_CC)) {
pcbc_generate_classic_lcb_auth(Z_CLASSIC_AUTHENTICATOR_OBJ_P(PCBC_P(cluster->auth)), &auth, LCB_TYPE_CLUSTER, username, password, &auth_hash TSRMLS_CC);
pcbc_generate_classic_lcb_auth(Z_CLASSIC_AUTHENTICATOR_OBJ_P(PCBC_P(cluster->auth)), &auth,
LCB_TYPE_CLUSTER, username, password, &auth_hash TSRMLS_CC);
} else if (instanceof_function(Z_OBJCE_P(PCBC_P(cluster->auth)), pcbc_password_authenticator_ce TSRMLS_CC)) {
pcbc_generate_password_lcb_auth(Z_PASSWORD_AUTHENTICATOR_OBJ_P(PCBC_P(cluster->auth)), &auth, LCB_TYPE_CLUSTER, username, password, &auth_hash TSRMLS_CC);
pcbc_generate_password_lcb_auth(Z_PASSWORD_AUTHENTICATOR_OBJ_P(PCBC_P(cluster->auth)), &auth,
LCB_TYPE_CLUSTER, username, password, &auth_hash TSRMLS_CC);
}
}
if (!auth) {
Expand Down
Loading

0 comments on commit 1cf072c

Please sign in to comment.