AmendHub

jcs

/

subtext

/

amendments

/

95

signup: Add interactive user account creation


jcs made amendment 95 9 months ago
--- db.c Sun Apr 10 09:20:09 2022 +++ db.c Wed Apr 20 21:59:38 2022 @@ -198,9 +198,9 @@ db_migrate(struct db *tdb, short is_new) db_config_save(tdb); /* create a default sysop user */ - user = user_build("sysop", &error); - if (!user) - panic("Failed creating initial sysop user: %s", error); + user = xmalloczero(sizeof(struct user)); + strncpy(user->username, "sysop", sizeof(user->username)); + user->created_at = Time; user_set_password(user, "p4ssw0rd"); user->is_sysop = DB_TRUE; user_save(user); --- session.c Thu Apr 14 16:04:15 2022 +++ session.c Thu Apr 21 17:02:47 2022 @@ -24,6 +24,7 @@ #include "mail.h" #include "subtext.h" #include "session.h" +#include "signup.h" #include "user.h" #include "uthread.h" #include "util.h" @@ -126,7 +127,8 @@ session_run(struct uthread *uthread, void *arg) s->user ? s->user->username : GUEST_USERNAME, sessions_tally, ordinal(sessions_tally)); session_flush(s); - + +main_menu: if (!session_output_view(s, DB_TEXT_MENU_ID)) session_output_string(s, "\r\n[ Menu missing! ]\r\n\r\n"); session_flush(s); @@ -167,6 +169,16 @@ get_another_char: case 'M': mail_menu(s); break; + case 's': + case 'S': + if (s->user) + ; //user_settings_menu(s); + else if ((s->user = signup(s))) { + session_printf(s, "\r\n" + "Welcome, {{B}}%s{{/}}!\r\n", s->user->username); + goto main_menu; + } + break; case 'w': case 'W': /* who's online */ @@ -1054,7 +1066,7 @@ session_recents(struct session *s) scount = bile_sorted_ids_by_type(db->sessions_bile, SL_LOG_RTYPE, &ids); - for (printed = 0; printed < 10, printed < scount; printed++) { + for (printed = 0; printed < 10 && printed < scount; printed++) { rsize = bile_read(db->sessions_bile, SL_LOG_RTYPE, ids[scount - 1 - printed], (char *)&slog, sizeof(slog)); if (rsize != sizeof(slog)) --- session.h Tue Mar 1 14:06:19 2022 +++ session.h Wed Apr 20 16:23:24 2022 @@ -31,8 +31,6 @@ enum session_input_state { #define DEFAULT_TERMINAL_COLUMNS 80 #define DEFAULT_TERMINAL_LINES 24 -#define GUEST_USERNAME "guest" - #define CONTROL_C 3 #define CONTROL_D 4 #define BACKSPACE 8 --- signup.c Thu Apr 21 16:52:17 2022 +++ signup.c Thu Apr 21 16:52:17 2022 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2022 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "subtext.h" +#include "session.h" +#include "signup.h" +#include "user.h" +#include "util.h" + +struct user * +signup(struct session *s) +{ + struct user *user = NULL; + char junk[SHA256_DIGEST_STRING_LENGTH]; + char *username = NULL, *password = NULL, *password_confirm = NULL, + *error = NULL; + size_t len; + short n; + + logger_printf(logger, "[%s] Signing up for an account", s->node); + + session_output_template(s, "{{B}}Create Account{{/}} " + "(^C to cancel)\r\n" + "{{B}}----------------------------{{/}}\r\n"); + session_flush(s); + + for (;;) { + session_printf(s, "{{B}}Username{{/}} (Up to %d letters/numbers): ", + DB_USERNAME_LENGTH); + session_flush(s); + + username = session_field_input(s, DB_USERNAME_LENGTH + 1, + DB_USERNAME_LENGTH, NULL, 0); + session_output(s, "\r\n", 2); + session_flush(s); + + if (username == NULL || username[0] == '\0' || s->ending) + goto signup_done; + + if (user_valid_username(username, &error) != 1) { + session_printf(s, "{{B}}Error:{{/}} %s\r\n", error); + free(error); + continue; + } + + break; + } + + for (;;) { + session_output_template(s, "{{B}}Password:{{/}} "); + session_flush(s); + password = session_field_input(s, 64, 64, NULL, '*'); + session_output(s, "\r\n", 2); + session_flush(s); + + if (password == NULL || s->ending) + goto signup_done; + + if (password[0] == '\0') { + session_printf(s, "{{B}}Error:{{/}} " + "Password cannot be blank\r\n"); + free(password); + continue; + } + + session_output_template(s, "{{B}}Password (again):{{/}} "); + session_flush(s); + password_confirm = session_field_input(s, 64, 64, NULL, '*'); + session_output(s, "\r\n", 2); + session_flush(s); + + if (password_confirm == NULL || s->ending) + goto signup_done; + + if (strcmp(password_confirm, password) != 0) { + session_printf(s, "{{B}}Error:{{/}} " + "Passwords do not match\r\n"); + free(password); + free(password_confirm); + continue; + } + + break; + } + + user = xmalloczero(sizeof(struct user)); + strncpy(user->username, username, sizeof(user->username)); + user->created_at = Time; + user_set_password(user, password); + user_save(user); + + logger_printf(logger, "[%s] New user account created: %s", s->node, + user->username); + +signup_done: + if (username) + free(username); + if (password) + free(password); + if (password_confirm) + free(password_confirm); + + return user; +} --- signup.h Wed Apr 20 16:17:51 2022 +++ signup.h Wed Apr 20 16:17:51 2022 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __SIGNUP_H__ +#define __SIGNUP_H__ + +#include "session.h" +#include "user.h" + +struct user * signup(struct session *s); + +#endif --- user.c Sat Jan 29 18:41:08 2022 +++ user.c Wed Apr 20 20:23:52 2022 @@ -24,6 +24,25 @@ #include "user.h" #include "util.h" +static char *BANNED_USERNAMES[] = { + "admin", "administrator", "\"admin", "admin1", + "client", "contact", + "daemon", "default", + "enable", + "fraud", + "guest", + "help", "hostmaster", + "mailer-daemon", + "moderator", "moderators", + "nobody", "notme", + "postmaster", + "root", "\"root", + "security", "server", "support", "sventek", "sysop", "system", + "test", + "ubnt", "unknown", + "webmaster", +}; + void user_update_cache_map(void) { @@ -54,40 +73,6 @@ user_update_cache_map(void) } } -struct user * -user_build(char *username, char **error) -{ - struct user suser; - struct user *user; - size_t len, n; - - len = strlen(username); - if (len >= sizeof(suser.username)) { - *error = xstrdup("username is too long"); - return NULL; - } - - for (n = 0; n < len; n++) { - if (!isalnum(username[n])) { - *error = xstrdup("username cannot contain " - "non-alphanumeric characters"); - return NULL; - } - } - - if ((user = user_find_by_username(username))) { - free(user); - *error = xstrdup("username already exists"); - return NULL; - } - - user = xmalloczero(sizeof(struct user)); - strncpy(user->username, username, sizeof(user->username)); - user->created_at = Time; - - return user; -} - void user_save(struct user *user) { @@ -221,4 +206,68 @@ user_set_password(struct user *user, const char *passw memset(&hash, 0, sizeof(hash)); memset(&salt, 0, sizeof(salt)); +} + +short +user_valid_username(char *username, char **error) +{ + char *lower = NULL, *tmp; + struct user *user; + size_t len, n; + char c; + short ret = 0; + + if (username == NULL) { + *error = xstrdup("username cannot be empty"); + goto done; + } + + len = strlen(username); + if (len > DB_USERNAME_LENGTH) { + *error = xmalloc(61); + snprintf(*error, 60, "username cannot be more than %d characters", + DB_USERNAME_LENGTH); + goto done; + } + + lower = xmalloc(len + 1); + + for (n = 0; n < len; n++) { + c = username[n]; + + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || c == '_')) { + *error = xstrdup("username cannot contain non-alphanumeric " + "characters"); + goto done; + } + + lower[n] = tolower(username[n]); + } + lower[len] = '\0'; + + if (strcmp(GUEST_USERNAME, lower) == 0) { + *error = xstrdup("username is already in use"); + goto done; + } + + for (n = 0; n < nitems(BANNED_USERNAMES); n++) { + if (strcmp(lower, BANNED_USERNAMES[n]) == 0) { + *error = xstrdup("username is not permitted"); + goto done; + } + } + + if ((user = user_find_by_username(username))) { + free(user); + *error = xstrdup("username is already in use"); + goto done; + } + + ret = 1; + +done: + if (lower) + free(lower); + return ret; } --- user.h Sat Jan 29 18:32:10 2022 +++ user.h Wed Apr 20 20:24:01 2022 @@ -22,12 +22,14 @@ #define AUTH_USER_OK 1 #define AUTH_USER_FAILED 2 +#define GUEST_USERNAME "guest" + void user_update_cache_map(void); -struct user * user_build(char *username, char **error); void user_save(struct user *user); struct user * user_find(unsigned long id); struct user * user_find_by_username(const char *username); short user_authenticate(struct user *user, const char *password); void user_set_password(struct user *user, const char *password); +short user_valid_username(char *username, char **error); #endif