Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * createuser
4 : *
5 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
6 : * Portions Copyright (c) 1994, Regents of the University of California
7 : *
8 : * src/bin/scripts/createuser.c
9 : *
10 : *-------------------------------------------------------------------------
11 : */
12 :
13 : #include "postgres_fe.h"
14 :
15 : #include <limits.h>
16 :
17 : #include "common.h"
18 : #include "common/logging.h"
19 : #include "common/string.h"
20 : #include "fe_utils/option_utils.h"
21 : #include "fe_utils/simple_list.h"
22 : #include "fe_utils/string_utils.h"
23 :
24 :
25 : static void help(const char *progname);
26 :
27 : int
7327 peter_e 28 CBC 20 : main(int argc, char *argv[])
29 : {
30 : static struct option long_options[] = {
31 : {"admin", required_argument, NULL, 'a'},
32 : {"connection-limit", required_argument, NULL, 'c'},
33 : {"createdb", no_argument, NULL, 'd'},
34 : {"no-createdb", no_argument, NULL, 'D'},
35 : {"echo", no_argument, NULL, 'e'},
36 : {"encrypted", no_argument, NULL, 'E'},
37 : {"role", required_argument, NULL, 'g'},
38 : {"host", required_argument, NULL, 'h'},
39 : {"inherit", no_argument, NULL, 'i'},
40 : {"no-inherit", no_argument, NULL, 'I'},
41 : {"login", no_argument, NULL, 'l'},
42 : {"no-login", no_argument, NULL, 'L'},
43 : {"member", required_argument, NULL, 'm'},
44 : {"port", required_argument, NULL, 'p'},
45 : {"pwprompt", no_argument, NULL, 'P'},
46 : {"createrole", no_argument, NULL, 'r'},
47 : {"no-createrole", no_argument, NULL, 'R'},
48 : {"superuser", no_argument, NULL, 's'},
49 : {"no-superuser", no_argument, NULL, 'S'},
50 : {"username", required_argument, NULL, 'U'},
51 : {"valid-until", required_argument, NULL, 'v'},
52 : {"no-password", no_argument, NULL, 'w'},
53 : {"password", no_argument, NULL, 'W'},
54 : {"replication", no_argument, NULL, 1},
55 : {"no-replication", no_argument, NULL, 2},
56 : {"interactive", no_argument, NULL, 3},
57 : {"bypassrls", no_argument, NULL, 4},
58 : {"no-bypassrls", no_argument, NULL, 5},
59 : {NULL, 0, NULL, 0}
60 : };
61 :
62 : const char *progname;
63 : int optindex;
64 : int c;
4079 peter_e 65 GIC 20 : const char *newuser = NULL;
7327 66 20 : char *host = NULL;
67 20 : char *port = NULL;
68 20 : char *username = NULL;
3406 rhaas 69 20 : SimpleStringList roles = {NULL, NULL};
270 michael 70 GNC 20 : SimpleStringList members = {NULL, NULL};
71 20 : SimpleStringList admins = {NULL, NULL};
5155 peter_e 72 CBC 20 : enum trivalue prompt_password = TRI_DEFAULT;
902 tgl 73 ECB : ConnParams cparams;
7327 peter_e 74 CBC 20 : bool echo = false;
4079 75 20 : bool interactive = false;
1154 alvherre 76 20 : int conn_limit = -2; /* less than minimum valid value */
6327 bruce 77 20 : bool pwprompt = false;
78 20 : char *newpassword = NULL;
270 michael 79 GNC 20 : char *pwexpiry = NULL;
6031 bruce 80 ECB :
81 : /* Tri-valued variables. */
6031 bruce 82 CBC 20 : enum trivalue createdb = TRI_DEFAULT,
83 20 : superuser = TRI_DEFAULT,
84 20 : createrole = TRI_DEFAULT,
85 20 : inherit = TRI_DEFAULT,
86 20 : login = TRI_DEFAULT,
270 michael 87 GNC 20 : replication = TRI_DEFAULT,
88 20 : bypassrls = TRI_DEFAULT;
89 :
90 : PQExpBufferData sql;
7327 peter_e 91 ECB :
92 : PGconn *conn;
93 : PGresult *result;
94 :
1469 peter 95 CBC 20 : pg_logging_init(argv[0]);
7327 peter_e 96 20 : progname = get_progname(argv[0]);
5232 97 20 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts"));
98 :
7327 peter_e 99 GIC 20 : handle_help_version_opts(argc, argv, "createuser", help);
100 :
270 michael 101 GNC 42 : while ((c = getopt_long(argc, argv, "a:c:dDeEg:h:iIlLm:p:PrRsSU:v:wW",
6447 tgl 102 GIC 42 : long_options, &optindex)) != -1)
103 : {
7327 peter_e 104 CBC 25 : switch (c)
7327 peter_e 105 ECB : {
270 michael 106 GNC 2 : case 'a':
107 2 : simple_string_list_append(&admins, optarg);
270 michael 108 CBC 2 : break;
270 michael 109 UNC 0 : case 'c':
110 0 : if (!option_parse_int(optarg, "-c/--connection-limit",
111 : -1, INT_MAX, &conn_limit))
112 0 : exit(1);
7327 peter_e 113 UBC 0 : break;
114 0 : case 'd':
6327 bruce 115 0 : createdb = TRI_YES;
7327 peter_e 116 0 : break;
117 0 : case 'D':
6327 bruce 118 0 : createdb = TRI_NO;
7327 peter_e 119 0 : break;
270 michael 120 UNC 0 : case 'e':
121 0 : echo = true;
6447 tgl 122 LBC 0 : break;
270 michael 123 UNC 0 : case 'E':
124 : /* no-op, accepted for backward compatibility */
6447 tgl 125 UBC 0 : break;
270 michael 126 GNC 1 : case 'g':
127 1 : simple_string_list_append(&roles, optarg);
6447 tgl 128 GBC 1 : break;
270 michael 129 UNC 0 : case 'h':
130 0 : host = pg_strdup(optarg);
6447 tgl 131 UBC 0 : break;
7327 peter_e 132 0 : case 'i':
6327 bruce 133 0 : inherit = TRI_YES;
6447 tgl 134 0 : break;
135 0 : case 'I':
6327 bruce 136 0 : inherit = TRI_NO;
6447 tgl 137 LBC 0 : break;
138 0 : case 'l':
6327 bruce 139 0 : login = TRI_YES;
6447 tgl 140 0 : break;
6447 tgl 141 CBC 1 : case 'L':
6327 bruce 142 1 : login = TRI_NO;
6447 tgl 143 GBC 1 : break;
270 michael 144 GNC 2 : case 'm':
145 2 : simple_string_list_append(&members, optarg);
146 2 : break;
270 michael 147 UNC 0 : case 'p':
148 0 : port = pg_strdup(optarg);
7327 peter_e 149 UBC 0 : break;
7327 peter_e 150 LBC 0 : case 'P':
151 0 : pwprompt = true;
152 0 : break;
270 michael 153 GNC 1 : case 'r':
154 1 : createrole = TRI_YES;
155 1 : break;
270 michael 156 UNC 0 : case 'R':
157 0 : createrole = TRI_NO;
158 0 : break;
270 michael 159 GNC 7 : case 's':
160 7 : superuser = TRI_YES;
161 7 : break;
270 michael 162 UNC 0 : case 'S':
163 0 : superuser = TRI_NO;
164 0 : break;
270 michael 165 GNC 6 : case 'U':
166 6 : username = pg_strdup(optarg);
167 6 : break;
168 1 : case 'v':
169 1 : pwexpiry = pg_strdup(optarg);
170 1 : break;
270 michael 171 UNC 0 : case 'w':
172 0 : prompt_password = TRI_NO;
173 0 : break;
174 0 : case 'W':
175 0 : prompt_password = TRI_YES;
7327 peter_e 176 UBC 0 : break;
4216 rhaas 177 CBC 1 : case 1:
178 1 : replication = TRI_YES;
179 1 : break;
4216 rhaas 180 UBC 0 : case 2:
181 0 : replication = TRI_NO;
182 0 : break;
4079 peter_e 183 LBC 0 : case 3:
184 0 : interactive = true;
185 0 : break;
270 michael 186 GNC 1 : case 4:
187 1 : bypassrls = TRI_YES;
188 1 : break;
189 1 : case 5:
190 1 : bypassrls = TRI_NO;
191 1 : break;
7327 peter_e 192 CBC 1 : default:
366 tgl 193 ECB : /* getopt_long already emitted a complaint */
366 tgl 194 CBC 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
7327 peter_e 195 GBC 1 : exit(1);
7327 peter_e 196 EUB : }
197 : }
198 :
7327 peter_e 199 GBC 17 : switch (argc - optind)
7327 peter_e 200 EUB : {
7327 peter_e 201 LBC 0 : case 0:
202 0 : break;
7327 peter_e 203 CBC 17 : case 1:
7327 peter_e 204 GBC 17 : newuser = argv[optind];
205 17 : break;
7327 peter_e 206 UBC 0 : default:
1469 peter 207 0 : pg_log_error("too many command-line arguments (first is \"%s\")",
1469 peter 208 EUB : argv[optind + 1]);
366 tgl 209 UBC 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
7327 peter_e 210 LBC 0 : exit(1);
7327 peter_e 211 ECB : }
212 :
7327 peter_e 213 CBC 17 : if (newuser == NULL)
4079 peter_e 214 ECB : {
4079 peter_e 215 LBC 0 : if (interactive)
2413 tgl 216 ECB : {
948 tgl 217 UIC 0 : newuser = simple_prompt("Enter name of role to add: ", true);
2413 tgl 218 ECB : }
4079 peter_e 219 : else
220 : {
4079 peter_e 221 UIC 0 : if (getenv("PGUSER"))
222 0 : newuser = getenv("PGUSER");
4079 peter_e 223 ECB : else
3399 bruce 224 UIC 0 : newuser = get_user_name_or_exit(progname);
4079 peter_e 225 EUB : }
226 : }
7327 peter_e 227 ECB :
7327 peter_e 228 CBC 17 : if (pwprompt)
7327 peter_e 229 ECB : {
948 tgl 230 EUB : char *pw2;
7327 peter_e 231 :
948 tgl 232 UIC 0 : newpassword = simple_prompt("Enter password for new role: ", false);
948 tgl 233 UBC 0 : pw2 = simple_prompt("Enter it again: ", false);
234 0 : if (strcmp(newpassword, pw2) != 0)
235 : {
7327 peter_e 236 UIC 0 : fprintf(stderr, _("Passwords didn't match.\n"));
7327 peter_e 237 LBC 0 : exit(1);
238 : }
948 tgl 239 UBC 0 : free(pw2);
240 : }
7327 peter_e 241 EUB :
34 dgustafsson 242 GNC 17 : if (superuser == TRI_DEFAULT)
243 : {
4079 peter_e 244 GIC 10 : if (interactive && yesno_prompt("Shall the new role be a superuser?"))
6327 bruce 245 UBC 0 : superuser = TRI_YES;
6447 tgl 246 EUB : else
6327 bruce 247 GIC 10 : superuser = TRI_NO;
6447 tgl 248 EUB : }
249 :
6327 bruce 250 GIC 17 : if (superuser == TRI_YES)
251 : {
6447 tgl 252 ECB : /* Not much point in trying to restrict a superuser */
6327 bruce 253 GIC 7 : createdb = TRI_YES;
254 7 : createrole = TRI_YES;
255 : }
6447 tgl 256 EUB :
34 dgustafsson 257 GNC 17 : if (createdb == TRI_DEFAULT)
7327 peter_e 258 EUB : {
4079 peter_e 259 GIC 10 : if (interactive && yesno_prompt("Shall the new role be allowed to create databases?"))
6327 bruce 260 UBC 0 : createdb = TRI_YES;
7327 peter_e 261 EUB : else
6327 bruce 262 GIC 10 : createdb = TRI_NO;
7327 peter_e 263 EUB : }
264 :
34 dgustafsson 265 GNC 17 : if (createrole == TRI_DEFAULT)
7327 peter_e 266 ECB : {
4079 peter_e 267 GIC 9 : if (interactive && yesno_prompt("Shall the new role be allowed to create more new roles?"))
6327 bruce 268 LBC 0 : createrole = TRI_YES;
7327 peter_e 269 EUB : else
6327 bruce 270 GIC 9 : createrole = TRI_NO;
6447 tgl 271 ECB : }
272 :
34 dgustafsson 273 GNC 17 : if (bypassrls == TRI_DEFAULT)
274 15 : bypassrls = TRI_NO;
275 :
276 17 : if (replication == TRI_DEFAULT)
277 16 : replication = TRI_NO;
278 :
279 17 : if (inherit == TRI_DEFAULT)
6327 bruce 280 CBC 17 : inherit = TRI_YES;
281 :
34 dgustafsson 282 GNC 17 : if (login == TRI_DEFAULT)
6327 bruce 283 CBC 16 : login = TRI_YES;
7327 peter_e 284 ECB :
902 tgl 285 GIC 17 : cparams.dbname = NULL; /* this program lacks any dbname option... */
286 17 : cparams.pghost = host;
902 tgl 287 CBC 17 : cparams.pgport = port;
902 tgl 288 GIC 17 : cparams.pguser = username;
902 tgl 289 CBC 17 : cparams.prompt_password = prompt_password;
902 tgl 290 GBC 17 : cparams.override_dbname = NULL;
291 :
902 tgl 292 CBC 17 : conn = connectMaintenanceDatabase(&cparams, progname, echo);
293 :
7327 peter_e 294 GIC 17 : initPQExpBuffer(&sql);
7327 peter_e 295 ECB :
6447 tgl 296 GIC 17 : printfPQExpBuffer(&sql, "CREATE ROLE %s", fmtId(newuser));
7327 peter_e 297 CBC 17 : if (newpassword)
7327 peter_e 298 EUB : {
299 : char *encrypted_password;
2162 heikki.linnakangas 300 ECB :
3429 heikki.linnakangas 301 UIC 0 : appendPQExpBufferStr(&sql, " PASSWORD ");
302 :
2162 heikki.linnakangas 303 LBC 0 : encrypted_password = PQencryptPasswordConn(conn,
2162 heikki.linnakangas 304 ECB : newpassword,
305 : newuser,
306 : NULL);
2162 heikki.linnakangas 307 LBC 0 : if (!encrypted_password)
366 tgl 308 UIC 0 : pg_fatal("password encryption failed: %s",
366 tgl 309 ECB : PQerrorMessage(conn));
2162 heikki.linnakangas 310 LBC 0 : appendStringLiteralConn(&sql, encrypted_password, conn);
2162 heikki.linnakangas 311 UIC 0 : PQfreemem(encrypted_password);
7327 peter_e 312 ECB : }
6327 bruce 313 CBC 17 : if (superuser == TRI_YES)
3429 heikki.linnakangas 314 GIC 7 : appendPQExpBufferStr(&sql, " SUPERUSER");
6327 bruce 315 CBC 17 : if (superuser == TRI_NO)
3429 heikki.linnakangas 316 10 : appendPQExpBufferStr(&sql, " NOSUPERUSER");
6327 bruce 317 17 : if (createdb == TRI_YES)
3429 heikki.linnakangas 318 7 : appendPQExpBufferStr(&sql, " CREATEDB");
6327 bruce 319 17 : if (createdb == TRI_NO)
3429 heikki.linnakangas 320 10 : appendPQExpBufferStr(&sql, " NOCREATEDB");
6327 bruce 321 GIC 17 : if (createrole == TRI_YES)
3429 heikki.linnakangas 322 CBC 8 : appendPQExpBufferStr(&sql, " CREATEROLE");
6327 bruce 323 GIC 17 : if (createrole == TRI_NO)
3429 heikki.linnakangas 324 CBC 9 : appendPQExpBufferStr(&sql, " NOCREATEROLE");
6327 bruce 325 GIC 17 : if (inherit == TRI_YES)
3429 heikki.linnakangas 326 CBC 17 : appendPQExpBufferStr(&sql, " INHERIT");
6327 bruce 327 17 : if (inherit == TRI_NO)
3429 heikki.linnakangas 328 UIC 0 : appendPQExpBufferStr(&sql, " NOINHERIT");
6327 bruce 329 GIC 17 : if (login == TRI_YES)
3429 heikki.linnakangas 330 16 : appendPQExpBufferStr(&sql, " LOGIN");
6327 bruce 331 GBC 17 : if (login == TRI_NO)
3429 heikki.linnakangas 332 GIC 1 : appendPQExpBufferStr(&sql, " NOLOGIN");
4216 rhaas 333 GBC 17 : if (replication == TRI_YES)
3429 heikki.linnakangas 334 GIC 1 : appendPQExpBufferStr(&sql, " REPLICATION");
4216 rhaas 335 17 : if (replication == TRI_NO)
3429 heikki.linnakangas 336 16 : appendPQExpBufferStr(&sql, " NOREPLICATION");
270 michael 337 GNC 17 : if (bypassrls == TRI_YES)
338 1 : appendPQExpBufferStr(&sql, " BYPASSRLS");
339 17 : if (bypassrls == TRI_NO)
340 16 : appendPQExpBufferStr(&sql, " NOBYPASSRLS");
1154 alvherre 341 GBC 17 : if (conn_limit >= -1)
1154 alvherre 342 UBC 0 : appendPQExpBuffer(&sql, " CONNECTION LIMIT %d", conn_limit);
270 michael 343 GNC 17 : if (pwexpiry != NULL)
344 : {
345 1 : appendPQExpBufferStr(&sql, " VALID UNTIL ");
346 1 : appendStringLiteralConn(&sql, pwexpiry, conn);
347 : }
3406 rhaas 348 GIC 17 : if (roles.head != NULL)
3406 rhaas 349 EUB : {
350 : SimpleStringListCell *cell;
351 :
3406 rhaas 352 CBC 1 : appendPQExpBufferStr(&sql, " IN ROLE ");
3406 rhaas 353 ECB :
3406 rhaas 354 CBC 2 : for (cell = roles.head; cell; cell = cell->next)
3406 rhaas 355 ECB : {
3406 rhaas 356 CBC 1 : if (cell->next)
3406 rhaas 357 LBC 0 : appendPQExpBuffer(&sql, "%s,", fmtId(cell->val));
3406 rhaas 358 ECB : else
2063 peter_e 359 CBC 1 : appendPQExpBufferStr(&sql, fmtId(cell->val));
3406 rhaas 360 ECB : }
361 : }
270 michael 362 GNC 17 : if (members.head != NULL)
363 : {
364 : SimpleStringListCell *cell;
365 :
366 1 : appendPQExpBufferStr(&sql, " ROLE ");
367 :
368 3 : for (cell = members.head; cell; cell = cell->next)
369 : {
370 2 : if (cell->next)
371 1 : appendPQExpBuffer(&sql, "%s,", fmtId(cell->val));
372 : else
373 1 : appendPQExpBufferStr(&sql, fmtId(cell->val));
374 : }
375 : }
376 17 : if (admins.head != NULL)
377 : {
378 : SimpleStringListCell *cell;
379 :
380 1 : appendPQExpBufferStr(&sql, " ADMIN ");
381 :
382 3 : for (cell = admins.head; cell; cell = cell->next)
383 : {
384 2 : if (cell->next)
385 1 : appendPQExpBuffer(&sql, "%s,", fmtId(cell->val));
386 : else
387 1 : appendPQExpBufferStr(&sql, fmtId(cell->val));
388 : }
389 : }
390 :
2838 heikki.linnakangas 391 CBC 17 : appendPQExpBufferChar(&sql, ';');
7327 peter_e 392 ECB :
7327 peter_e 393 CBC 17 : if (echo)
3345 peter_e 394 LBC 0 : printf("%s\n", sql.data);
7327 peter_e 395 CBC 17 : result = PQexec(conn, sql.data);
7327 peter_e 396 EUB :
7327 peter_e 397 CBC 17 : if (PQresultStatus(result) != PGRES_COMMAND_OK)
7327 peter_e 398 ECB : {
1469 peter 399 CBC 1 : pg_log_error("creation of new role failed: %s", PQerrorMessage(conn));
7327 peter_e 400 1 : PQfinish(conn);
401 1 : exit(1);
7327 peter_e 402 ECB : }
403 :
6159 bruce 404 CBC 16 : PQclear(result);
7327 peter_e 405 16 : PQfinish(conn);
406 16 : exit(0);
7327 peter_e 407 ECB : }
408 :
409 :
7327 peter_e 410 EUB : static void
7327 peter_e 411 CBC 1 : help(const char *progname)
412 : {
6447 tgl 413 1 : printf(_("%s creates a new PostgreSQL role.\n\n"), progname);
7327 peter_e 414 1 : printf(_("Usage:\n"));
6400 peter_e 415 GIC 1 : printf(_(" %s [OPTION]... [ROLENAME]\n"), progname);
7327 peter_e 416 CBC 1 : printf(_("\nOptions:\n"));
270 michael 417 GNC 1 : printf(_(" -a, --admin=ROLE this role will be a member of new role with admin\n"
418 : " option\n"));
5156 peter_e 419 GIC 1 : printf(_(" -c, --connection-limit=N connection limit for role (default: no limit)\n"));
6447 tgl 420 1 : printf(_(" -d, --createdb role can create new databases\n"));
4079 peter_e 421 1 : printf(_(" -D, --no-createdb role cannot create databases (default)\n"));
5156 peter_e 422 CBC 1 : printf(_(" -e, --echo show the commands being sent to the server\n"));
3406 rhaas 423 GIC 1 : printf(_(" -g, --role=ROLE new role will be a member of this role\n"));
6400 peter_e 424 CBC 1 : printf(_(" -i, --inherit role inherits privileges of roles it is a\n"
425 : " member of (default)\n"));
426 1 : printf(_(" -I, --no-inherit role does not inherit privileges\n"));
5156 peter_e 427 GBC 1 : printf(_(" -l, --login role can login (default)\n"));
5156 peter_e 428 GIC 1 : printf(_(" -L, --no-login role cannot login\n"));
270 michael 429 GNC 1 : printf(_(" -m, --member=ROLE this role will be a member of new role\n"));
5156 peter_e 430 CBC 1 : printf(_(" -P, --pwprompt assign a password to new role\n"));
5156 peter_e 431 GIC 1 : printf(_(" -r, --createrole role can create new roles\n"));
4079 432 1 : printf(_(" -R, --no-createrole role cannot create roles (default)\n"));
5156 peter_e 433 CBC 1 : printf(_(" -s, --superuser role will be superuser\n"));
4079 peter_e 434 GIC 1 : printf(_(" -S, --no-superuser role will not be superuser (default)\n"));
269 michael 435 GNC 1 : printf(_(" -v, --valid-until=TIMESTAMP\n"
436 : " password expiration date for role\n"));
3947 peter_e 437 GIC 1 : printf(_(" -V, --version output version information, then exit\n"));
4079 438 1 : printf(_(" --interactive prompt for missing role name and attributes rather\n"
4079 peter_e 439 ECB : " than using defaults\n"));
270 michael 440 GNC 1 : printf(_(" --bypassrls role can bypass row-level security (RLS) policy\n"));
34 dgustafsson 441 1 : printf(_(" --no-bypassrls role cannot bypass row-level security (RLS) policy\n"
442 : " (default)\n"));
4216 rhaas 443 GIC 1 : printf(_(" --replication role can initiate replication\n"));
34 dgustafsson 444 GNC 1 : printf(_(" --no-replication role cannot initiate replication (default)\n"));
3947 peter_e 445 GIC 1 : printf(_(" -?, --help show this help, then exit\n"));
7327 peter_e 446 CBC 1 : printf(_("\nConnection options:\n"));
7242 bruce 447 1 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
7327 peter_e 448 GIC 1 : printf(_(" -p, --port=PORT database server port\n"));
7327 peter_e 449 CBC 1 : printf(_(" -U, --username=USERNAME user name to connect as (not the one to create)\n"));
5155 peter_e 450 GIC 1 : printf(_(" -w, --no-password never prompt for password\n"));
5598 tgl 451 1 : printf(_(" -W, --password force password prompt\n"));
1136 peter 452 CBC 1 : printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1136 peter 453 GIC 1 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
7327 peter_e 454 1 : }
|