1
0
mirror of https://github.com/haiwen/seafile-server.git synced 2025-09-01 23:46:53 +00:00

Get nickname use seahub API (#670)

* Get nickname use seahub API

* Add parse site_root

* Use regex to match SECRET_KEY

---------

Co-authored-by: 杨赫然 <heran.yang@seafile.com>
This commit is contained in:
feiniks
2024-07-30 17:58:18 +08:00
committed by GitHub
parent 7d26e0a8f4
commit 49c860b50d
11 changed files with 392 additions and 200 deletions

View File

@@ -27,13 +27,12 @@ get_nickname_by_modifier (GHashTable *email_to_nickname, const char *modifier)
return nickname;
}
char *sql = "SELECT nickname from profile_profile WHERE user = ?";
nickname = seaf_db_statement_get_string(seaf->seahub_db, sql, 1, "string", modifier);
nickname = http_tx_manager_get_nickname (modifier);
if (!nickname) {
nickname = modifier;
nickname = g_strdup (modifier);
}
g_hash_table_insert (email_to_nickname, g_strdup(modifier), g_strdup(nickname));
g_hash_table_insert (email_to_nickname, g_strdup(modifier), nickname);
return nickname;
}
@@ -73,7 +72,7 @@ merge_conflict_filename (const char *store_id, int version,
}
nickname = modifier;
if (seaf->seahub_db)
if (seaf->seahub_pk)
nickname = get_nickname_by_modifier (opt->email_to_nickname, modifier);
conflict_name = gen_conflict_path (filename, nickname, mtime);
@@ -106,7 +105,7 @@ merge_conflict_dirname (const char *store_id, int version,
seaf_commit_unref (commit);
nickname = modifier;
if (seaf->seahub_db)
if (seaf->seahub_pk)
nickname = get_nickname_by_modifier (opt->email_to_nickname, modifier);
conflict_name = gen_conflict_path (dirname, nickname, (gint64)time(NULL));

View File

@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *
seafile_session_get_tmp_file_path (SeafileSession *session,
@@ -380,128 +381,64 @@ load_ccnet_database_config (SeafileSession *session)
return ret;
}
static char *
parse_seahub_db_config ()
#ifdef FULL_FEATURE
void
load_seahub_private_key (SeafileSession *session, const char *conf_dir)
{
char buf[1024];
char *conf_path = g_build_filename(conf_dir, "seahub_settings.py", NULL);
char *data = NULL;
GRegex *secret_key_regex = NULL;
GRegex *site_root_regex = NULL;
GError *error = NULL;
int retcode = 0;
char *child_stdout = NULL;
char *child_stderr = NULL;
char *binary_path = g_find_program_in_path ("parse_seahub_db.py");
FILE *file = fopen(conf_path, "r");
if (!file) {
seaf_warning ("Failed to open seahub_settings.py: %s\n", strerror(errno));
goto out;
}
snprintf (buf,
sizeof(buf),
"python3 %s",
binary_path);
g_spawn_command_line_sync (buf,
&child_stdout,
&child_stderr,
&retcode,
&error);
if (error != NULL) {
seaf_warning ("Failed to run python parse_seahub_db.py: %s\n", error->message);
g_free (binary_path);
g_free (child_stdout);
g_free (child_stderr);
secret_key_regex = g_regex_new ("SECRET_KEY\\s*=\\s*'(.+)'", 0, 0, &error);
if (error) {
g_clear_error (&error);
return NULL;
seaf_warning ("Failed to create secret key regex: %s\n", error->message);
goto out;
}
g_spawn_check_exit_status (retcode, &error);
if (error != NULL) {
seaf_warning ("Failed to run python parse_seahub_db.py: %s\n", error->message);
g_free (binary_path);
g_free (child_stdout);
g_free (child_stderr);
site_root_regex = g_regex_new ("SITE_ROOT\\s*=\\s*'(.+)'", 0, 0, &error);
if (error) {
g_clear_error (&error);
return NULL;
}
g_free (binary_path);
g_free (child_stderr);
return child_stdout;
}
int
load_seahub_database_config (SeafileSession *session)
{
int ret = 0;
json_t *object = NULL;
json_error_t err;
const char *engine = NULL, *name = NULL, *user = NULL, *password = NULL, *host = NULL, *charset = NULL;
int port;
char *json_str = NULL;
json_str = parse_seahub_db_config ();
if (!json_str){
seaf_warning ("Failed to parse seahub database config.\n");
ret = -1;
seaf_warning ("Failed to create site root regex: %s\n", error->message);
goto out;
}
object = json_loadb (json_str, strlen(json_str), 0, &err);
if (!object) {
seaf_warning ("Failed to load seahub db json: %s: %s\n", json_str, err.text);
ret = -1;
goto out;
}
engine = json_object_get_string_member (object, "ENGINE");
name = json_object_get_string_member (object, "NAME");
user = json_object_get_string_member (object, "USER");
password = json_object_get_string_member (object, "PASSWORD");
host = json_object_get_string_member (object, "HOST");
charset = json_object_get_string_member (object, "CHARSET");
port = json_object_get_int_member (object, "PORT");
if (port <= 0) {
port = MYSQL_DEFAULT_PORT;
}
if (!engine || strstr (engine, "sqlite") != NULL) {
goto out;
}
#ifdef HAVE_MYSQL
else if (strstr (engine, "mysql") != NULL) {
seaf_message("Use database Mysql\n");
if (!host) {
seaf_warning ("Seahub DB host not set in config.\n");
ret = -1;
goto out;
}
if (!user) {
seaf_warning ("Seahub DB user not set in config.\n");
ret = -1;
goto out;
}
if (!password) {
seaf_warning ("Seahub DB password not set in config.\n");
ret = -1;
goto out;
}
if (!name) {
seaf_warning ("Seahub DB name not set in config.\n");
ret = -1;
goto out;
char line[256];
char *site_root = NULL;
while (fgets(line, sizeof(line), file)) {
GMatchInfo *match_info;
if (g_regex_match (secret_key_regex, line, 0, &match_info)) {
char *sk = g_match_info_fetch (match_info, 1);
session->seahub_pk = sk;
}
session->seahub_db = seaf_db_new_mysql (host, port, user, password, name, NULL, FALSE, FALSE, NULL, charset, DEFAULT_MAX_CONNECTIONS);
if (!session->seahub_db) {
seaf_warning ("Failed to open seahub database.\n");
ret = -1;
goto out;
if (g_regex_match (site_root_regex, line, 0, &match_info)) {
site_root = g_match_info_fetch (match_info, 1);
}
}
#endif
else {
seaf_warning ("Unknown database type: %s.\n", engine);
ret = -1;
if (session->seahub_pk) {
if (site_root) {
session->seahub_url = g_strdup_printf("http://127.0.0.1:8000%sapi/v2.1/internal/user-list/", site_root);
} else {
session->seahub_url = g_strdup("http://127.0.0.1:8000/api/v2.1/internal/user-list/");
}
session->seahub_conn_pool = connection_pool_new ();
}
out:
if (object)
json_decref (object);
g_free (json_str);
return ret;
g_regex_unref (secret_key_regex);
g_regex_unref (site_root_regex);
g_free (conf_path);
g_free (data);
}
#endif

View File

@@ -16,7 +16,9 @@ load_database_config (struct _SeafileSession *session);
int
load_ccnet_database_config (struct _SeafileSession *session);
int
load_seahub_database_config (SeafileSession *session);
#ifdef FULL_FEATURE
void
load_seahub_private_key (SeafileSession *session, const char *conf_dir);
#endif
#endif

View File

@@ -2,21 +2,20 @@
package main
import (
"bufio"
"crypto/tls"
"crypto/x509"
"database/sql"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"os/signal"
"path/filepath"
"regexp"
"runtime/debug"
"strconv"
"strings"
"syscall"
@@ -44,7 +43,8 @@ var pidFilePath string
var logFp *os.File
var dbType string
var seafileDB, ccnetDB, seahubDB *sql.DB
var seafileDB, ccnetDB *sql.DB
var seahubURL, seahubPK string
// when SQLite is used, user and group db are separated.
var userDB, groupDB *sql.DB
@@ -272,66 +272,45 @@ func loadSeafileDB() {
dbType = dbEngine
}
func loadSeahubDB() {
scriptPath, err := exec.LookPath("parse_seahub_db.py")
func loadSeahubPK() {
confPath := filepath.Join(centralDir, "seahub_settings.py")
file, err := os.Open(confPath)
if err != nil {
log.Warnf("Failed to find script of parse_seahub_db.py: %v", err)
log.Warnf("Failed to open seahub_settings.py: %v", err)
return
}
cmd := exec.Command("python3", scriptPath)
dbData, err := cmd.CombinedOutput()
defer file.Close()
scanner := bufio.NewScanner(file)
pkRe, err := regexp.Compile("SECRET_KEY\\s*=\\s*'([^']*)'")
if err != nil {
log.Warnf("Failed to run python parse_seahub_db.py: %v", err)
log.Warnf("Failed to compile regex: %v", err)
return
}
siteRootRe, err := regexp.Compile("SITE_ROOT\\s*=\\s*'([^']*)'")
if err != nil {
log.Warnf("Failed to compile regex: %v", err)
return
}
dbConfig := make(map[string]string)
err = json.Unmarshal(dbData, &dbConfig)
if err != nil {
log.Warnf("Failed to decode seahub database json file: %v", err)
return
siteRoot := ""
for scanner.Scan() {
line := scanner.Text()
matches := pkRe.FindStringSubmatch(line)
if matches != nil {
seahubPK = matches[1]
}
matches = siteRootRe.FindStringSubmatch(line)
if matches != nil {
siteRoot = matches[1]
}
}
dbEngine := dbConfig["ENGINE"]
dbName := dbConfig["NAME"]
user := dbConfig["USER"]
password := dbConfig["PASSWORD"]
host := dbConfig["HOST"]
portStr := dbConfig["PORT"]
if strings.Index(dbEngine, "mysql") >= 0 {
port, err := strconv.ParseInt(portStr, 10, 64)
if err != nil || port <= 0 {
port = 3306
}
if dbName == "" {
log.Warnf("Seahub DB name not set in config")
return
}
if user == "" {
log.Warnf("Seahub DB user not set in config")
return
}
if password == "" {
log.Warnf("Seahub DB password not set in config")
return
}
if host == "" {
log.Warnf("Seahub DB host not set in config")
return
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?tls=%t", user, password, host, port, dbName, false)
seahubDB, err = sql.Open("mysql", dsn)
if err != nil {
log.Warnf("Failed to open database: %v", err)
}
} else if strings.Index(dbEngine, "sqlite") >= 0 {
return
if siteRoot != "" {
seahubURL = fmt.Sprintf("http://127.0.0.1:8000%sapi/v2.1/internal/user-list/", siteRoot)
} else {
log.Warnf("Unsupported database %s.", dbEngine)
seahubURL = ("http://127.0.0.1:8000/api/v2.1/internal/user-list/")
}
}
@@ -433,7 +412,7 @@ func main() {
fp.Close()
}
loadSeahubDB()
loadSeahubPK()
repomgr.Init(seafileDB)

View File

@@ -1,14 +1,19 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"path/filepath"
"sort"
"strings"
"time"
jwt "github.com/dgrijalva/jwt-go"
"github.com/haiwen/seafile-server/fileserver/commitmgr"
"github.com/haiwen/seafile-server/fileserver/fsmgr"
"github.com/haiwen/seafile-server/fileserver/utils"
)
type mergeOptions struct {
@@ -379,10 +384,8 @@ func getNickNameByModifier(emailToNickname map[string]string, modifier string) s
if ok {
return nickname
}
if seahubDB != nil {
sqlStr := "SELECT nickname from profile_profile WHERE user = ?"
row := seahubDB.QueryRow(sqlStr, modifier)
row.Scan(&nickname)
if seahubPK != "" {
nickname = postGetNickName(modifier)
}
if nickname == "" {
@@ -394,6 +397,72 @@ func getNickNameByModifier(emailToNickname map[string]string, modifier string) s
return nickname
}
type SeahubClaims struct {
Exp int64
IsInternal bool `json:"is_internal"`
}
func (*SeahubClaims) Valid() error {
return nil
}
func postGetNickName(modifier string) string {
claims := SeahubClaims{
time.Now().Add(time.Second * 300).Unix(),
true,
}
token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), &claims)
tokenString, err := token.SignedString([]byte(seahubPK))
if err != nil {
return ""
}
header := map[string][]string{
"Authorization": {"Token " + tokenString},
"Content-Type": {"application/json"},
}
data, err := json.Marshal(map[string]interface{}{
"user_id_list": []string{modifier},
})
if err != nil {
return ""
}
status, body, err := utils.HttpCommon("POST", seahubURL, header, bytes.NewReader(data))
if err != nil {
return ""
}
if status != http.StatusOK {
return ""
}
results := make(map[string]interface{})
err = json.Unmarshal(body, &results)
if err != nil {
return ""
}
userList, ok := results["user_list"].([]interface{})
if !ok {
return ""
}
nickname := ""
for _, element := range userList {
list, ok := element.(map[string]interface{})
if !ok {
continue
}
nickname, _ = list["name"].(string)
if nickname != "" {
break
}
}
return nickname
}
func getFileModifierMtime(repoID, storeID, head, filePath string) (string, int64, error) {
commit, err := commitmgr.Load(repoID, head)
if err != nil {

34
fileserver/utils/http.go Normal file
View File

@@ -0,0 +1,34 @@
package utils
import (
"context"
"fmt"
"io"
"net/http"
)
var HttpReqContext, HttpReqCancel = context.WithCancel(context.Background())
func HttpCommon(method, url string, header map[string][]string, reader io.Reader) (int, []byte, error) {
req, err := http.NewRequestWithContext(HttpReqContext, method, url, reader)
if err != nil {
return -1, nil, err
}
req.Header = header
rsp, err := http.DefaultClient.Do(req)
if err != nil {
return -1, nil, err
}
defer rsp.Body.Close()
if rsp.StatusCode == http.StatusNotFound {
return rsp.StatusCode, nil, fmt.Errorf("url %s not found", url)
}
body, err := io.ReadAll(rsp.Body)
if err != nil {
return rsp.StatusCode, nil, err
}
return rsp.StatusCode, body, nil
}

View File

@@ -24,7 +24,7 @@ struct _SeafileSession {
GKeyFile *ccnet_config;
SeafDB *db;
SeafDB *ccnet_db;
SeafDB *seahub_db;
char *seahub_pk;
SeafBlockManager *block_mgr;
SeafFSManager *fs_mgr;

View File

@@ -5,6 +5,7 @@
#include <jansson.h>
#include <timer.h>
#include <jwt.h>
#include "seafile-session.h"
#include "http-tx-mgr.h"
@@ -289,29 +290,13 @@ send_request (void *ptr, size_t size, size_t nmemb, void *userp)
return copy_size;
}
int
http_post (Connection *conn, const char *url, const char *token,
const char *req_content, gint64 req_size,
int *rsp_status, char **rsp_content, gint64 *rsp_size,
gboolean timeout, int timeout_sec)
static int
http_post_common (CURL *curl, const char *url, const char *token,
const char *req_content, gint64 req_size,
int *rsp_status, char **rsp_content, gint64 *rsp_size,
gboolean timeout, int timeout_sec)
{
char *token_header;
struct curl_slist *headers = NULL;
int ret = 0;
CURL *curl;
curl = conn->curl;
g_return_val_if_fail (req_content != NULL, -1);
headers = curl_slist_append (headers, "User-Agent: Seafile/"SEAFILE_CLIENT_VERSION" ("USER_AGENT_OS")");
if (token) {
token_header = g_strdup_printf ("Seafile-Repo-Token: %s", token);
headers = curl_slist_append (headers, token_header);
g_free (token_header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
@@ -378,9 +363,190 @@ http_post (Connection *conn, const char *url, const char *token,
out:
if (ret < 0) {
conn->release = TRUE;
g_free (rsp.content);
}
return ret;
}
int
http_post (Connection *conn, const char *url, const char *token,
const char *req_content, gint64 req_size,
int *rsp_status, char **rsp_content, gint64 *rsp_size,
gboolean timeout, int timeout_sec)
{
char *token_header;
struct curl_slist *headers = NULL;
int ret = 0;
CURL *curl;
curl = conn->curl;
headers = curl_slist_append (headers, "User-Agent: Seafile/"SEAFILE_CLIENT_VERSION" ("USER_AGENT_OS")");
if (token) {
token_header = g_strdup_printf ("Seafile-Repo-Token: %s", token);
headers = curl_slist_append (headers, token_header);
g_free (token_header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
}
g_return_val_if_fail (req_content != NULL, -1);
ret = http_post_common (curl, url, token, req_content, req_size,
rsp_status, rsp_content, rsp_size, timeout, timeout_sec);
if (ret < 0) {
conn->release = TRUE;
}
curl_slist_free_all (headers);
return ret;
}
static char *
parse_nickname (const char *rsp_content, int rsp_size)
{
json_t *array = NULL, *object, *member;
json_error_t jerror;
size_t n;
int i;
char *nickname = NULL;
object = json_loadb (rsp_content, rsp_size, 0, &jerror);
if (!object) {
seaf_warning ("Parse response failed: %s.\n", jerror.text);
return NULL;
}
array = json_object_get (object, "user_list");
if (!array) {
goto out;
}
n = json_array_size (array);
for (i = 0; i < n; ++i) {
json_t *obj = json_array_get (array, i);
member = json_object_get (obj, "name");
if (!member) {
continue;
}
nickname = g_strdup (json_string_value(member));
break;
}
out:
json_decref (object);
return nickname;
}
static char *
gen_jwt_token ()
{
char *jwt_token = NULL;
gint64 now = (gint64)time(NULL);
jwt_t *jwt = NULL;
if (!seaf->seahub_pk) {
return NULL;
}
int ret = jwt_new (&jwt);
if (ret != 0 || jwt == NULL) {
seaf_warning ("Failed to create jwt\n");
goto out;
}
ret = jwt_add_grant_bool (jwt, "is_internal", TRUE);
if (ret != 0) {
seaf_warning ("Failed to add is_internal to jwt\n");
goto out;
}
ret = jwt_add_grant_int (jwt, "exp", now + 300);
if (ret != 0) {
seaf_warning ("Failed to add expire time to jwt\n");
goto out;
}
ret = jwt_set_alg (jwt, JWT_ALG_HS256, (unsigned char *)seaf->seahub_pk, strlen(seaf->seahub_pk));
if (ret != 0) {
seaf_warning ("Failed to set alg\n");
goto out;
}
jwt_token = jwt_encode_str (jwt);
out:
jwt_free (jwt);
return jwt_token;
}
char *
http_tx_manager_get_nickname (const char *modifier)
{
Connection *conn = NULL;
char *token_header;
struct curl_slist *headers = NULL;
int ret = 0;
CURL *curl;
json_t *content = NULL;
json_t *array = NULL;
int rsp_status;
char *req_content = NULL;
char *jwt_token = NULL;
char *rsp_content = NULL;
char *nickname = NULL;
gint64 rsp_size;
jwt_token = gen_jwt_token ();
if (!jwt_token) {
return NULL;
}
conn = connection_pool_get_connection (seaf->seahub_conn_pool);
if (!conn) {
g_free (jwt_token);
seaf_warning ("Failed to get connection: out of memory.\n");
return NULL;
}
content = json_object ();
array = json_array ();
json_array_append_new (array, json_string (modifier));
json_object_set_new (content, "user_id_list", array);
req_content = json_dumps (content, JSON_COMPACT);
if (!req_content) {
json_decref (content);
seaf_warning ("Failed to dump json request.\n");
goto out;
}
json_decref (content);
curl = conn->curl;
headers = curl_slist_append (headers, "User-Agent: Seafile/"SEAFILE_CLIENT_VERSION" ("USER_AGENT_OS")");
token_header = g_strdup_printf ("Authorization: Token %s", jwt_token);
headers = curl_slist_append (headers, token_header);
headers = curl_slist_append (headers, "Content-Type: application/json");
g_free (token_header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
ret = http_post_common (curl, seaf->seahub_url, jwt_token, req_content, strlen(req_content),
&rsp_status, &rsp_content, &rsp_size, TRUE, 1);
if (ret < 0) {
conn->release = TRUE;
goto out;
}
if (rsp_status != HTTP_OK) {
seaf_warning ("Failed to get user list from seahub %d.\n",
rsp_status);
}
nickname = parse_nickname (rsp_content, rsp_size);
out:
g_free (jwt_token);
g_free (req_content);
g_free (rsp_content);
connection_pool_return_connection (seaf->seahub_conn_pool, conn);
return nickname;
}

View File

@@ -45,4 +45,7 @@ http_post (Connection *conn, const char *url, const char *token,
void
http_tx_manager_init ();
char *
http_tx_manager_get_nickname (const char *modifier);
#endif

View File

@@ -218,7 +218,7 @@ seafile_session_new(const char *central_config_dir,
goto onerror;
}
load_seahub_database_config (session);
load_seahub_private_key (session, abs_central_config_dir ? abs_central_config_dir : abs_seafile_dir);
session->cfg_mgr = seaf_cfg_manager_new (session);
if (!session->cfg_mgr)

View File

@@ -29,6 +29,7 @@
#include "zip-download-mgr.h"
#include "index-blocks-mgr.h"
#include "notif-mgr.h"
#include "http-tx-mgr.h"
#include <searpc-client.h>
@@ -47,7 +48,9 @@ struct _SeafileSession {
GKeyFile *ccnet_config;
SeafDB *db;
CcnetDB *ccnet_db;
SeafDB *seahub_db;
char *seahub_pk;
char *seahub_url;
ConnectionPool *seahub_conn_pool;
SeafBlockManager *block_mgr;
SeafFSManager *fs_mgr;