working on oauth
This commit is contained in:
parent
e6bc87dd08
commit
268de3e3d1
15
INSTALL
15
INSTALL
@ -769,6 +769,21 @@ used for the HMAC key generation.
|
||||
The key must be 32 characters (HEX representation of 16 bytes) for SHA1,
|
||||
or 64 characters (HEX representation of 32 bytes) for SHA256.
|
||||
|
||||
# oAuth key storage table.
|
||||
#
|
||||
CREATE TABLE oauth_key (
|
||||
kid varchar(128),
|
||||
ikm_key varchar(256) default '',
|
||||
timestamp bigint default 0,
|
||||
lifetime integer default 0,
|
||||
hkdf_hash_func varchar(64) default '',
|
||||
as_rs_alg varchar(64) default '',
|
||||
as_rs_key varchar(256) default '',
|
||||
auth_alg varchar(64) default '',
|
||||
auth_key varchar(256) default '',
|
||||
primary key (kid)
|
||||
);
|
||||
|
||||
You can use turnadmin program to manage the database - you can either use
|
||||
turnadmin to add/modify/delete users, or you can use turnadmin to produce
|
||||
the hmac keys and modify the database with your favorite tools.
|
||||
|
||||
@ -234,6 +234,79 @@ static int mongo_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
|
||||
bson_destroy(&fields);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mongo_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
|
||||
|
||||
mongoc_collection_t * collection = mongo_get_collection("oauth_key");
|
||||
|
||||
if (!collection)
|
||||
return -1;
|
||||
|
||||
bson_t query;
|
||||
bson_init(&query);
|
||||
BSON_APPEND_UTF8(&query, "kid", (const char *)key->kid);
|
||||
|
||||
bson_t fields;
|
||||
bson_init(&fields);
|
||||
BSON_APPEND_INT32(&fields, "lifetime", 1);
|
||||
BSON_APPEND_INT32(&fields, "timestamp", 1);
|
||||
BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
|
||||
BSON_APPEND_INT32(&fields, "as_rs_key", 1);
|
||||
BSON_APPEND_INT32(&fields, "auth_alg", 1);
|
||||
BSON_APPEND_INT32(&fields, "auth_key", 1);
|
||||
BSON_APPEND_INT32(&fields, "hkdf_hash_func", 1);
|
||||
BSON_APPEND_INT32(&fields, "ikm_key", 1);
|
||||
|
||||
mongoc_cursor_t * cursor;
|
||||
cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0,
|
||||
&query, &fields, NULL);
|
||||
|
||||
int ret = -1;
|
||||
|
||||
ns_bzero(key,sizeof(oauth_key_data_raw));
|
||||
STRCPY(key->kid,kid);
|
||||
|
||||
if (!cursor) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
|
||||
"Error querying MongoDB collection 'oauth_key'\n");
|
||||
} else {
|
||||
const bson_t * item;
|
||||
uint32_t length;
|
||||
bson_iter_t iter;
|
||||
if (mongoc_cursor_next(cursor, &item)) {
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->as_rs_alg,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->as_rs_key,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->auth_alg,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->auth_key,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "ikm_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->ikm_key,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "hkdf_hash_func") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->hkdf_hash_func,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "timestamp") && BSON_ITER_HOLDS_INT64(&iter)) {
|
||||
key->timestamp = (u64bits)bson_iter_int64(&iter);
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) {
|
||||
key->lifetime = (u32bits)bson_iter_int32(&iter);
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
mongoc_cursor_destroy(cursor);
|
||||
}
|
||||
mongoc_collection_destroy(collection);
|
||||
bson_destroy(&query);
|
||||
bson_destroy(&fields);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mongo_get_user_pwd(u08bits *usname, st_password_t pwd) {
|
||||
mongoc_collection_t * collection = mongo_get_collection("turnusers_st");
|
||||
@ -302,7 +375,43 @@ static int mongo_set_user_key(u08bits *usname, u08bits *realm, const char *key)
|
||||
int ret = -1;
|
||||
|
||||
if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information\n");
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information\n");
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
mongoc_collection_destroy(collection);
|
||||
bson_destroy(&doc);
|
||||
bson_destroy(&query);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mongo_set_oauth_key(oauth_key_data_raw *key) {
|
||||
|
||||
mongoc_collection_t * collection = mongo_get_collection("oauth_key");
|
||||
|
||||
if(!collection)
|
||||
return -1;
|
||||
|
||||
bson_t query;
|
||||
bson_init(&query);
|
||||
BSON_APPEND_UTF8(&query, "kid", (const char *)key->kid);
|
||||
|
||||
bson_t doc;
|
||||
bson_init(&doc);
|
||||
BSON_APPEND_UTF8(&query, "kid", (const char *)key->kid);
|
||||
BSON_APPEND_UTF8(&doc, "as_rs_alg", (const char *)key->as_rs_alg);
|
||||
BSON_APPEND_UTF8(&doc, "as_rs_key", (const char *)key->as_rs_key);
|
||||
BSON_APPEND_UTF8(&doc, "auth_alg", (const char *)key->auth_alg);
|
||||
BSON_APPEND_UTF8(&doc, "auth_key", (const char *)key->auth_key);
|
||||
BSON_APPEND_UTF8(&doc, "hkdf_hash_func", (const char *)key->hkdf_hash_func);
|
||||
BSON_APPEND_UTF8(&doc, "ikm_key", (const char *)key->ikm_key);
|
||||
BSON_APPEND_INT64(&doc, "timestamp", (int64_t)key->timestamp);
|
||||
BSON_APPEND_INT32(&doc, "lifetime", (int32_t)key->lifetime);
|
||||
|
||||
int ret = -1;
|
||||
|
||||
if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth key information\n");
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
@ -364,6 +473,29 @@ static int mongo_del_user(u08bits *usname, int is_st, u08bits *realm) {
|
||||
bson_destroy(&query);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mongo_del_oauth_key(const u08bits *kid) {
|
||||
|
||||
mongoc_collection_t * collection = mongo_get_collection("oauth_key");
|
||||
|
||||
if(!collection)
|
||||
return -1;
|
||||
|
||||
bson_t query;
|
||||
bson_init(&query);
|
||||
BSON_APPEND_UTF8(&query, "kid", (const char *)kid);
|
||||
|
||||
int ret = -1;
|
||||
|
||||
if (!mongoc_collection_delete(collection, MONGOC_DELETE_SINGLE_REMOVE, &query, NULL, NULL)) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth key information\n");
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
mongoc_collection_destroy(collection);
|
||||
bson_destroy(&query);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mongo_list_users(int is_st, u08bits *realm) {
|
||||
const char * collection_name = is_st ? "turnusers_st" : "turnusers_lt";
|
||||
@ -425,6 +557,86 @@ static int mongo_list_users(int is_st, u08bits *realm) {
|
||||
bson_destroy(&fields);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mongo_list_oauth_keys(void) {
|
||||
|
||||
const char * collection_name = "oauth_key";
|
||||
mongoc_collection_t * collection = mongo_get_collection(collection_name);
|
||||
|
||||
if(!collection)
|
||||
return -1;
|
||||
|
||||
bson_t query, child;
|
||||
bson_init(&query);
|
||||
bson_append_document_begin(&query, "$orderby", -1, &child);
|
||||
bson_append_int32(&child, "kid", -1, 1);
|
||||
bson_append_document_end(&query, &child);
|
||||
|
||||
bson_t fields;
|
||||
bson_init(&fields);
|
||||
BSON_APPEND_INT32(&fields, "kid", 1);
|
||||
BSON_APPEND_INT32(&fields, "lifetime", 1);
|
||||
BSON_APPEND_INT32(&fields, "timestamp", 1);
|
||||
BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
|
||||
BSON_APPEND_INT32(&fields, "as_rs_key", 1);
|
||||
BSON_APPEND_INT32(&fields, "auth_alg", 1);
|
||||
BSON_APPEND_INT32(&fields, "auth_key", 1);
|
||||
BSON_APPEND_INT32(&fields, "hkdf_hash_func", 1);
|
||||
BSON_APPEND_INT32(&fields, "ikm_key", 1);
|
||||
|
||||
mongoc_cursor_t * cursor;
|
||||
cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
|
||||
|
||||
int ret = -1;
|
||||
|
||||
if (!cursor) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection '%s'\n", collection_name);
|
||||
} else {
|
||||
const bson_t * item;
|
||||
oauth_key_data_raw key_;
|
||||
oauth_key_data_raw *key=&key_;
|
||||
uint32_t length;
|
||||
bson_iter_t iter;
|
||||
while (mongoc_cursor_next(cursor, &item)) {
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "kid") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->kid,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->as_rs_alg,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->as_rs_key,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->auth_alg,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->auth_key,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "ikm_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->ikm_key,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "hkdf_hash_func") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
||||
STRCPY(key->hkdf_hash_func,bson_iter_utf8(&iter, &length));
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "timestamp") && BSON_ITER_HOLDS_INT64(&iter)) {
|
||||
key->timestamp = (u64bits)bson_iter_int64(&iter);
|
||||
}
|
||||
if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) {
|
||||
key->lifetime = (u32bits)bson_iter_int32(&iter);
|
||||
}
|
||||
printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
|
||||
key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
|
||||
key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
|
||||
}
|
||||
mongoc_cursor_destroy(cursor);
|
||||
ret = 0;
|
||||
}
|
||||
mongoc_collection_destroy(collection);
|
||||
bson_destroy(&query);
|
||||
bson_destroy(&fields);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mongo_show_secret(u08bits *realm) {
|
||||
mongoc_collection_t * collection = mongo_get_collection("turn_secret");
|
||||
@ -921,7 +1133,11 @@ static turn_dbdriver_t driver = {
|
||||
&mongo_list_realm_options,
|
||||
&mongo_auth_ping,
|
||||
&mongo_get_ip_list,
|
||||
&mongo_reread_realms
|
||||
&mongo_reread_realms,
|
||||
&mongo_set_oauth_key,
|
||||
&mongo_get_oauth_key,
|
||||
&mongo_del_oauth_key,
|
||||
&mongo_list_oauth_keys
|
||||
};
|
||||
|
||||
turn_dbdriver_t * get_mongo_dbdriver(void) {
|
||||
|
||||
@ -372,6 +372,140 @@ static int mysql_get_user_pwd(u08bits *usname, st_password_t pwd) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
|
||||
|
||||
int ret = 1;
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key from oauth_key where kid='%s'",(const char*)kid);
|
||||
|
||||
MYSQL * myc = get_mydb_connection();
|
||||
if(myc) {
|
||||
int res = mysql_query(myc, statement);
|
||||
if(res) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
|
||||
} else {
|
||||
MYSQL_RES *mres = mysql_store_result(myc);
|
||||
if(!mres) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
|
||||
} else if(mysql_field_count(myc)!=8) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
|
||||
} else {
|
||||
MYSQL_ROW row = mysql_fetch_row(mres);
|
||||
if(row && row[0]) {
|
||||
unsigned long *lengths = mysql_fetch_lengths(mres);
|
||||
if(lengths) {
|
||||
STRCPY((char*)key->kid,kid);
|
||||
ns_bcopy(row[0],key->ikm_key,lengths[0]);
|
||||
key->ikm_key[lengths[0]]=0;
|
||||
|
||||
char stimestamp[128];
|
||||
ns_bcopy(row[1],stimestamp,lengths[1]);
|
||||
stimestamp[lengths[1]]=0;
|
||||
key->timestamp = (u64bits)strtoull(stimestamp,NULL,10);
|
||||
|
||||
char slifetime[128];
|
||||
ns_bcopy(row[2],slifetime,lengths[2]);
|
||||
slifetime[lengths[2]]=0;
|
||||
key->lifetime = (u32bits)strtoul(slifetime,NULL,10);
|
||||
|
||||
ns_bcopy(row[3],key->hkdf_hash_func,lengths[3]);
|
||||
key->hkdf_hash_func[lengths[3]]=0;
|
||||
|
||||
ns_bcopy(row[4],key->as_rs_alg,lengths[4]);
|
||||
key->as_rs_alg[lengths[4]]=0;
|
||||
|
||||
ns_bcopy(row[5],key->as_rs_key,lengths[5]);
|
||||
key->as_rs_key[lengths[5]]=0;
|
||||
|
||||
ns_bcopy(row[6],key->auth_alg,lengths[6]);
|
||||
key->auth_alg[lengths[6]]=0;
|
||||
|
||||
ns_bcopy(row[7],key->auth_key,lengths[7]);
|
||||
key->auth_key[lengths[7]]=0;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mres)
|
||||
mysql_free_result(mres);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mysql_list_oauth_keys(void) {
|
||||
|
||||
oauth_key_data_raw key_;
|
||||
oauth_key_data_raw *key=&key_;
|
||||
int ret = 1;
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key,kid from oauth_key order by kid");
|
||||
|
||||
MYSQL * myc = get_mydb_connection();
|
||||
if(myc) {
|
||||
int res = mysql_query(myc, statement);
|
||||
if(res) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
|
||||
} else {
|
||||
MYSQL_RES *mres = mysql_store_result(myc);
|
||||
if(!mres) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
|
||||
} else if(mysql_field_count(myc)!=9) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
|
||||
} else {
|
||||
MYSQL_ROW row = mysql_fetch_row(mres);
|
||||
while(row) {
|
||||
unsigned long *lengths = mysql_fetch_lengths(mres);
|
||||
if(lengths) {
|
||||
|
||||
ns_bcopy(row[0],key->ikm_key,lengths[0]);
|
||||
key->ikm_key[lengths[0]]=0;
|
||||
|
||||
char stimestamp[128];
|
||||
ns_bcopy(row[1],stimestamp,lengths[1]);
|
||||
stimestamp[lengths[1]]=0;
|
||||
key->timestamp = (u64bits)strtoull(stimestamp,NULL,10);
|
||||
|
||||
char slifetime[128];
|
||||
ns_bcopy(row[2],slifetime,lengths[2]);
|
||||
slifetime[lengths[2]]=0;
|
||||
key->lifetime = (u32bits)strtoul(slifetime,NULL,10);
|
||||
|
||||
ns_bcopy(row[3],key->hkdf_hash_func,lengths[3]);
|
||||
key->hkdf_hash_func[lengths[3]]=0;
|
||||
ns_bcopy(row[4],key->as_rs_alg,lengths[4]);
|
||||
key->as_rs_alg[lengths[4]]=0;
|
||||
|
||||
ns_bcopy(row[5],key->as_rs_key,lengths[5]);
|
||||
key->as_rs_key[lengths[5]]=0;
|
||||
|
||||
ns_bcopy(row[6],key->auth_alg,lengths[6]);
|
||||
key->auth_alg[lengths[6]]=0;
|
||||
|
||||
ns_bcopy(row[7],key->auth_key,lengths[7]);
|
||||
key->auth_key[lengths[7]]=0;
|
||||
|
||||
ns_bcopy(row[8],key->kid,lengths[8]);
|
||||
key->kid[lengths[8]]=0;
|
||||
|
||||
printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
|
||||
key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
|
||||
key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
|
||||
}
|
||||
row = mysql_fetch_row(mres);
|
||||
}
|
||||
}
|
||||
|
||||
if(mres)
|
||||
mysql_free_result(mres);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mysql_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
|
||||
int ret = 1;
|
||||
@ -390,6 +524,27 @@ static int mysql_set_user_key(u08bits *usname, u08bits *realm, const char *key)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mysql_set_oauth_key(oauth_key_data_raw *key) {
|
||||
int ret = 1;
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
MYSQL * myc = get_mydb_connection();
|
||||
if(myc) {
|
||||
snprintf(statement,sizeof(statement),"insert into oauth_key (kid,ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key) values('%s','%s',%llu,%lu,'%s','%s','%s','%s','%s')",
|
||||
key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime,
|
||||
key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key);
|
||||
int res = mysql_query(myc, statement);
|
||||
if(res) {
|
||||
snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, hkdf_hash_func = '%s', as_rs_alg='%s',as_rs_key='%s',auth_alg='%s',auth_key='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime,
|
||||
key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key,key->kid);
|
||||
res = mysql_query(myc, statement);
|
||||
if(res) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth key information: %s\n",mysql_error(myc));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mysql_set_user_pwd(u08bits *usname, st_password_t pwd) {
|
||||
int ret = 1;
|
||||
@ -430,6 +585,22 @@ static int mysql_del_user(u08bits *usname, int is_st, u08bits *realm) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mysql_del_oauth_key(const u08bits *kid) {
|
||||
int ret = 1;
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
MYSQL * myc = get_mydb_connection();
|
||||
if(myc) {
|
||||
snprintf(statement,sizeof(statement),"delete from oauth_key where kid = '%s'",(const char*)kid);
|
||||
int res = mysql_query(myc, statement);
|
||||
if(res) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth key information: %s\n",mysql_error(myc));
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mysql_list_users(int is_st, u08bits *realm) {
|
||||
int ret = 1;
|
||||
@ -893,7 +1064,11 @@ static turn_dbdriver_t driver = {
|
||||
&mysql_list_realm_options,
|
||||
&mysql_auth_ping,
|
||||
&mysql_get_ip_list,
|
||||
&mysql_reread_realms
|
||||
&mysql_reread_realms,
|
||||
&mysql_set_oauth_key,
|
||||
&mysql_get_oauth_key,
|
||||
&mysql_del_oauth_key,
|
||||
&mysql_list_oauth_keys
|
||||
};
|
||||
|
||||
turn_dbdriver_t * get_mysql_dbdriver(void) {
|
||||
|
||||
@ -174,6 +174,86 @@ static int pgsql_get_user_pwd(u08bits *usname, st_password_t pwd) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pgsql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
|
||||
|
||||
int ret = 1;
|
||||
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key from oauth_key where kid='%s'",(const char*)kid);
|
||||
|
||||
PGconn * pqc = get_pqdb_connection();
|
||||
if(pqc) {
|
||||
PGresult *res = PQexec(pqc, statement);
|
||||
|
||||
if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res)!=1)) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
|
||||
} else {
|
||||
STRCPY((char*)key->ikm_key,PQgetvalue(res,0,0));
|
||||
key->timestamp = (u64bits)strtoll(PQgetvalue(res,0,1),NULL,10);
|
||||
key->lifetime = (u32bits)strtol(PQgetvalue(res,0,2),NULL,10);
|
||||
STRCPY((char*)key->hkdf_hash_func,PQgetvalue(res,0,3));
|
||||
STRCPY((char*)key->as_rs_alg,PQgetvalue(res,0,4));
|
||||
STRCPY((char*)key->as_rs_key,PQgetvalue(res,0,5));
|
||||
STRCPY((char*)key->auth_alg,PQgetvalue(res,0,6));
|
||||
STRCPY((char*)key->auth_key,PQgetvalue(res,0,7));
|
||||
STRCPY((char*)key->kid,kid);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if(res) {
|
||||
PQclear(res);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pgsql_list_oauth_keys(void) {
|
||||
|
||||
oauth_key_data_raw key_;
|
||||
oauth_key_data_raw *key=&key_;
|
||||
|
||||
int ret = 1;
|
||||
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key,kid from oauth_key order by kid");
|
||||
|
||||
PGconn * pqc = get_pqdb_connection();
|
||||
if(pqc) {
|
||||
PGresult *res = PQexec(pqc, statement);
|
||||
|
||||
if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
|
||||
} else {
|
||||
int i = 0;
|
||||
for(i=0;i<PQntuples(res);i++) {
|
||||
|
||||
STRCPY((char*)key->ikm_key,PQgetvalue(res,i,0));
|
||||
key->timestamp = (u64bits)strtoll(PQgetvalue(res,i,1),NULL,10);
|
||||
key->lifetime = (u32bits)strtol(PQgetvalue(res,i,2),NULL,10);
|
||||
STRCPY((char*)key->hkdf_hash_func,PQgetvalue(res,i,3));
|
||||
STRCPY((char*)key->as_rs_alg,PQgetvalue(res,i,4));
|
||||
STRCPY((char*)key->as_rs_key,PQgetvalue(res,i,5));
|
||||
STRCPY((char*)key->auth_alg,PQgetvalue(res,i,6));
|
||||
STRCPY((char*)key->auth_key,PQgetvalue(res,i,7));
|
||||
STRCPY((char*)key->kid,PQgetvalue(res,i,8));
|
||||
|
||||
printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
|
||||
key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
|
||||
key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(res) {
|
||||
PQclear(res);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pgsql_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
|
||||
int ret = 1;
|
||||
@ -201,7 +281,38 @@ static int pgsql_set_user_key(u08bits *usname, u08bits *realm, const char *key)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int pgsql_set_oauth_key(oauth_key_data_raw *key) {
|
||||
|
||||
int ret = 1;
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
PGconn *pqc = get_pqdb_connection();
|
||||
if(pqc) {
|
||||
snprintf(statement,sizeof(statement),"insert into oauth_key (kid,ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key) values('%s','%s',%llu,%lu,'%s','%s','%s','%s','%s')",
|
||||
key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime,
|
||||
key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key);
|
||||
|
||||
PGresult *res = PQexec(pqc, statement);
|
||||
if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
|
||||
if(res) {
|
||||
PQclear(res);
|
||||
}
|
||||
snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, hkdf_hash_func = '%s', as_rs_alg='%s',as_rs_key='%s',auth_alg='%s',auth_key='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime,
|
||||
key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key,key->kid);
|
||||
res = PQexec(pqc, statement);
|
||||
if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth_key information: %s\n",PQerrorMessage(pqc));
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
if(res) {
|
||||
PQclear(res);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pgsql_set_user_pwd(u08bits *usname, st_password_t pwd) {
|
||||
int ret = 1;
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
@ -246,6 +357,27 @@ static int pgsql_del_user(u08bits *usname, int is_st, u08bits *realm) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pgsql_del_oauth_key(const u08bits *kid) {
|
||||
|
||||
int ret = 1;
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
PGconn *pqc = get_pqdb_connection();
|
||||
if(pqc) {
|
||||
snprintf(statement,sizeof(statement),"delete from oauth_key where kid = '%s'",(const char*)kid);
|
||||
|
||||
PGresult *res = PQexec(pqc, statement);
|
||||
if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth_key information: %s\n",PQerrorMessage(pqc));
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
if(res) {
|
||||
PQclear(res);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pgsql_list_users(int is_st, u08bits *realm) {
|
||||
int ret = 1;
|
||||
@ -655,7 +787,11 @@ static turn_dbdriver_t driver = {
|
||||
&pgsql_list_realm_options,
|
||||
&pgsql_auth_ping,
|
||||
&pgsql_get_ip_list,
|
||||
&pgsql_reread_realms
|
||||
&pgsql_reread_realms,
|
||||
&pgsql_set_oauth_key,
|
||||
&pgsql_get_oauth_key,
|
||||
&pgsql_del_oauth_key,
|
||||
&pgsql_list_oauth_keys
|
||||
};
|
||||
|
||||
turn_dbdriver_t * get_pgsql_dbdriver(void) {
|
||||
|
||||
@ -492,6 +492,53 @@ static int redis_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int redis_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
|
||||
int ret = 1;
|
||||
redisContext * rc = get_redis_connection();
|
||||
if(rc) {
|
||||
char s[TURN_LONG_STRING_SIZE];
|
||||
ns_bzero(key,sizeof(oauth_key_data_raw));
|
||||
STRCPY(key->kid,kid);
|
||||
snprintf(s,sizeof(s),"hgetall turn/oauth/kid/%s", (const char*)kid);
|
||||
redisReply *reply = (redisReply *)redisCommand(rc, s);
|
||||
if(reply) {
|
||||
if (reply->type == REDIS_REPLY_ERROR)
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str);
|
||||
else if (reply->type != REDIS_REPLY_ARRAY) {
|
||||
if (reply->type != REDIS_REPLY_NIL)
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < (reply->elements)/2; ++i) {
|
||||
char *kw = reply->element[i]->str;
|
||||
if(kw) {
|
||||
if(!strcmp(kw,"as_rs_alg")) {
|
||||
STRCPY(key->as_rs_alg,reply->element[i+1]->str);
|
||||
} else if(!strcmp(kw,"as_rs_key")) {
|
||||
STRCPY(key->as_rs_key,reply->element[i+1]->str);
|
||||
} else if(!strcmp(kw,"auth_key")) {
|
||||
STRCPY(key->auth_key,reply->element[i+1]->str);
|
||||
} else if(!strcmp(kw,"auth_alg")) {
|
||||
STRCPY(key->auth_alg,reply->element[i+1]->str);
|
||||
} else if(!strcmp(kw,"ikm_key")) {
|
||||
STRCPY(key->ikm_key,reply->element[i+1]->str);
|
||||
} else if(!strcmp(kw,"hkdf_hash_func")) {
|
||||
STRCPY(key->hkdf_hash_func,reply->element[i+1]->str);
|
||||
} else if(!strcmp(kw,"timestamp")) {
|
||||
key->timestamp = (u64bits)strtoull(reply->element[i+1]->str,NULL,10);
|
||||
} else if(!strcmp(kw,"lifetime")) {
|
||||
key->lifetime = (u32bits)strtoul(reply->element[i+1]->str,NULL,10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
turnFreeRedisReply(reply);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int redis_get_user_pwd(u08bits *usname, st_password_t pwd) {
|
||||
int ret = 1;
|
||||
@ -531,16 +578,30 @@ static int redis_set_user_key(u08bits *usname, u08bits *realm, const char *key)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int redis_set_oauth_key(oauth_key_data_raw *key) {
|
||||
int ret = 1;
|
||||
redisContext *rc = get_redis_connection();
|
||||
if(rc) {
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
snprintf(statement,sizeof(statement),"hmset turn/oauth/kid/%s ikm_key '%s' hkdf_hash_func '%s' as_rs_alg '%s' as_rs_key '%s' auth_alg '%s' auth_key '%s' timestamp %llu lifetime %lu",
|
||||
key->kid,key->ikm_key,key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime);
|
||||
turnFreeRedisReply(redisCommand(rc, statement));
|
||||
turnFreeRedisReply(redisCommand(rc, "save"));
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int redis_set_user_pwd(u08bits *usname, st_password_t pwd) {
|
||||
int ret = 1;
|
||||
redisContext *rc = get_redis_connection();
|
||||
if(rc) {
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
snprintf(statement,sizeof(statement),"set turn/user/%s/password %s",usname,pwd);
|
||||
turnFreeRedisReply(redisCommand(rc, statement));
|
||||
snprintf(statement,sizeof(statement),"set turn/user/%s/password %s",usname,pwd);
|
||||
turnFreeRedisReply(redisCommand(rc, statement));
|
||||
turnFreeRedisReply(redisCommand(rc, "save"));
|
||||
ret = 0;
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -565,6 +626,19 @@ static int redis_del_user(u08bits *usname, int is_st, u08bits *realm) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int redis_del_oauth_key(const u08bits *kid) {
|
||||
int ret = 1;
|
||||
redisContext *rc = get_redis_connection();
|
||||
if(rc) {
|
||||
char statement[TURN_LONG_STRING_SIZE];
|
||||
snprintf(statement,sizeof(statement),"del turn/oauth/kid/%s",(const char*)kid);
|
||||
turnFreeRedisReply(redisCommand(rc, statement));
|
||||
turnFreeRedisReply(redisCommand(rc, "save"));
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int redis_list_users(int is_st, u08bits *realm) {
|
||||
int ret = 1;
|
||||
@ -656,6 +730,51 @@ static int redis_list_users(int is_st, u08bits *realm) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int redis_list_oauth_keys(void) {
|
||||
int ret = 1;
|
||||
redisContext *rc = get_redis_connection();
|
||||
secrets_list_t keys;
|
||||
size_t isz = 0;
|
||||
init_secrets_list(&keys);
|
||||
|
||||
if(rc) {
|
||||
redisReply *reply = NULL;
|
||||
|
||||
reply = (redisReply*)redisCommand(rc, "keys turn/oauth/kid/*");
|
||||
if(reply) {
|
||||
|
||||
if (reply->type == REDIS_REPLY_ERROR)
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str);
|
||||
else if (reply->type != REDIS_REPLY_ARRAY) {
|
||||
if (reply->type != REDIS_REPLY_NIL)
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < reply->elements; ++i) {
|
||||
add_to_secrets_list(&keys,reply->element[i]->str);
|
||||
}
|
||||
}
|
||||
turnFreeRedisReply(reply);
|
||||
}
|
||||
}
|
||||
|
||||
for(isz=0;isz<keys.sz;++isz) {
|
||||
char *s = keys.secrets[isz];
|
||||
oauth_key_data_raw key_;
|
||||
oauth_key_data_raw *key=&key_;
|
||||
if(redis_get_oauth_key((const u08bits*)s,key) == 0) {
|
||||
printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
|
||||
key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
|
||||
key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
|
||||
}
|
||||
}
|
||||
|
||||
clean_secrets_list(&keys);
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int redis_show_secret(u08bits *realm) {
|
||||
int ret = 1;
|
||||
@ -1134,7 +1253,11 @@ static turn_dbdriver_t driver = {
|
||||
&redis_list_realm_options,
|
||||
&redis_auth_ping,
|
||||
&redis_get_ip_list,
|
||||
&redis_reread_realms
|
||||
&redis_reread_realms,
|
||||
&redis_set_oauth_key,
|
||||
&redis_get_oauth_key,
|
||||
&redis_del_oauth_key,
|
||||
&redis_list_oauth_keys
|
||||
};
|
||||
|
||||
turn_dbdriver_t * get_redis_dbdriver(void) {
|
||||
|
||||
@ -42,6 +42,22 @@ extern "C" {
|
||||
|
||||
////////////////////////////////////////////
|
||||
|
||||
struct _oauth_key_data_raw {
|
||||
char kid[OAUTH_KID_SIZE+1];
|
||||
char ikm_key[OAUTH_KEY_SIZE+1];
|
||||
u64bits timestamp;
|
||||
u32bits lifetime;
|
||||
char hkdf_hash_func[OAUTH_HASH_FUNC_SIZE+1];
|
||||
char as_rs_alg[OAUTH_ALG_SIZE+1];
|
||||
char as_rs_key[OAUTH_KEY_SIZE+1];
|
||||
char auth_alg[OAUTH_ALG_SIZE+1];
|
||||
char auth_key[OAUTH_KEY_SIZE+1];
|
||||
};
|
||||
|
||||
typedef struct _oauth_key_data_raw oauth_key_data_raw;
|
||||
|
||||
////////////////////////////////////////////
|
||||
|
||||
typedef struct _turn_dbdriver_t {
|
||||
int (*get_auth_secrets)(secrets_list_t *sl, u08bits *realm);
|
||||
int (*get_user_key)(u08bits *usname, u08bits *realm, hmackey_t key);
|
||||
@ -61,6 +77,10 @@ typedef struct _turn_dbdriver_t {
|
||||
void (*auth_ping)(void * rch);
|
||||
int (*get_ip_list)(const char *kind, ip_range_list_t * list);
|
||||
void (*reread_realms)(secrets_list_t * realms_list);
|
||||
int (*set_oauth_key)(oauth_key_data_raw *key);
|
||||
int (*get_oauth_key)(const u08bits *kid, oauth_key_data_raw *key);
|
||||
int (*del_oauth_key)(const u08bits *kid);
|
||||
int (*list_oauth_keys)(void);
|
||||
} turn_dbdriver_t;
|
||||
|
||||
/////////// USER DB CHECK //////////////////
|
||||
@ -71,20 +91,6 @@ turn_dbdriver_t * get_dbdriver(void);
|
||||
|
||||
////////////// OAUTH UTILS ////////////////
|
||||
|
||||
struct _oauth_key_data_raw {
|
||||
char kid[OAUTH_KID_SIZE+1];
|
||||
char ikm_key[OAUTH_KEY_SIZE+1];
|
||||
u64bits timestamp;
|
||||
u32bits lifetime;
|
||||
char hkdf_hash_func[OAUTH_HASH_FUNC_SIZE+1];
|
||||
char as_rs_alg[OAUTH_ALG_SIZE+1];
|
||||
char as_rs_key[OAUTH_KEY_SIZE+1];
|
||||
char auth_alg[OAUTH_ALG_SIZE+1];
|
||||
char auth_key[OAUTH_KEY_SIZE+1];
|
||||
};
|
||||
|
||||
typedef struct _oauth_key_data_raw oauth_key_data_raw;
|
||||
|
||||
void convert_oauth_key_data_raw(const oauth_key_data_raw *raw, oauth_key_data *oakd);
|
||||
|
||||
////////////////////////////////////////////
|
||||
|
||||
@ -42,13 +42,13 @@ CREATE TABLE turn_realm_option (
|
||||
|
||||
CREATE TABLE oauth_key (
|
||||
kid varchar(128),
|
||||
ikm_key varchar(256),
|
||||
timestamp bigint,
|
||||
lifetime integer,
|
||||
hkdf_hash_func varchar(64),
|
||||
as_rs_alg varchar(64),
|
||||
as_rs_key varchar(256),
|
||||
auth_alg varchar(64),
|
||||
auth_key varchar(256),
|
||||
ikm_key varchar(256) default '',
|
||||
timestamp bigint default 0,
|
||||
lifetime integer default 0,
|
||||
hkdf_hash_func varchar(64) default '',
|
||||
as_rs_alg varchar(64) default '',
|
||||
as_rs_key varchar(256) default '',
|
||||
auth_alg varchar(64) default '',
|
||||
auth_key varchar(256) default '',
|
||||
primary key (kid)
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user