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