From: Richard Kettlewell Date: Thu, 20 Dec 2007 18:31:37 +0000 (+0000) Subject: userinfo/edituser implementation X-Git-Tag: 3.0~199 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/5df73aebf27f6c3b57a91ecfd504fa6ee367d20a?hp=f0feb22e80bfe438c16d212a7cc8be6d2282b6ac userinfo/edituser implementation --- diff --git a/lib/Makefile.am b/lib/Makefile.am index 7c792d8..1cbb7bf 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -52,6 +52,7 @@ libdisorder_a_SOURCES=charset.c charset.h \ asprintf.c fprintf.c snprintf.c \ queue.c queue.h \ regsub.c regsub.h \ + rights.c rights.h \ rtp.h \ selection.c selection.h \ signame.c signame.h \ diff --git a/lib/client.c b/lib/client.c index e4e28e8..8184306 100644 --- a/lib/client.c +++ b/lib/client.c @@ -50,6 +50,7 @@ #include "addr.h" #include "authhash.h" #include "client-common.h" +#include "rights.h" #include "trackdb.h" struct disorder_client { @@ -659,6 +660,15 @@ int disorder_deluser(disorder_client *c, const char *user) { return disorder_simple(c, 0, "deluser", user, (char *)0); } +int disorder_userinfo(disorder_client *c, const char *user, const char *key) { + return disorder_simple(c, 0, "userinfo", user, key, (char *)0); +} + +int disorder_edituser(disorder_client *c, const char *user, + const char *key, const char *value) { + return disorder_simple(c, 0, "edituser", user, key, value, (char *)0); +} + /* Local Variables: c-basic-offset:2 diff --git a/lib/client.h b/lib/client.h index ab93115..64f58b8 100644 --- a/lib/client.h +++ b/lib/client.h @@ -191,6 +191,9 @@ int disorder_rtp_address(disorder_client *c, char **addressp, char **portp); int disorder_adduser(disorder_client *c, const char *user, const char *password); int disorder_deluser(disorder_client *c, const char *user); +int disorder_userinfo(disorder_client *c, const char *user, const char *key); +int disorder_edituser(disorder_client *c, const char *user, + const char *key, const char *value); #endif /* CLIENT_H */ diff --git a/lib/cookies.c b/lib/cookies.c index 666c6ce..cce6b1b 100644 --- a/lib/cookies.c +++ b/lib/cookies.c @@ -40,6 +40,7 @@ #include "mime.h" #include "configuration.h" #include "kvp.h" +#include "rights.h" #include "trackdb.h" /** @brief Hash function used in signing HMAC */ diff --git a/lib/rights.c b/lib/rights.c new file mode 100644 index 0000000..8d84743 --- /dev/null +++ b/lib/rights.c @@ -0,0 +1,148 @@ +/* + * This file is part of DisOrder + * Copyright (C) 2007 Richard Kettlewell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +/** @file lib/rights.c + * @brief User rights + */ + +#include +#include "types.h" + +#include + +#include "mem.h" +#include "log.h" +#include "configuration.h" +#include "rights.h" +#include "vector.h" + +static const struct { + rights_type bit; + const char *name; +} rights_names[] = { + { RIGHT_READ, "read" }, + { RIGHT_PLAY, "play" }, + { RIGHT_MOVE_ANY, "move any" }, + { RIGHT_MOVE_MINE, "move mine" }, + { RIGHT_MOVE_RANDOM, "move random" }, + { RIGHT_REMOVE_ANY, "remove any" }, + { RIGHT_REMOVE_MINE, "remove mine" }, + { RIGHT_REMOVE_RANDOM, "remove random" }, + { RIGHT_SCRATCH_ANY, "scratch any" }, + { RIGHT_SCRATCH_MINE, "scratch mine" }, + { RIGHT_SCRATCH_RANDOM, "scratch random" }, + { RIGHT_VOLUME, "volume" }, + { RIGHT_ADMIN, "admin" }, + { RIGHT_RESCAN, "rescan" }, + { RIGHT_REGISTER, "register" }, + { RIGHT_USERINFO, "userinfo" }, + { RIGHT_PREFS, "prefs" }, + { RIGHT_GLOBAL_PREFS, "global prefs" } +}; +#define NRIGHTS (sizeof rights_names / sizeof *rights_names) + +/** @brief Convert a rights word to a string */ +char *rights_string(rights_type r) { + struct dynstr d[1]; + size_t n; + + dynstr_init(d); + for(n = 0; n < NRIGHTS; ++n) { + if(r & rights_names[n].bit) { + if(d->nvec) + dynstr_append(d, ','); + dynstr_append_string(d, rights_names[n].name); + } + } + dynstr_terminate(d); + return d->vec; +} + +/** @brief Compute default rights for a new user + * @return Default rights value + */ +rights_type default_rights(void) { + /* TODO get rights from config */ + rights_type r = RIGHTS__MASK & ~(RIGHT_ADMIN|RIGHT_REGISTER + |RIGHT_MOVE__MASK + |RIGHT_SCRATCH__MASK + |RIGHT_REMOVE__MASK); + if(config->restrictions & RESTRICT_SCRATCH) + r |= RIGHT_SCRATCH_MINE|RIGHT_SCRATCH_RANDOM; + else + r |= RIGHT_SCRATCH_ANY; + if(!(config->restrictions & RESTRICT_MOVE)) + r |= RIGHT_MOVE_ANY; + if(config->restrictions & RESTRICT_REMOVE) + r |= RIGHT_REMOVE_MINE; + else + r |= RIGHT_REMOVE_ANY; + return r; +} + +/** @brief Parse a rights list + * @param s Rights list in string form + * @param rp Where to store rights, or NULL to just validate + * @return 0 on success, non-0 if @p s is not valid + */ +int parse_rights(const char *s, rights_type *rp) { + rights_type r = 0; + const char *t; + size_t n, l; + + if(!*s) { + /* You can't have no rights */ + error(0, "empty rights string"); + return -1; + } + while(*s) { + t = strchr(s, ','); + if(!t) + t = s + strlen(s); + l = (size_t)(t - s); + if(l == 3 && !strncmp(s, "all", 3)) + r = RIGHTS__MASK; + else { + for(n = 0; n < NRIGHTS; ++n) + if(strlen(rights_names[n].name) == l + && !strncmp(rights_names[n].name, s, l)) + break; + if(n >= NRIGHTS) { + error(0, "unknown user right '%.*s'", (int)l, s); + return -1; + } + r |= rights_names[n].bit; + } + s = t; + if(*s == ',') + ++s; + } + if(rp) + *rp = r; + return 0; +} + +/* +Local Variables: +c-basic-offset:2 +comment-column:40 +fill-column:79 +indent-tabs-mode:nil +End: +*/ diff --git a/lib/rights.h b/lib/rights.h new file mode 100644 index 0000000..f4bf363 --- /dev/null +++ b/lib/rights.h @@ -0,0 +1,106 @@ +/* + * This file is part of DisOrder + * Copyright (C) 2007 Richard Kettlewell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +/** @file lib/rights.h + * @brief User rights + */ + +#ifndef RIGHTS_H +#define RIGHTS_H + +/** @brief User can perform read-only operations */ +#define RIGHT_READ 0x00000001 + +/** @brief User can add tracks to the queue */ +#define RIGHT_PLAY 0x00000002 + +/** @brief User can move any track */ +#define RIGHT_MOVE_ANY 0x00000004 + +/** @brief User can move their own tracks */ +#define RIGHT_MOVE_MINE 0x00000008 + +/** @brief User can move randomly chosen tracks */ +#define RIGHT_MOVE_RANDOM 0x00000010 + +#define RIGHT_MOVE__MASK 0x0000001c + +/** @brief User can remove any track */ +#define RIGHT_REMOVE_ANY 0x00000020 + +/** @brief User can remove their own tracks */ +#define RIGHT_REMOVE_MINE 0x00000040 + +/** @brief User can remove randomly chosen tracks */ +#define RIGHT_REMOVE_RANDOM 0x00000080 + +#define RIGHT_REMOVE__MASK 0x000000e0 + +/** @brief User can scratch any track */ +#define RIGHT_SCRATCH_ANY 0x00000100 + +/** @brief User can scratch their own tracks */ +#define RIGHT_SCRATCH_MINE 0x00000200 + +/** @brief User can scratch randomly chosen tracks */ +#define RIGHT_SCRATCH_RANDOM 0x00000400 + +#define RIGHT_SCRATCH__MASK 0x00000700 + +/** @brief User can change the volume */ +#define RIGHT_VOLUME 0x00000800 + +/** @brief User can perform admin operations */ +#define RIGHT_ADMIN 0x00001000 + +/** @brief User can initiate a rescan */ +#define RIGHT_RESCAN 0x00002000 + +/** @brief User can register new users */ +#define RIGHT_REGISTER 0x00004000 + +/** @brief User can edit their own userinfo */ +#define RIGHT_USERINFO 0x00008000 + +/** @brief User can modify track preferences */ +#define RIGHT_PREFS 0x00010000 + +/** @brief User can modify global preferences */ +#define RIGHT_GLOBAL_PREFS 0x00020000 + +/** @brief Current rights mask */ +#define RIGHTS__MASK 0x0003ffff + +/** @brief Unsigned type big enough for rights */ +typedef uint32_t rights_type; + +rights_type default_rights(void); +char *rights_string(rights_type r); +int parse_rights(const char *s, rights_type *rp); + +#endif /* RIGHTS_H */ + +/* +Local Variables: +c-basic-offset:2 +comment-column:40 +fill-column:79 +indent-tabs-mode:nil +End: +*/ diff --git a/lib/trackdb.c b/lib/trackdb.c index d3e6e40..90d9457 100644 --- a/lib/trackdb.c +++ b/lib/trackdb.c @@ -49,6 +49,7 @@ #include "kvp.h" #include "log.h" #include "vector.h" +#include "rights.h" #include "trackdb.h" #include "configuration.h" #include "syscalls.h" @@ -2423,69 +2424,6 @@ static int trusted(const char *user) { return n < config->trust.n; } -static const struct { - rights_type bit; - const char *name; -} rights_names[] = { - { RIGHT_READ, "read" }, - { RIGHT_PLAY, "play" }, - { RIGHT_MOVE_ANY, "move any" }, - { RIGHT_MOVE_MINE, "move mine" }, - { RIGHT_MOVE_RANDOM, "move random" }, - { RIGHT_REMOVE_ANY, "remove any" }, - { RIGHT_REMOVE_MINE, "remove mine" }, - { RIGHT_REMOVE_RANDOM, "remove random" }, - { RIGHT_SCRATCH_ANY, "scratch any" }, - { RIGHT_SCRATCH_MINE, "scratch mine" }, - { RIGHT_SCRATCH_RANDOM, "scratch random" }, - { RIGHT_VOLUME, "volume" }, - { RIGHT_ADMIN, "admin" }, - { RIGHT_RESCAN, "rescan" }, - { RIGHT_REGISTER, "register" }, - { RIGHT_USERINFO, "userinfo" }, - { RIGHT_PREFS, "prefs" }, - { RIGHT_GLOBAL_PREFS, "global prefs" } -}; -#define NRIGHTS (sizeof rights_names / sizeof *rights_names) - -/** @brief Convert a rights word to a string */ -static char *rights_string(rights_type r) { - struct dynstr d[1]; - size_t n; - - dynstr_init(d); - for(n = 0; n < NRIGHTS; ++n) { - if(r & rights_names[n].bit) { - if(d->nvec) - dynstr_append(d, ','); - dynstr_append_string(d, rights_names[n].name); - } - } - dynstr_terminate(d); - return d->vec; -} - -/** @brief Compute default rights for a new user */ -rights_type default_rights(void) { - /* TODO get rights from config. This is probably in the wrong place but it - * will do for now... */ - rights_type r = RIGHTS__MASK & ~(RIGHT_ADMIN|RIGHT_REGISTER - |RIGHT_MOVE__MASK - |RIGHT_SCRATCH__MASK - |RIGHT_REMOVE__MASK); - if(config->restrictions & RESTRICT_SCRATCH) - r |= RIGHT_SCRATCH_MINE|RIGHT_SCRATCH_RANDOM; - else - r |= RIGHT_SCRATCH_ANY; - if(!(config->restrictions & RESTRICT_MOVE)) - r |= RIGHT_MOVE_ANY; - if(config->restrictions & RESTRICT_REMOVE) - r |= RIGHT_REMOVE_MINE; - else - r |= RIGHT_REMOVE_ANY; - return r; -} - /** @brief Add a user */ static int create_user(const char *user, const char *password, @@ -2580,6 +2518,9 @@ void trackdb_create_root(void) { * Only works if running as a user that can read the database! * * If the user exists but has no password, "" is returned. + * + * If the user was created with 'register' and has not yet been confirmed then + * NULL is still returned. */ const char *trackdb_get_password(const char *user) { int e; @@ -2589,6 +2530,8 @@ const char *trackdb_get_password(const char *user) { WITH_TRANSACTION(trackdb_getdata(trackdb_usersdb, user, &k, tid)); if(e) return 0; + if(kvp_get(k, "confirmation")) + return 0; password = kvp_get(k, "password"); return password ? password : ""; } @@ -2685,10 +2628,33 @@ int trackdb_edituserinfo(const char *user, const char *key, const char *value) { int e; + if(!strcmp(key, "rights")) { + if(!value) { + error(0, "cannot remove 'rights' key from user '%s'", user); + return -1; + } + if(parse_rights(value, 0)) { + error(0, "invalid rights string"); + return -1; + } + } else if(!strcmp(key, "email")) { + if(!strchr(value, '@')) { + error(0, "invalid email address '%s' for user '%s'", user, value); + return -1; + } + } else if(!strcmp(key, "created")) { + error(0, "cannot change creation date for user '%s'", user); + return -1; + } else if(strcmp(key, "password") + && !strcmp(key, "confirmation")) { + error(0, "unknown user info key '%s' for user '%s'", key, user); + return -1; + } WITH_TRANSACTION(trackdb_edituserinfo_tid(user, key, value, tid)); - if(e) + if(e) { + error(0, "unknown user '%s'", user); return -1; - else + } else return 0; } diff --git a/lib/trackdb.h b/lib/trackdb.h index b317974..a136537 100644 --- a/lib/trackdb.h +++ b/lib/trackdb.h @@ -29,72 +29,6 @@ extern const struct cache_type cache_files_type; extern unsigned long cache_files_hits, cache_files_misses; /* Cache entry type and tracking for regexp-based lookups */ -/** @brief User can perform read-only operations */ -#define RIGHT_READ 0x00000001 - -/** @brief User can add tracks to the queue */ -#define RIGHT_PLAY 0x00000002 - -/** @brief User can move any track */ -#define RIGHT_MOVE_ANY 0x00000004 - -/** @brief User can move their own tracks */ -#define RIGHT_MOVE_MINE 0x00000008 - -/** @brief User can move randomly chosen tracks */ -#define RIGHT_MOVE_RANDOM 0x00000010 - -#define RIGHT_MOVE__MASK 0x0000001c - -/** @brief User can remove any track */ -#define RIGHT_REMOVE_ANY 0x00000020 - -/** @brief User can remove their own tracks */ -#define RIGHT_REMOVE_MINE 0x00000040 - -/** @brief User can remove randomly chosen tracks */ -#define RIGHT_REMOVE_RANDOM 0x00000080 - -#define RIGHT_REMOVE__MASK 0x000000e0 - -/** @brief User can scratch any track */ -#define RIGHT_SCRATCH_ANY 0x00000100 - -/** @brief User can scratch their own tracks */ -#define RIGHT_SCRATCH_MINE 0x00000200 - -/** @brief User can scratch randomly chosen tracks */ -#define RIGHT_SCRATCH_RANDOM 0x00000400 - -#define RIGHT_SCRATCH__MASK 0x00000700 - -/** @brief User can change the volume */ -#define RIGHT_VOLUME 0x00000800 - -/** @brief User can perform admin operations */ -#define RIGHT_ADMIN 0x00001000 - -/** @brief User can initiate a rescan */ -#define RIGHT_RESCAN 0x00002000 - -/** @brief User can register new users */ -#define RIGHT_REGISTER 0x00004000 - -/** @brief User can edit their own userinfo */ -#define RIGHT_USERINFO 0x00008000 - -/** @brief User can modify track preferences */ -#define RIGHT_PREFS 0x00010000 - -/** @brief User can modify global preferences */ -#define RIGHT_GLOBAL_PREFS 0x00020000 - -/** @brief Current rights mask */ -#define RIGHTS__MASK 0x0003ffff - -/** @brief Unsigned type big enough for rights */ -typedef uint32_t rights_type; - /** @brief Do not attempt database recovery (trackdb_init()) */ #define TRACKDB_NO_RECOVER 0x0000 @@ -234,8 +168,6 @@ struct kvp *trackdb_getuserinfo(const char *user); int trackdb_edituserinfo(const char *user, const char *key, const char *value); -rights_type default_rights(void); - #endif /* TRACKDB_H */ /* diff --git a/python/disorder.py.in b/python/disorder.py.in index 6478981..325599b 100644 --- a/python/disorder.py.in +++ b/python/disorder.py.in @@ -868,6 +868,17 @@ class client: """Delete a user""" self._simple("deluser", user) + def userinfo(self, user, key): + """Get user information""" + res, details = self._simple("userinfo", user, key) + if res == 555: + return None + return _split(details)[0] + + def edituser(self, user, key, value): + """Set user information""" + self._simple("edituser", user, key, value) + ######################################################################## # I/O infrastructure diff --git a/server/api-server.c b/server/api-server.c index 77960a3..e152980 100644 --- a/server/api-server.c +++ b/server/api-server.c @@ -33,6 +33,7 @@ #include "mem.h" #include "disorder.h" #include "event.h" +#include "rights.h" #include "trackdb.h" int disorder_track_exists(const char *track) { diff --git a/server/dbupgrade.c b/server/dbupgrade.c index b4cf58e..0d8600d 100644 --- a/server/dbupgrade.c +++ b/server/dbupgrade.c @@ -34,6 +34,7 @@ #include "log.h" #include "defs.h" #include "kvp.h" +#include "rights.h" #include "trackdb.h" #include "trackdb-int.h" #include "mem.h" diff --git a/server/deadlock.c b/server/deadlock.c index bce3eee..99359b2 100644 --- a/server/deadlock.c +++ b/server/deadlock.c @@ -39,6 +39,7 @@ #include "defs.h" #include "mem.h" #include "kvp.h" +#include "rights.h" #include "trackdb.h" #include "trackdb-int.h" diff --git a/server/disorderd.c b/server/disorderd.c index c1f578e..f7bc378 100644 --- a/server/disorderd.c +++ b/server/disorderd.c @@ -44,6 +44,7 @@ #include "event.h" #include "log.h" #include "configuration.h" +#include "rights.h" #include "trackdb.h" #include "queue.h" #include "mem.h" diff --git a/server/dump.c b/server/dump.c index ec1fc4d..3be14be 100644 --- a/server/dump.c +++ b/server/dump.c @@ -42,6 +42,7 @@ #include "kvp.h" #include "vector.h" #include "inputline.h" +#include "rights.h" #include "trackdb.h" #include "trackdb-int.h" #include "charset.h" diff --git a/server/play.c b/server/play.c index 7b70799..e9c2b68 100644 --- a/server/play.c +++ b/server/play.c @@ -44,6 +44,7 @@ #include "configuration.h" #include "queue.h" #include "server-queue.h" +#include "rights.h" #include "trackdb.h" #include "play.h" #include "plugin.h" diff --git a/server/rescan.c b/server/rescan.c index e45eaa6..dab56df 100644 --- a/server/rescan.c +++ b/server/rescan.c @@ -47,6 +47,7 @@ #include "wstat.h" #include "kvp.h" #include "printf.h" +#include "rights.h" #include "trackdb.h" #include "trackdb-int.h" #include "trackname.h" diff --git a/server/server.c b/server/server.c index 145bfe2..6591d91 100644 --- a/server/server.c +++ b/server/server.c @@ -51,6 +51,7 @@ #include "split.h" #include "configuration.h" #include "hex.h" +#include "rights.h" #include "trackdb.h" #include "table.h" #include "kvp.h" @@ -1050,16 +1051,42 @@ static int c_deluser(struct conn *c, } static int c_edituser(struct conn *c, - char attribute((unused)) **vec, + char **vec, int attribute((unused)) nvec) { - sink_writes(ev_writer_sink(c->w), "550 Not implemented\n"); /* TODO */ + /* TODO local only */ + if(trusted(c) + || (!strcmp(c->who, vec[0]) + && (!strcmp(vec[1], "email") + || !strcmp(vec[1], "password")))) { + if(trackdb_edituserinfo(vec[0], vec[1], vec[2])) + sink_writes(ev_writer_sink(c->w), "550 Failed to change setting\n"); + else + sink_writes(ev_writer_sink(c->w), "250 OK\n"); + } else + sink_writes(ev_writer_sink(c->w), "550 Restricted to administrators\n"); return 1; } static int c_userinfo(struct conn *c, char attribute((unused)) **vec, int attribute((unused)) nvec) { - sink_writes(ev_writer_sink(c->w), "550 Not implemented\n"); /* TODO */ + struct kvp *k; + const char *value; + + /* TODO local only */ + if(trusted(c) + || (!strcmp(c->who, vec[0]) + && (!strcmp(vec[1], "email") + || !strcmp(vec[1], "rights")))) { + if((k = trackdb_getuserinfo(vec[0]))) + if((value = kvp_get(k, vec[1]))) + sink_printf(ev_writer_sink(c->w), "252 %s\n", quoteutf8(value)); + else + sink_writes(ev_writer_sink(c->w), "555 Not set\n"); + else + sink_writes(ev_writer_sink(c->w), "550 No such user\n"); + } else + sink_writes(ev_writer_sink(c->w), "550 Restricted to administrators\n"); return 1; } diff --git a/server/state.c b/server/state.c index 79cae43..d6b6699 100644 --- a/server/state.c +++ b/server/state.c @@ -36,6 +36,7 @@ #include "event.h" #include "play.h" +#include "rights.h" #include "trackdb.h" #include "state.h" #include "configuration.h" diff --git a/server/stats.c b/server/stats.c index c397be8..44558eb 100644 --- a/server/stats.c +++ b/server/stats.c @@ -34,6 +34,7 @@ #include "log.h" #include "syscalls.h" #include "configuration.h" +#include "rights.h" #include "trackdb.h" static const struct option options[] = { diff --git a/tests/user.py b/tests/user.py index 8c13232..13bee66 100755 --- a/tests/user.py +++ b/tests/user.py @@ -30,6 +30,10 @@ def test(): print " checking new user can log in" c = disorder.client(user="bob", password="bobpass") c.version() + print " checking bob can set their email address" + c.edituser("bob", "email", "foo@bar") + email = c.userinfo("bob", "email") + assert email == "foo@bar", "checking bob's email address" print " checking user deletion" c = disorder.client() c.deluser("bob")