jcs
/subtext
/amendments
/95
signup: Add interactive user account creation
jcs made amendment 95 over 2 years 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