1
0
mirror of https://github.com/haiwen/seafile-server.git synced 2025-04-28 11:24:48 +00:00
seafile-server/common/user-mgr.c

1993 lines
65 KiB
C
Raw Normal View History

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "common.h"
#include <sys/stat.h>
#include <dirent.h>
#include "utils.h"
#include "seafile-session.h"
#include "seafile-error.h"
#include "user-mgr.h"
#include "seaf-db.h"
#include "seaf-utils.h"
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#ifdef HAVE_LDAP
#ifndef WIN32
#define LDAP_DEPRECATED 1
#include <ldap.h>
#else
#include <winldap.h>
#include <winber.h>
#ifndef LDAP_OPT_SUCCESS
#define LDAP_OPT_SUCCESS LDAP_SUCCESS
#endif
#endif
#endif
#define DEBUG_FLAG CCNET_DEBUG_PEER
#include "log.h"
#define DEFAULT_SAVING_INTERVAL_MSEC 30000
#define DEFAULT_MAX_CONNECTIONS 100
G_DEFINE_TYPE (CcnetUserManager, ccnet_user_manager, G_TYPE_OBJECT);
#define GET_PRIV(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_USER_MANAGER, CcnetUserManagerPriv))
static int open_db (CcnetUserManager *manager);
#ifdef HAVE_LDAP
static int try_load_ldap_settings (CcnetUserManager *manager);
#endif
struct CcnetUserManagerPriv {
CcnetDB *db;
int max_users;
};
static void
ccnet_user_manager_class_init (CcnetUserManagerClass *klass)
{
g_type_class_add_private (klass, sizeof (CcnetUserManagerPriv));
}
static void
ccnet_user_manager_init (CcnetUserManager *manager)
{
manager->priv = GET_PRIV(manager);
}
CcnetUserManager*
ccnet_user_manager_new (SeafileSession *session)
{
CcnetUserManager* manager;
manager = g_object_new (CCNET_TYPE_USER_MANAGER, NULL);
manager->session = session;
manager->user_hash = g_hash_table_new (g_str_hash, g_str_equal);
return manager;
}
#define DEFAULT_PASSWD_HASH_ITER 10000
// return current active user number
static int
get_current_user_number (CcnetUserManager *manager)
{
int total = 0, count;
count = ccnet_user_manager_count_emailusers (manager, "DB");
if (count < 0) {
ccnet_warning ("Failed to get user number from DB.\n");
return -1;
}
total += count;
#ifdef HAVE_LDAP
if (manager->use_ldap) {
count = ccnet_user_manager_count_emailusers (manager, "LDAP");
if (count < 0) {
ccnet_warning ("Failed to get user number from LDAP.\n");
return -1;
}
total += count;
}
#endif
return total;
}
static gboolean
check_user_number (CcnetUserManager *manager, gboolean allow_equal)
{
if (manager->priv->max_users == 0) {
return TRUE;
}
int cur_num = get_current_user_number (manager);
if (cur_num < 0) {
return FALSE;
}
if ((allow_equal && cur_num > manager->priv->max_users) ||
(!allow_equal && cur_num >= manager->priv->max_users)) {
ccnet_warning ("The number of users exceeds limit, max %d, current %d\n",
manager->priv->max_users, cur_num);
return FALSE;
}
return TRUE;
}
int
ccnet_user_manager_prepare (CcnetUserManager *manager)
{
int ret;
#ifdef HAVE_LDAP
if (try_load_ldap_settings (manager) < 0)
return -1;
#endif
int iter = g_key_file_get_integer (manager->session->ccnet_config,
"USER", "PASSWORD_HASH_ITERATIONS",
NULL);
if (iter <= 0)
iter = DEFAULT_PASSWD_HASH_ITER;
manager->passwd_hash_iter = iter;
manager->userdb_path = g_build_filename (manager->session->ccnet_dir,
"user-db", NULL);
ret = open_db(manager);
if (ret < 0)
return ret;
if (!check_user_number (manager, TRUE)) {
return -1;
}
return 0;
}
void
ccnet_user_manager_free (CcnetUserManager *manager)
{
g_object_unref (manager);
}
void
ccnet_user_manager_start (CcnetUserManager *manager)
{
}
void ccnet_user_manager_on_exit (CcnetUserManager *manager)
{
}
void
ccnet_user_manager_set_max_users (CcnetUserManager *manager, gint64 max_users)
{
manager->priv->max_users = max_users;
}
/* -------- LDAP related --------- */
#ifdef HAVE_LDAP
static int try_load_ldap_settings (CcnetUserManager *manager)
{
GKeyFile *config = manager->session->ccnet_config;
manager->ldap_host = ccnet_key_file_get_string (config, "LDAP", "HOST");
if (!manager->ldap_host)
return 0;
manager->use_ldap = TRUE;
#ifdef WIN32
manager->use_ssl = g_key_file_get_boolean (config, "LDAP", "USE_SSL", NULL);
#endif
char *base_list = ccnet_key_file_get_string (config, "LDAP", "BASE");
if (!base_list) {
ccnet_warning ("LDAP: BASE not found in config file.\n");
return -1;
}
manager->base_list = g_strsplit (base_list, ";", -1);
manager->filter = ccnet_key_file_get_string (config, "LDAP", "FILTER");
manager->user_dn = ccnet_key_file_get_string (config, "LDAP", "USER_DN");
if (manager->user_dn) {
manager->password = ccnet_key_file_get_string (config, "LDAP", "PASSWORD");
if (!manager->password) {
ccnet_warning ("LDAP: PASSWORD not found in config file.\n");
return -1;
}
}
/* Use anonymous if user_dn is not set. */
manager->login_attr = ccnet_key_file_get_string (config, "LDAP", "LOGIN_ATTR");
if (!manager->login_attr)
manager->login_attr = g_strdup("mail");
GError *error = NULL;
manager->follow_referrals = g_key_file_get_boolean (config,
"LDAP", "FOLLOW_REFERRALS",
&error);
if (error) {
/* Default is follow referrals. */
g_clear_error (&error);
manager->follow_referrals = TRUE;
}
return 0;
}
static LDAP *ldap_init_and_bind (CcnetUserManager *manager,
const char *host,
#ifdef WIN32
gboolean use_ssl,
#endif
const char *user_dn,
const char *password)
{
LDAP *ld;
int res;
int desired_version = LDAP_VERSION3;
#ifndef WIN32
res = ldap_initialize (&ld, host);
if (res != LDAP_SUCCESS) {
ccnet_warning ("ldap_initialize failed: %s.\n", ldap_err2string(res));
return NULL;
}
#else
char *host_copy = g_strdup (host);
if (!use_ssl)
ld = ldap_init (host_copy, LDAP_PORT);
else
ld = ldap_sslinit (host_copy, LDAP_SSL_PORT, 1);
g_free (host_copy);
if (!ld) {
ccnet_warning ("ldap_init failed: %ul.\n", LdapGetLastError());
return NULL;
}
#endif
/* set the LDAP version to be 3 */
res = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &desired_version);
if (res != LDAP_OPT_SUCCESS) {
ccnet_warning ("ldap_set_option failed: %s.\n", ldap_err2string(res));
return NULL;
}
res = ldap_set_option (ld, LDAP_OPT_REFERRALS,
manager->follow_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF);
if (res != LDAP_OPT_SUCCESS) {
ccnet_warning ("ldap_set_option referrals failed: %s.\n",
ldap_err2string(res));
return NULL;
}
if (user_dn) {
#ifndef WIN32
res = ldap_bind_s (ld, user_dn, password, LDAP_AUTH_SIMPLE);
#else
char *dn_copy = g_strdup(user_dn);
char *password_copy = g_strdup(password);
res = ldap_bind_s (ld, dn_copy, password_copy, LDAP_AUTH_SIMPLE);
g_free (dn_copy);
g_free (password_copy);
#endif
if (res != LDAP_SUCCESS ) {
ccnet_warning ("ldap_bind failed for user %s: %s.\n",
user_dn, ldap_err2string(res));
ldap_unbind_s (ld);
return NULL;
}
}
return ld;
}
static gboolean
get_uid_cb (CcnetDBRow *row, void *data)
{
int *id = data;
*id = seaf_db_row_get_column_int (row, 0);
return FALSE;
}
static int
add_ldapuser (CcnetDB *db,
const char *email,
const char *password,
gboolean is_staff,
gboolean is_active,
const char *extra_attrs)
{
int rc;
int uid = -1;
rc = seaf_db_statement_foreach_row (db,
"SELECT id FROM LDAPUsers WHERE email = ?",
get_uid_cb, &uid, 1, "string", email);
if (rc < 0) {
return rc;
}
if (rc == 1) {
return uid;
}
if (extra_attrs)
rc = seaf_db_statement_query (db,
"INSERT INTO LDAPUsers (email, password, is_staff, "
"is_active, extra_attrs) VALUES (?, ?, ?, ?, ?)",
5, "string", email, "string", password, "int",
is_staff, "int", is_active, "string", extra_attrs);
else
rc = seaf_db_statement_query (db,
"INSERT INTO LDAPUsers (email, password, is_staff, "
"is_active) VALUES (?, ?, ?, ?)", 4, "string", email,
"string", password, "int", is_staff, "int", is_active);
if (rc < 0) {
return rc;
}
seaf_db_statement_foreach_row (db,
"SELECT id FROM LDAPUsers WHERE email = ?",
get_uid_cb, &uid, 1, "string", email);
return uid;
}
static int ldap_verify_user_password (CcnetUserManager *manager,
const char *uid,
const char *password)
{
LDAP *ld = NULL;
int res;
GString *filter;
char *filter_str = NULL;
char *attrs[2];
LDAPMessage *msg = NULL, *entry;
char *dn = NULL;
int ret = 0;
/* First search for the DN with the given uid. */
ld = ldap_init_and_bind (manager,
manager->ldap_host,
#ifdef WIN32
manager->use_ssl,
#endif
manager->user_dn,
manager->password);
if (!ld) {
ccnet_warning ("Please check USER_DN and PASSWORD settings.\n");
return -1;
}
filter = g_string_new (NULL);
if (!manager->filter)
g_string_printf (filter, "(%s=%s)", manager->login_attr, uid);
else
g_string_printf (filter, "(&(%s=%s) (%s))",
manager->login_attr, uid, manager->filter);
filter_str = g_string_free (filter, FALSE);
attrs[0] = manager->login_attr;
attrs[1] = NULL;
char **base;
for (base = manager->base_list; *base; base++) {
res = ldap_search_s (ld, *base, LDAP_SCOPE_SUBTREE,
filter_str, attrs, 0, &msg);
if (res != LDAP_SUCCESS) {
ccnet_warning ("ldap_search user '%s=%s' failed for base %s: %s.\n",
manager->login_attr, uid, *base, ldap_err2string(res));
ccnet_warning ("Please check BASE setting in ccnet.conf.\n");
ret = -1;
ldap_msgfree (msg);
goto out;
}
entry = ldap_first_entry (ld, msg);
if (entry) {
dn = ldap_get_dn (ld, entry);
ldap_msgfree (msg);
break;
}
ldap_msgfree (msg);
}
if (!dn) {
ccnet_debug ("Cannot find user %s in LDAP.\n", uid);
ret = -1;
goto out;
}
/* Then bind the DN with password. */
ldap_unbind_s (ld);
ld = ldap_init_and_bind (manager,
manager->ldap_host,
#ifdef WIN32
manager->use_ssl,
#endif
dn, password);
if (!ld) {
ccnet_debug ("Password incorrect for %s in LDAP.\n", uid);
ret = -1;
}
out:
ldap_memfree (dn);
g_free (filter_str);
if (ld) ldap_unbind_s (ld);
return ret;
}
/*
* @uid: user's uid, list all users if * is passed in.
*/
static GList *ldap_list_users (CcnetUserManager *manager, const char *uid,
int start, int limit)
{
LDAP *ld = NULL;
GList *ret = NULL;
int res;
GString *filter;
char *filter_str;
char *attrs[2];
LDAPMessage *msg = NULL, *entry;
ld = ldap_init_and_bind (manager,
manager->ldap_host,
#ifdef WIN32
manager->use_ssl,
#endif
manager->user_dn,
manager->password);
if (!ld) {
ccnet_warning ("Please check USER_DN and PASSWORD settings.\n");
return NULL;
}
filter = g_string_new (NULL);
if (!manager->filter)
g_string_printf (filter, "(%s=%s)", manager->login_attr, uid);
else
g_string_printf (filter, "(&(%s=%s) (%s))",
manager->login_attr, uid, manager->filter);
filter_str = g_string_free (filter, FALSE);
attrs[0] = manager->login_attr;
attrs[1] = NULL;
int i = 0;
if (start == -1)
start = 0;
char **base;
for (base = manager->base_list; *base; ++base) {
res = ldap_search_s (ld, *base, LDAP_SCOPE_SUBTREE,
filter_str, attrs, 0, &msg);
if (res != LDAP_SUCCESS) {
ccnet_warning ("ldap_search user '%s=%s' failed for base %s: %s.\n",
manager->login_attr, uid, *base, ldap_err2string(res));
ccnet_warning ("Please check BASE setting in ccnet.conf.\n");
ret = NULL;
ldap_msgfree (msg);
goto out;
}
for (entry = ldap_first_entry (ld, msg);
entry != NULL;
entry = ldap_next_entry (ld, entry), ++i) {
char *attr;
char **vals;
BerElement *ber;
CcnetEmailUser *user;
if (i < start)
continue;
if (limit >= 0 && i >= start + limit) {
ldap_msgfree (msg);
goto out;
}
attr = ldap_first_attribute (ld, entry, &ber);
vals = ldap_get_values (ld, entry, attr);
char *email_l = g_ascii_strdown (vals[0], -1);
user = g_object_new (CCNET_TYPE_EMAIL_USER,
"id", 0,
"email", email_l,
"is_staff", FALSE,
"is_active", TRUE,
"ctime", (gint64)0,
"source", "LDAP",
"password", "!",
NULL);
g_free (email_l);
ret = g_list_prepend (ret, user);
ldap_memfree (attr);
ldap_value_free (vals);
ber_free (ber, 0);
}
ldap_msgfree (msg);
}
out:
g_free (filter_str);
if (ld) ldap_unbind_s (ld);
return ret;
}
#endif /* HAVE_LDAP */
/* -------- DB Operations -------- */
static int check_db_table (SeafDB *db)
{
char *sql;
int db_type = seaf_db_type (db);
if (db_type == SEAF_DB_TYPE_MYSQL) {
sql = "CREATE TABLE IF NOT EXISTS EmailUser ("
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
"email VARCHAR(255), passwd VARCHAR(256), "
"is_staff BOOL NOT NULL, is_active BOOL NOT NULL, "
"ctime BIGINT, reference_id VARCHAR(255),"
"UNIQUE INDEX (email), UNIQUE INDEX (reference_id))"
"ENGINE=INNODB";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS Binding (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
"email VARCHAR(255), peer_id CHAR(41),"
"UNIQUE INDEX (peer_id), INDEX (email(20)))"
"ENGINE=INNODB";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS UserRole ("
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
"email VARCHAR(255), role VARCHAR(255), UNIQUE INDEX (email)) "
"ENGINE=INNODB";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS LDAPUsers ("
"id BIGINT PRIMARY KEY AUTO_INCREMENT, "
"email VARCHAR(255) NOT NULL, password varchar(255) NOT NULL, "
"is_staff BOOL NOT NULL, is_active BOOL NOT NULL, extra_attrs TEXT, "
"reference_id VARCHAR(255), "
"UNIQUE INDEX(email), UNIQUE INDEX (reference_id)) ENGINE=INNODB";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS LDAPConfig ( "
"id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, cfg_group VARCHAR(255) NOT NULL,"
"cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER) ENGINE=INNODB";
if (seaf_db_query (db, sql) < 0)
return -1;
} else if (db_type == SEAF_DB_TYPE_SQLITE) {
sql = "CREATE TABLE IF NOT EXISTS EmailUser ("
"id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
"email TEXT, passwd TEXT, is_staff bool NOT NULL, "
"is_active bool NOT NULL, ctime INTEGER, "
"reference_id TEXT)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE UNIQUE INDEX IF NOT EXISTS email_index on EmailUser (email)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE UNIQUE INDEX IF NOT EXISTS reference_id_index on EmailUser (reference_id)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS Binding (email TEXT, peer_id TEXT)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE INDEX IF NOT EXISTS email_index on Binding (email)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE UNIQUE INDEX IF NOT EXISTS peer_index on Binding (peer_id)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS UserRole (email TEXT, role TEXT)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE INDEX IF NOT EXISTS userrole_email_index on UserRole (email)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE UNIQUE INDEX IF NOT EXISTS userrole_userrole_index on UserRole (email, role)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS LDAPUsers ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"email TEXT NOT NULL, password TEXT NOT NULL, "
"is_staff BOOL NOT NULL, is_active BOOL NOT NULL, extra_attrs TEXT, "
"reference_id TEXT)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE UNIQUE INDEX IF NOT EXISTS ldapusers_email_index on LDAPUsers(email)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE UNIQUE INDEX IF NOT EXISTS ldapusers_reference_id_index on LDAPUsers(reference_id)";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS LDAPConfig (cfg_group VARCHAR(255) NOT NULL,"
"cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER)";
if (seaf_db_query (db, sql) < 0)
return -1;
} else if (db_type == SEAF_DB_TYPE_PGSQL) {
sql = "CREATE TABLE IF NOT EXISTS EmailUser ("
"id SERIAL PRIMARY KEY, "
"email VARCHAR(255), passwd VARCHAR(256), "
"is_staff INTEGER NOT NULL, is_active INTEGER NOT NULL, "
"ctime BIGINT, reference_id VARCHAR(255), UNIQUE (email))";
if (seaf_db_query (db, sql) < 0)
return -1;
//if (!pgsql_index_exists (db, "emailuser_reference_id_idx")) {
// sql = "CREATE UNIQUE INDEX emailuser_reference_id_idx ON EmailUser (reference_id)";
// if (seaf_db_query (db, sql) < 0)
// return -1;
//}
sql = "CREATE TABLE IF NOT EXISTS Binding (email VARCHAR(255), peer_id CHAR(41),"
"UNIQUE (peer_id))";
if (seaf_db_query (db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS UserRole (email VARCHAR(255), "
" role VARCHAR(255), UNIQUE (email, role))";
if (seaf_db_query (db, sql) < 0)
return -1;
//if (!pgsql_index_exists (db, "userrole_email_idx")) {
// sql = "CREATE INDEX userrole_email_idx ON UserRole (email)";
// if (seaf_db_query (db, sql) < 0)
// return -1;
//}
sql = "CREATE TABLE IF NOT EXISTS LDAPUsers ("
"id SERIAL PRIMARY KEY, "
"email VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, "
"is_staff SMALLINT NOT NULL, is_active SMALLINT NOT NULL, extra_attrs TEXT,"
"reference_id VARCHAR(255))";
if (seaf_db_query (db, sql) < 0)
return -1;
//if (!pgsql_index_exists (db, "ldapusers_email_idx")) {
// sql = "CREATE UNIQUE INDEX ldapusers_email_idx ON LDAPUsers (email)";
// if (seaf_db_query (db, sql) < 0)
// return -1;
//}
//if (!pgsql_index_exists (db, "ldapusers_reference_id_idx")) {
// sql = "CREATE UNIQUE INDEX ldapusers_reference_id_idx ON LDAPUsers (reference_id)";
// if (seaf_db_query (db, sql) < 0)
// return -1;
//}
sql = "CREATE TABLE IF NOT EXISTS LDAPConfig (cfg_group VARCHAR(255) NOT NULL,"
"cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER)";
if (seaf_db_query (db, sql) < 0)
return -1;
}
return 0;
}
static CcnetDB *
open_sqlite_db (CcnetUserManager *manager)
{
CcnetDB *db = NULL;
char *db_dir;
char *db_path;
db_dir = g_build_filename (manager->session->ccnet_dir, "PeerMgr", NULL);
if (checkdir_with_mkdir(db_dir) < 0) {
ccnet_error ("Cannot open db dir %s: %s\n", db_dir,
strerror(errno));
return NULL;
}
g_free (db_dir);
db_path = g_build_filename (manager->session->ccnet_dir, "PeerMgr",
"usermgr.db", NULL);
db = seaf_db_new_sqlite (db_path, DEFAULT_MAX_CONNECTIONS);
g_free (db_path);
return db;
}
static int
open_db (CcnetUserManager *manager)
{
CcnetDB *db = NULL;
switch (seaf_db_type(manager->session->ccnet_db)) {
/* To be compatible with the db file layout of 0.9.1 version,
* we don't use conf-dir/ccnet.db for user and peer info, but
* user conf-dir/PeerMgr/peermgr.db and conf-dir/PeerMgr/usermgr.db instead.
*/
case SEAF_DB_TYPE_SQLITE:
db = open_sqlite_db (manager);
break;
case SEAF_DB_TYPE_PGSQL:
case SEAF_DB_TYPE_MYSQL:
db = manager->session->ccnet_db;
break;
}
if (!db)
return -1;
manager->priv->db = db;
if ((manager->session->ccnet_create_tables || seaf_db_type(db) == SEAF_DB_TYPE_PGSQL)
&& check_db_table (db) < 0) {
ccnet_warning ("Failed to create user db tables.\n");
return -1;
}
return 0;
}
/* -------- EmailUser Management -------- */
/* This fixed salt is used in very early versions. It's kept for compatibility.
* For the current password hashing algorithm, please see hash_password_pbkdf2_sha256()
*/
static unsigned char salt[8] = { 0xdb, 0x91, 0x45, 0xc3, 0x06, 0xc7, 0xcc, 0x26 };
static void
hash_password (const char *passwd, char *hashed_passwd)
{
unsigned char sha1[20];
SHA_CTX s;
SHA1_Init (&s);
SHA1_Update (&s, passwd, strlen(passwd));
SHA1_Final (sha1, &s);
rawdata_to_hex (sha1, hashed_passwd, 20);
}
static void
hash_password_salted (const char *passwd, char *hashed_passwd)
{
unsigned char sha[SHA256_DIGEST_LENGTH];
SHA256_CTX s;
SHA256_Init (&s);
SHA256_Update (&s, passwd, strlen(passwd));
SHA256_Update (&s, salt, sizeof(salt));
SHA256_Final (sha, &s);
rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH);
}
static void
hash_password_pbkdf2_sha256 (const char *passwd,
int iterations,
char **db_passwd)
{
guint8 sha[SHA256_DIGEST_LENGTH];
guint8 salt[SHA256_DIGEST_LENGTH];
char hashed_passwd[SHA256_DIGEST_LENGTH*2+1];
char salt_str[SHA256_DIGEST_LENGTH*2+1];
if (!RAND_bytes (salt, sizeof(salt))) {
ccnet_warning ("Failed to generate salt "
"with RAND_bytes(), use RAND_pseudo_bytes().\n");
RAND_pseudo_bytes (salt, sizeof(salt));
}
PKCS5_PBKDF2_HMAC (passwd, strlen(passwd),
salt, sizeof(salt),
iterations,
EVP_sha256(),
sizeof(sha), sha);
rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH);
rawdata_to_hex (salt, salt_str, SHA256_DIGEST_LENGTH);
/* Encode password hash related information into one string, similar to Django. */
GString *buf = g_string_new (NULL);
g_string_printf (buf, "PBKDF2SHA256$%d$%s$%s",
iterations, salt_str, hashed_passwd);
*db_passwd = g_string_free (buf, FALSE);
}
static gboolean
validate_passwd_pbkdf2_sha256 (const char *passwd, const char *db_passwd)
{
char **tokens;
char *salt_str, *hash;
int iter;
guint8 sha[SHA256_DIGEST_LENGTH];
guint8 salt[SHA256_DIGEST_LENGTH];
char hashed_passwd[SHA256_DIGEST_LENGTH*2+1];
tokens = g_strsplit (db_passwd, "$", -1);
if (!tokens || g_strv_length (tokens) != 4) {
ccnet_warning ("Invalide db passwd format %s.\n", db_passwd);
return FALSE;
}
iter = atoi (tokens[1]);
salt_str = tokens[2];
hash = tokens[3];
hex_to_rawdata (salt_str, salt, SHA256_DIGEST_LENGTH);
PKCS5_PBKDF2_HMAC (passwd, strlen(passwd),
salt, sizeof(salt),
iter,
EVP_sha256(),
sizeof(sha), sha);
rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH);
gboolean ret = (strcmp (hash, hashed_passwd) == 0);
g_strfreev (tokens);
return ret;
}
static gboolean
validate_passwd (const char *passwd, const char *stored_passwd,
gboolean *need_upgrade)
{
char hashed_passwd[SHA256_DIGEST_LENGTH * 2 + 1];
int hash_len = strlen(stored_passwd);
*need_upgrade = FALSE;
if (hash_len == SHA256_DIGEST_LENGTH * 2) {
hash_password_salted (passwd, hashed_passwd);
*need_upgrade = TRUE;
} else if (hash_len == SHA_DIGEST_LENGTH * 2) {
hash_password (passwd, hashed_passwd);
*need_upgrade = TRUE;
} else {
return validate_passwd_pbkdf2_sha256 (passwd, stored_passwd);
}
if (strcmp (hashed_passwd, stored_passwd) == 0)
return TRUE;
else
return FALSE;
}
static int
update_user_passwd (CcnetUserManager *manager,
const char *email, const char *passwd)
{
CcnetDB *db = manager->priv->db;
char *db_passwd = NULL;
int ret;
hash_password_pbkdf2_sha256 (passwd, manager->passwd_hash_iter,
&db_passwd);
/* convert email to lower case for case insensitive lookup. */
char *email_down = g_ascii_strdown (email, strlen(email));
ret = seaf_db_statement_query (db,
"UPDATE EmailUser SET passwd=? WHERE email=?",
2, "string", db_passwd, "string", email_down);
g_free (db_passwd);
g_free (email_down);
if (ret < 0)
return ret;
return 0;
}
int
ccnet_user_manager_add_emailuser (CcnetUserManager *manager,
const char *email,
const char *passwd,
int is_staff, int is_active)
{
CcnetDB *db = manager->priv->db;
gint64 now = get_current_time();
char *db_passwd = NULL;
int ret;
if (!check_user_number (manager, FALSE)) {
return -1;
}
/* A user with unhashed "!" as password cannot be logged in.
* Such users are created for book keeping, such as users from
* Shibboleth.
*/
if (g_strcmp0 (passwd, "!") != 0)
hash_password_pbkdf2_sha256 (passwd, manager->passwd_hash_iter,
&db_passwd);
else
db_passwd = g_strdup(passwd);
/* convert email to lower case for case insensitive lookup. */
char *email_down = g_ascii_strdown (email, strlen(email));
ret = seaf_db_statement_query (db,
"INSERT INTO EmailUser(email, passwd, is_staff, "
"is_active, ctime) VALUES (?, ?, ?, ?, ?)",
5, "string", email_down, "string", db_passwd,
"int", is_staff, "int", is_active, "int64", now);
g_free (db_passwd);
g_free (email_down);
if (ret < 0)
return ret;
return 0;
}
int
ccnet_user_manager_remove_emailuser (CcnetUserManager *manager,
const char *source,
const char *email)
{
CcnetDB *db = manager->priv->db;
int ret;
seaf_db_statement_query (db,
"DELETE FROM UserRole WHERE email=?",
1, "string", email);
if (strcmp (source, "DB") == 0) {
ret = seaf_db_statement_query (db,
"DELETE FROM EmailUser WHERE email=?",
1, "string", email);
return ret;
}
#ifdef HAVE_LDAP
if (strcmp (source, "LDAP") == 0 && manager->use_ldap) {
ret = seaf_db_statement_query (db,
"DELETE FROM LDAPUsers WHERE email=?",
1, "string", email);
return ret;
}
#endif
return -1;
}
static gboolean
get_password (CcnetDBRow *row, void *data)
{
char **p_passwd = data;
*p_passwd = g_strdup(seaf_db_row_get_column_text (row, 0));
return FALSE;
}
int
ccnet_user_manager_validate_emailuser (CcnetUserManager *manager,
const char *email,
const char *passwd)
{
CcnetDB *db = manager->priv->db;
int ret = -1;
char *sql;
char *email_down;
char *login_id;
char *stored_passwd = NULL;
gboolean need_upgrade = FALSE;
/* Users with password "!" are for internal book keeping only. */
if (g_strcmp0 (passwd, "!") == 0)
return -1;
login_id = ccnet_user_manager_get_login_id (manager, email);
if (!login_id) {
ccnet_warning ("Failed to get login_id for %s\n", email);
return -1;
}
#ifdef HAVE_LDAP
if (manager->use_ldap) {
if (ldap_verify_user_password (manager, login_id, passwd) == 0) {
ret = 0;
goto out;
}
}
#endif
sql = "SELECT passwd FROM EmailUser WHERE email=?";
if (seaf_db_statement_foreach_row (db, sql,
get_password, &stored_passwd,
1, "string", login_id) > 0) {
if (validate_passwd (passwd, stored_passwd, &need_upgrade)) {
if (need_upgrade)
update_user_passwd (manager, login_id, passwd);
ret = 0;
goto out;
} else {
goto out;
}
}
email_down = g_ascii_strdown (email, strlen(login_id));
if (seaf_db_statement_foreach_row (db, sql,
get_password, &stored_passwd,
1, "string", email_down) > 0) {
g_free (email_down);
if (validate_passwd (passwd, stored_passwd, &need_upgrade)) {
if (need_upgrade)
update_user_passwd (manager, login_id, passwd);
ret = 0;
goto out;
} else {
goto out;
}
}
g_free (email_down);
out:
g_free (login_id);
g_free (stored_passwd);
return ret;
}
static gboolean
get_emailuser_cb (CcnetDBRow *row, void *data)
{
CcnetEmailUser **p_emailuser = data;
int id = seaf_db_row_get_column_int (row, 0);
const char *email = (const char *)seaf_db_row_get_column_text (row, 1);
int is_staff = seaf_db_row_get_column_int (row, 2);
int is_active = seaf_db_row_get_column_int (row, 3);
gint64 ctime = seaf_db_row_get_column_int64 (row, 4);
const char *password = seaf_db_row_get_column_text (row, 5);
const char *reference_id = seaf_db_row_get_column_text (row, 6);
const char *role = seaf_db_row_get_column_text (row, 7);
char *email_l = g_ascii_strdown (email, -1);
*p_emailuser = g_object_new (CCNET_TYPE_EMAIL_USER,
"id", id,
"email", email_l,
"is_staff", is_staff,
"is_active", is_active,
"ctime", ctime,
"source", "DB",
"password", password,
"reference_id", reference_id,
"role", role ? role : "",
NULL);
g_free (email_l);
return FALSE;
}
static char*
ccnet_user_manager_get_role_emailuser (CcnetUserManager *manager,
const char* email);
static gboolean
get_ldap_emailuser_cb (CcnetDBRow *row, void *data)
{
CcnetEmailUser **p_emailuser = data;
int id = seaf_db_row_get_column_int (row, 0);
const char *email = (const char *)seaf_db_row_get_column_text (row, 1);
int is_staff = seaf_db_row_get_column_int (row, 2);
int is_active = seaf_db_row_get_column_int (row, 3);
const char *reference_id = seaf_db_row_get_column_text (row, 4);
const char *role = seaf_db_row_get_column_text (row, 5);
*p_emailuser = g_object_new (CCNET_TYPE_EMAIL_USER,
"id", id,
"email", email,
"is_staff", is_staff,
"is_active", is_active,
"ctime", (gint64)0,
"source", "LDAPImport",
"password", "!",
"reference_id", reference_id,
"role", role ? role : "",
NULL);
return FALSE;
}
static CcnetEmailUser*
get_emailuser (CcnetUserManager *manager,
const char *email,
gboolean import)
{
CcnetDB *db = manager->priv->db;
char *sql;
CcnetEmailUser *emailuser = NULL;
char *email_down;
sql = "SELECT e.id, e.email, is_staff, is_active, ctime, passwd, reference_id, role "
" FROM EmailUser e LEFT JOIN UserRole ON e.email = UserRole.email "
" WHERE e.email=?";
if (seaf_db_statement_foreach_row (db, sql, get_emailuser_cb, &emailuser,
1, "string", email) > 0) {
return emailuser;
}
email_down = g_ascii_strdown (email, strlen(email));
if (seaf_db_statement_foreach_row (db, sql, get_emailuser_cb, &emailuser,
1, "string", email_down) > 0) {
g_free (email_down);
return emailuser;
}
#ifdef HAVE_LDAP
if (manager->use_ldap) {
int ret = seaf_db_statement_foreach_row (db,
"SELECT l.id, l.email, is_staff, is_active, "
"reference_id, role "
"FROM LDAPUsers l LEFT JOIN UserRole ON "
"l.email = UserRole.email WHERE l.email = ?",
get_ldap_emailuser_cb,
&emailuser, 1, "string", email_down);
if (ret < 0) {
ccnet_warning ("get ldapuser from db failed.\n");
g_free (email_down);
return NULL;
}
if (!emailuser) {
GList *users, *ptr;
users = ldap_list_users (manager, email, -1, -1);
if (!users) {
/* Only print warning if this function is called in login. */
if (import)
ccnet_warning ("Cannot find user %s in LDAP.\n", email);
g_free (email_down);
return NULL;
}
emailuser = users->data;
/* Free all except the first user. */
for (ptr = users->next; ptr; ptr = ptr->next)
g_object_unref (ptr->data);
g_list_free (users);
if (import) {
if (!check_user_number (manager, FALSE)) {
g_free (email_down);
g_object_unref (emailuser);
return NULL;
}
// add user to LDAPUsers
ret = add_ldapuser (manager->priv->db, email_down, "",
FALSE, TRUE, NULL);
if (ret < 0) {
ccnet_warning ("add ldapuser to db failed.\n");
g_free (email_down);
g_object_unref (emailuser);
return NULL;
}
g_object_set (emailuser, "id", ret, NULL);
}
}
g_free (email_down);
return emailuser;
}
#endif
g_free (email_down);
return NULL;
}
CcnetEmailUser*
ccnet_user_manager_get_emailuser (CcnetUserManager *manager,
const char *email)
{
return get_emailuser (manager, email, FALSE);
}
CcnetEmailUser*
ccnet_user_manager_get_emailuser_with_import (CcnetUserManager *manager,
const char *email)
{
return get_emailuser (manager, email, TRUE);
}
CcnetEmailUser*
ccnet_user_manager_get_emailuser_by_id (CcnetUserManager *manager, int id)
{
CcnetDB *db = manager->priv->db;
char *sql;
CcnetEmailUser *emailuser = NULL;
sql = "SELECT e.id, e.email, is_staff, is_active, ctime, passwd, reference_id, role "
" FROM EmailUser e LEFT JOIN UserRole ON e.email = UserRole.email "
" WHERE e.id=?";
if (seaf_db_statement_foreach_row (db, sql, get_emailuser_cb, &emailuser,
1, "int", id) < 0)
return NULL;
return emailuser;
}
static gboolean
get_emailusers_cb (CcnetDBRow *row, void *data)
{
GList **plist = data;
CcnetEmailUser *emailuser;
int id = seaf_db_row_get_column_int (row, 0);
const char *email = (const char *)seaf_db_row_get_column_text (row, 1);
int is_staff = seaf_db_row_get_column_int (row, 2);
int is_active = seaf_db_row_get_column_int (row, 3);
gint64 ctime = seaf_db_row_get_column_int64 (row, 4);
const char *role = (const char *)seaf_db_row_get_column_text (row, 5);
const char *password = seaf_db_row_get_column_text (row, 6);
char *email_l = g_ascii_strdown (email, -1);
emailuser = g_object_new (CCNET_TYPE_EMAIL_USER,
"id", id,
"email", email_l,
"is_staff", is_staff,
"is_active", is_active,
"ctime", ctime,
"role", role ? role : "",
"source", "DB",
"password", password,
NULL);
g_free (email_l);
*plist = g_list_prepend (*plist, emailuser);
return TRUE;
}
static gboolean
get_ldap_emailusers_cb (CcnetDBRow *row, void *data)
{
GList **plist = data;
CcnetEmailUser *emailuser = NULL;
int id = seaf_db_row_get_column_int (row, 0);
const char *email = (const char *)seaf_db_row_get_column_text (row, 1);
int is_staff = seaf_db_row_get_column_int (row, 2);
int is_active = seaf_db_row_get_column_int (row, 3);
const char *role = seaf_db_row_get_column_text (row, 4);
emailuser = g_object_new (CCNET_TYPE_EMAIL_USER,
"id", id,
"email", email,
"is_staff", is_staff,
"is_active", is_active,
"ctime", (gint64)0,
"role", role ? role : "",
"source", "LDAPImport",
"password", "!",
NULL);
if (!emailuser)
return FALSE;
*plist = g_list_prepend (*plist, emailuser);
return TRUE;
}
GList*
ccnet_user_manager_get_emailusers (CcnetUserManager *manager,
const char *source,
int start, int limit,
const char *status)
{
CcnetDB *db = manager->priv->db;
const char *status_condition = "";
char *sql = NULL;
GList *ret = NULL;
int rc;
#ifdef HAVE_LDAP
if (manager->use_ldap) {
GList *users = NULL;
if (g_strcmp0 (source, "LDAP") == 0) {
users = ldap_list_users (manager, "*", start, limit);
return g_list_reverse (users);
} else if (g_strcmp0 (source, "LDAPImport") == 0) {
if (start == -1 && limit == -1) {
if (g_strcmp0(status, "active") == 0)
status_condition = "WHERE t1.is_active = 1";
else if (g_strcmp0(status, "inactive") == 0)
status_condition = "WHERE t1.is_active = 0";
sql = g_strdup_printf ("SELECT t1.id, t1.email, t1.is_staff, "
"t1.is_active, t2.role "
"FROM LDAPUsers t1 LEFT JOIN UserRole t2 "
"ON t1.email = t2.email %s",
status_condition);
rc = seaf_db_statement_foreach_row (db,
sql,
get_ldap_emailusers_cb,
&users, 0);
g_free (sql);
} else {
if (g_strcmp0(status, "active") == 0)
status_condition = "WHERE t1.is_active = 1";
else if (g_strcmp0(status, "inactive") == 0)
status_condition = "WHERE t1.is_active = 0";
sql = g_strdup_printf ("SELECT t1.id, t1.email, t1.is_staff, "
"t1.is_active, t2.role "
"FROM LDAPUsers t1 LEFT JOIN UserRole t2 "
"ON t1.email = t2.email %s LIMIT ? OFFSET ?",
status_condition);
rc = seaf_db_statement_foreach_row (db,
sql,
get_ldap_emailusers_cb,
&users, 2, "int", limit, "int", start);
g_free (sql);
}
if (rc < 0) {
while (users) {
g_object_unref (users->data);
users = g_list_delete_link (users, users);
}
return NULL;
}
return g_list_reverse (users);
}
}
#endif
if (g_strcmp0 (source, "DB") != 0)
return NULL;
if (start == -1 && limit == -1) {
if (g_strcmp0(status, "active") == 0)
status_condition = "WHERE t1.is_active = 1";
else if (g_strcmp0(status, "inactive") == 0)
status_condition = "WHERE t1.is_active = 0";
sql = g_strdup_printf ("SELECT t1.id, t1.email, "
"t1.is_staff, t1.is_active, t1.ctime, "
"t2.role, t1.passwd FROM EmailUser t1 "
"LEFT JOIN UserRole t2 "
"ON t1.email = t2.email %s "
"WHERE t1.email NOT LIKE '%%@seafile_group'",
status_condition);
rc = seaf_db_statement_foreach_row (db,
sql,
get_emailusers_cb, &ret,
0);
g_free (sql);
} else {
if (g_strcmp0(status, "active") == 0)
status_condition = "WHERE t1.is_active = 1";
else if (g_strcmp0(status, "inactive") == 0)
status_condition = "WHERE t1.is_active = 0";
sql = g_strdup_printf ("SELECT t1.id, t1.email, "
"t1.is_staff, t1.is_active, t1.ctime, "
"t2.role, t1.passwd FROM EmailUser t1 "
"LEFT JOIN UserRole t2 "
"ON t1.email = t2.email %s "
"WHERE t1.email NOT LIKE '%%@seafile_group' "
"ORDER BY t1.id LIMIT ? OFFSET ?",
status_condition);
rc = seaf_db_statement_foreach_row (db,
sql,
get_emailusers_cb, &ret,
2, "int", limit, "int", start);
g_free (sql);
}
if (rc < 0) {
while (ret != NULL) {
g_object_unref (ret->data);
ret = g_list_delete_link (ret, ret);
}
return NULL;
}
return g_list_reverse (ret);
}
GList*
ccnet_user_manager_search_emailusers (CcnetUserManager *manager,
const char *source,
const char *keyword,
int start, int limit)
{
CcnetDB *db = manager->priv->db;
GList *ret = NULL;
int rc;
char *db_patt = g_strdup_printf ("%%%s%%", keyword);
#ifdef HAVE_LDAP
if (manager->use_ldap) {
if (strcmp (source, "LDAP") == 0) {
if (start == -1 && limit == -1) {
rc = seaf_db_statement_foreach_row (db,
"SELECT t1.id, t1.email, t1.is_staff, "
"t1.is_active, t2.role "
"FROM LDAPUsers t1 LEFT JOIN UserRole t2 "
"ON t1.email = t2.email WHERE t1.email LIKE ?",
get_ldap_emailusers_cb,
&ret, 1, "string", db_patt);
} else {
rc = seaf_db_statement_foreach_row (db,
"SELECT t1.id, t1.email, t1.is_staff, "
"t1.is_active, t2.role "
"FROM LDAPUsers t1 LEFT JOIN UserRole t2 "
"ON t1.email = t2.email WHERE t1.email LIKE ? "
"LIMIT ? OFFSET ?",
get_ldap_emailusers_cb,
&ret, 3, "string", db_patt,
"int", limit, "int", start);
}
g_free (db_patt);
if (rc < 0) {
while (ret) {
g_object_unref (ret->data);
ret = g_list_delete_link (ret, ret);
}
return NULL;
}
return g_list_reverse (ret);
}
}
#endif
if (strcmp (source, "DB") != 0) {
g_free (db_patt);
return NULL;
}
if (start == -1 && limit == -1)
rc = seaf_db_statement_foreach_row (db,
"SELECT t1.id, t1.email, "
"t1.is_staff, t1.is_active, t1.ctime, "
"t2.role, t1.passwd FROM EmailUser t1 "
"LEFT JOIN UserRole t2 "
"ON t1.email = t2.email "
"WHERE t1.Email LIKE ? "
"AND t1.email NOT LIKE '%%@seafile_group' "
"ORDER BY t1.id",
get_emailusers_cb, &ret,
1, "string", db_patt);
else
rc = seaf_db_statement_foreach_row (db,
"SELECT t1.id, t1.email, "
"t1.is_staff, t1.is_active, t1.ctime, "
"t2.role, t1.passwd FROM EmailUser t1 "
"LEFT JOIN UserRole t2 "
"ON t1.email = t2.email "
"WHERE t1.Email LIKE ? "
"AND t1.email NOT LIKE '%%@seafile_group' "
"ORDER BY t1.id LIMIT ? OFFSET ?",
get_emailusers_cb, &ret,
3, "string", db_patt,
"int", limit, "int", start);
g_free (db_patt);
if (rc < 0) {
while (ret != NULL) {
g_object_unref (ret->data);
ret = g_list_delete_link (ret, ret);
}
return NULL;
}
return g_list_reverse (ret);
}
GList*
ccnet_user_manager_search_ldapusers (CcnetUserManager *manager,
const char *keyword,
int start, int limit)
{
GList *ret = NULL;
#ifdef HAVE_LDAP
if (!manager->use_ldap) {
return NULL;
}
char *ldap_patt = g_strdup_printf ("*%s*", keyword);
ret = ldap_list_users (manager, ldap_patt, start, limit);
g_free (ldap_patt);
#endif
return ret;
}
gint64
ccnet_user_manager_count_emailusers (CcnetUserManager *manager, const char *source)
{
CcnetDB* db = manager->priv->db;
char sql[512];
gint64 ret;
#ifdef HAVE_LDAP
if (manager->use_ldap && g_strcmp0(source, "LDAP") == 0) {
gint64 ret = seaf_db_get_int64 (db, "SELECT COUNT(id) FROM LDAPUsers WHERE is_active = 1");
if (ret < 0)
return -1;
return ret;
}
#endif
if (g_strcmp0 (source, "DB") != 0)
return -1;
snprintf (sql, 512, "SELECT COUNT(id) FROM EmailUser WHERE is_active = 1");
ret = seaf_db_get_int64 (db, sql);
if (ret < 0)
return -1;
return ret;
}
gint64
ccnet_user_manager_count_inactive_emailusers (CcnetUserManager *manager, const char *source)
{
CcnetDB* db = manager->priv->db;
char sql[512];
gint64 ret;
#ifdef HAVE_LDAP
if (manager->use_ldap && g_strcmp0(source, "LDAP") == 0) {
gint64 ret = seaf_db_get_int64 (db, "SELECT COUNT(id) FROM LDAPUsers WHERE is_active = 0");
if (ret < 0)
return -1;
return ret;
}
#endif
if (g_strcmp0 (source, "DB") != 0)
return -1;
snprintf (sql, 512, "SELECT COUNT(id) FROM EmailUser WHERE is_active = 0");
ret = seaf_db_get_int64 (db, sql);
if (ret < 0)
return -1;
return ret;
}
#if 0
GList*
ccnet_user_manager_filter_emailusers_by_emails(CcnetUserManager *manager,
const char *emails)
{
CcnetDB *db = manager->priv->db;
char *copy = g_strdup (emails), *saveptr;
GList *ret = NULL;
#ifdef HAVE_LDAP
if (manager->use_ldap)
return NULL; /* todo */
#endif
GString *sql = g_string_new(NULL);
g_string_append (sql, "SELECT * FROM EmailUser WHERE Email IN (");
char *name = strtok_r (copy, ", ", &saveptr);
while (name != NULL) {
g_string_append_printf (sql, "'%s',", name);
name = strtok_r (NULL, ", ", &saveptr);
}
g_string_erase (sql, sql->len-1, 1); /* remove last "," */
g_string_append (sql, ")");
if (seaf_db_foreach_selected_row (db, sql->str, get_emailusers_cb,
&ret) < 0) {
while (ret != NULL) {
g_object_unref (ret->data);
ret = g_list_delete_link (ret, ret);
}
return NULL;
}
g_free (copy);
g_string_free (sql, TRUE);
return g_list_reverse (ret);
}
#endif
int
ccnet_user_manager_update_emailuser (CcnetUserManager *manager,
const char *source,
int id, const char* passwd,
int is_staff, int is_active)
{
CcnetDB* db = manager->priv->db;
char *db_passwd = NULL;
// in case set user user1 to inactive, then add another active user user2,
// if current user num already the max user num,
// then reset user1 to active should fail
if (is_active && !check_user_number (manager, FALSE)) {
return -1;
}
if (strcmp (source, "DB") == 0) {
if (g_strcmp0 (passwd, "!") == 0) {
/* Don't update passwd if it starts with '!' */
return seaf_db_statement_query (db, "UPDATE EmailUser SET is_staff=?, "
"is_active=? WHERE id=?",
3, "int", is_staff, "int", is_active,
"int", id);
} else {
hash_password_pbkdf2_sha256 (passwd, manager->passwd_hash_iter, &db_passwd);
return seaf_db_statement_query (db, "UPDATE EmailUser SET passwd=?, "
"is_staff=?, is_active=? WHERE id=?",
4, "string", db_passwd, "int", is_staff,
"int", is_active, "int", id);
}
}
#ifdef HAVE_LDAP
if (manager->use_ldap && strcmp (source, "LDAP") == 0) {
return seaf_db_statement_query (db, "UPDATE LDAPUsers SET is_staff=?, "
"is_active=? WHERE id=?",
3, "int", is_staff, "int", is_active,
"int", id);
}
#endif
return -1;
}
static gboolean
get_role_emailuser_cb (CcnetDBRow *row, void *data)
{
*((char **)data) = g_strdup (seaf_db_row_get_column_text (row, 0));
return FALSE;
}
static char*
ccnet_user_manager_get_role_emailuser (CcnetUserManager *manager,
const char* email)
{
CcnetDB *db = manager->priv->db;
const char *sql;
char* role;
sql = "SELECT role FROM UserRole WHERE email=?";
if (seaf_db_statement_foreach_row (db, sql, get_role_emailuser_cb, &role,
1, "string", email) > 0)
return role;
return NULL;
}
int
ccnet_user_manager_update_role_emailuser (CcnetUserManager *manager,
const char* email, const char* role)
{
CcnetDB* db = manager->priv->db;
char *old_role = ccnet_user_manager_get_role_emailuser (manager, email);
if (old_role) {
g_free (old_role);
return seaf_db_statement_query (db, "UPDATE UserRole SET role=? "
"WHERE email=?",
2, "string", role, "string", email);
} else
return seaf_db_statement_query (db, "INSERT INTO UserRole(role, email)"
" VALUES (?, ?)",
2, "string", role, "string", email);
}
GList*
ccnet_user_manager_get_superusers(CcnetUserManager *manager)
{
CcnetDB* db = manager->priv->db;
GList *ret = NULL;
char sql[512];
snprintf (sql, 512,
"SELECT t1.id, t1.email, "
"t1.is_staff, t1.is_active, t1.ctime, "
"t2.role, t1.passwd FROM EmailUser t1 "
"LEFT JOIN UserRole t2 "
"ON t1.email = t2.email "
"WHERE is_staff = 1 AND t1.email NOT LIKE '%%@seafile_group';");
if (seaf_db_foreach_selected_row (db, sql, get_emailusers_cb, &ret) < 0) {
while (ret != NULL) {
g_object_unref (ret->data);
ret = g_list_delete_link (ret, ret);
}
return NULL;
}
if (seaf_db_foreach_selected_row (db,
"SELECT t1.id, t1.email, "
"t1.is_staff, t1.is_active, "
"t2.role FROM LDAPUsers t1 "
"LEFT JOIN UserRole t2 "
"ON t1.email = t2.email "
"WHERE is_staff = 1",
get_ldap_emailusers_cb, &ret) < 0) {
while (ret != NULL) {
g_object_unref (ret->data);
ret = g_list_delete_link (ret, ret);
}
return NULL;
}
return g_list_reverse (ret);
}
int
ccnet_user_manager_set_reference_id (CcnetUserManager *manager,
const char *primary_id,
const char *reference_id,
GError **error)
{
int rc;
char *sql;
gboolean exists, err;
#ifdef HAVE_LDAP
if (manager->use_ldap) {
sql = "SELECT email FROM LDAPUsers WHERE email = ?";
exists = seaf_db_statement_exists (manager->priv->db, sql, &err,
1, "string", primary_id);
if (err)
return -1;
/* Make sure reference_id is unique */
if (exists) {
sql = "SELECT 1 FROM EmailUser e, LDAPUsers l "
"WHERE (e.reference_id=? AND e.email!=?) OR "
"(l.reference_id=? AND l.email!=?) OR "
"(e.email=? AND e.email!=?) OR (l.email=? AND l.email!=?)";
exists = seaf_db_statement_exists (manager->priv->db, sql, &err,
8, "string", reference_id,
"string", primary_id,
"string", reference_id,
"string", primary_id,
"string", reference_id,
"string", primary_id,
"string", reference_id,
"string", primary_id);
if (err)
return -1;
if (exists) {
ccnet_warning ("Failed to set reference id, email '%s' exists\n", reference_id);
return -1;
}
sql = "UPDATE LDAPUsers SET reference_id=? WHERE email=?";
rc = seaf_db_statement_query (manager->priv->db, sql, 2,
"string", reference_id, "string", primary_id);
if (rc < 0){
ccnet_warning ("Failed to set reference id for '%s'\n", primary_id);
}
return rc;
}
}
#endif
sql = "SELECT email FROM EmailUser WHERE email = ?";
exists = seaf_db_statement_exists (manager->priv->db, sql, &err,
1, "string", primary_id);
if (err)
return -1;
/* Make sure reference_id is unique */
if (exists) {
sql = "SELECT 1 FROM EmailUser e, LDAPUsers l "
"WHERE (e.reference_id=? AND e.email!=?) OR "
"(l.reference_id=? AND l.email!=?) OR "
"(e.email=? AND e.email!=?) OR (l.email=? AND l.email!=?)";
exists = seaf_db_statement_exists (manager->priv->db, sql, &err,
8, "string", reference_id,
"string", primary_id,
"string", reference_id,
"string", primary_id,
"string", reference_id,
"string", primary_id,
"string", reference_id,
"string", primary_id);
if (err)
return -1;
if (exists) {
ccnet_warning ("Failed to set reference id, email '%s' exists\n", reference_id);
return -1;
}
sql = "UPDATE EmailUser SET reference_id=? WHERE email=?";
rc = seaf_db_statement_query (manager->priv->db, sql, 2,
"string", reference_id, "string", primary_id);
if (rc < 0){
ccnet_warning ("Failed to set reference id for %s\n", primary_id);
return -1;
}
return rc;
} else {
ccnet_warning ("Failed to set reference id, Primary id '%s' not exists\n", primary_id);
return -1;
}
}
char *
ccnet_user_manager_get_primary_id (CcnetUserManager *manager, const char *email)
{
char *sql;
char *primary_id = NULL;
#ifdef HAVE_LDAP
if (manager->use_ldap) {
sql = "SELECT email FROM LDAPUsers WHERE reference_id=?";
primary_id = seaf_db_statement_get_string (manager->priv->db, sql, 1, "string", email);
if (primary_id)
return primary_id;
}
#endif
sql = "SELECT email FROM EmailUser WHERE reference_id=?";
primary_id = seaf_db_statement_get_string (manager->priv->db, sql, 1, "string", email);
if (primary_id)
return primary_id;
else
return NULL;
}
char *
ccnet_user_manager_get_login_id (CcnetUserManager *manager, const char *primary_id)
{
#ifdef HAVE_LDAP
if (manager->use_ldap) {
char *sql = "SELECT reference_id FROM LDAPUsers WHERE email=?";
char *ldap_login_id = seaf_db_statement_get_string (manager->priv->db, sql, 1, "string", primary_id);
if (ldap_login_id)
return ldap_login_id;
}
#endif
return g_strdup (primary_id);
}
GList *
ccnet_user_manager_get_emailusers_in_list (CcnetUserManager *manager,
const char *source,
const char *user_list,
GError **error)
{
int i;
const char *username;
json_t *j_array = NULL, *j_obj;
json_error_t j_error;
GList *ret = NULL;
const char *args[20];
j_array = json_loadb (user_list, strlen(user_list), 0, &j_error);
if (!j_array) {
g_set_error (error, CCNET_DOMAIN, 0, "Bad args.");
return NULL;
}
/* Query 20 users at most. */
size_t user_num = json_array_size (j_array);
if (user_num > 20) {
g_set_error (error, CCNET_DOMAIN, 0, "Number of users exceeds 20.");
json_decref (j_array);
return NULL;
}
GString *sql = g_string_new ("");
for (i = 0; i < 20; i++) {
if (i < user_num) {
j_obj = json_array_get (j_array, i);
username = json_string_value(j_obj);
args[i] = username;
} else {
args[i] = "";
}
}
#ifdef HAVE_LDAP
if (manager->use_ldap) {
if (strcmp (source, "LDAP") == 0) {
g_string_printf (sql, "SELECT l.id, l.email, is_staff, is_active, role "
"FROM LDAPUsers l LEFT JOIN UserRole r "
"ON l.email = r.email "
"WHERE l.email IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
if (seaf_db_statement_foreach_row (manager->priv->db, sql->str, get_ldap_emailusers_cb, &ret, 20,
"string", args[0], "string", args[1], "string", args[2],
"string", args[3], "string", args[4], "string", args[5],
"string", args[6], "string", args[7], "string", args[8],
"string", args[9], "string", args[10], "string", args[11],
"string", args[12], "string", args[13], "string", args[14],
"string", args[15], "string", args[16], "string", args[17],
"string", args[18], "string", args[19]) < 0)
ccnet_warning("Failed to get users in list %s.\n", user_list);
goto out;
}
}
#endif
if (strcmp (source, "DB") != 0)
goto out;
g_string_printf (sql, "SELECT e.id, e.email, is_staff, is_active, ctime, "
"role, passwd FROM EmailUser e "
"LEFT JOIN UserRole r ON e.email = r.email "
"WHERE e.email IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
if (seaf_db_statement_foreach_row (manager->priv->db, sql->str, get_emailusers_cb, &ret, 20,
"string", args[0], "string", args[1], "string", args[2],
"string", args[3], "string", args[4], "string", args[5],
"string", args[6], "string", args[7], "string", args[8],
"string", args[9], "string", args[10], "string", args[11],
"string", args[12], "string", args[13], "string", args[14],
"string", args[15], "string", args[16], "string", args[17],
"string", args[18], "string", args[19]) < 0)
ccnet_warning("Failed to get users in list %s.\n", user_list);
out:
json_decref (j_array);
g_string_free (sql, TRUE);
return ret;
}