1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * createuser |
4 | * |
5 | * Portions Copyright (c) 1996-2019, 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 | #include "common.h" |
15 | #include "common/logging.h" |
16 | #include "fe_utils/simple_list.h" |
17 | #include "fe_utils/string_utils.h" |
18 | |
19 | |
20 | static void help(const char *progname); |
21 | |
22 | int |
23 | main(int argc, char *argv[]) |
24 | { |
25 | static struct option long_options[] = { |
26 | {"host" , required_argument, NULL, 'h'}, |
27 | {"port" , required_argument, NULL, 'p'}, |
28 | {"username" , required_argument, NULL, 'U'}, |
29 | {"role" , required_argument, NULL, 'g'}, |
30 | {"no-password" , no_argument, NULL, 'w'}, |
31 | {"password" , no_argument, NULL, 'W'}, |
32 | {"echo" , no_argument, NULL, 'e'}, |
33 | {"createdb" , no_argument, NULL, 'd'}, |
34 | {"no-createdb" , no_argument, NULL, 'D'}, |
35 | {"superuser" , no_argument, NULL, 's'}, |
36 | {"no-superuser" , no_argument, NULL, 'S'}, |
37 | {"createrole" , no_argument, NULL, 'r'}, |
38 | {"no-createrole" , no_argument, NULL, 'R'}, |
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 | {"replication" , no_argument, NULL, 1}, |
44 | {"no-replication" , no_argument, NULL, 2}, |
45 | {"interactive" , no_argument, NULL, 3}, |
46 | /* adduser is obsolete, undocumented spelling of superuser */ |
47 | {"adduser" , no_argument, NULL, 'a'}, |
48 | {"no-adduser" , no_argument, NULL, 'A'}, |
49 | {"connection-limit" , required_argument, NULL, 'c'}, |
50 | {"pwprompt" , no_argument, NULL, 'P'}, |
51 | {"encrypted" , no_argument, NULL, 'E'}, |
52 | {NULL, 0, NULL, 0} |
53 | }; |
54 | |
55 | const char *progname; |
56 | int optindex; |
57 | int c; |
58 | const char *newuser = NULL; |
59 | char *host = NULL; |
60 | char *port = NULL; |
61 | char *username = NULL; |
62 | SimpleStringList roles = {NULL, NULL}; |
63 | enum trivalue prompt_password = TRI_DEFAULT; |
64 | bool echo = false; |
65 | bool interactive = false; |
66 | char *conn_limit = NULL; |
67 | bool pwprompt = false; |
68 | char *newpassword = NULL; |
69 | char newuser_buf[128]; |
70 | char newpassword_buf[100]; |
71 | |
72 | /* Tri-valued variables. */ |
73 | enum trivalue createdb = TRI_DEFAULT, |
74 | superuser = TRI_DEFAULT, |
75 | createrole = TRI_DEFAULT, |
76 | inherit = TRI_DEFAULT, |
77 | login = TRI_DEFAULT, |
78 | replication = TRI_DEFAULT; |
79 | |
80 | PQExpBufferData sql; |
81 | |
82 | PGconn *conn; |
83 | PGresult *result; |
84 | |
85 | pg_logging_init(argv[0]); |
86 | progname = get_progname(argv[0]); |
87 | set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts" )); |
88 | |
89 | handle_help_version_opts(argc, argv, "createuser" , help); |
90 | |
91 | while ((c = getopt_long(argc, argv, "h:p:U:g:wWedDsSaArRiIlLc:PE" , |
92 | long_options, &optindex)) != -1) |
93 | { |
94 | switch (c) |
95 | { |
96 | case 'h': |
97 | host = pg_strdup(optarg); |
98 | break; |
99 | case 'p': |
100 | port = pg_strdup(optarg); |
101 | break; |
102 | case 'U': |
103 | username = pg_strdup(optarg); |
104 | break; |
105 | case 'g': |
106 | simple_string_list_append(&roles, optarg); |
107 | break; |
108 | case 'w': |
109 | prompt_password = TRI_NO; |
110 | break; |
111 | case 'W': |
112 | prompt_password = TRI_YES; |
113 | break; |
114 | case 'e': |
115 | echo = true; |
116 | break; |
117 | case 'd': |
118 | createdb = TRI_YES; |
119 | break; |
120 | case 'D': |
121 | createdb = TRI_NO; |
122 | break; |
123 | case 's': |
124 | case 'a': |
125 | superuser = TRI_YES; |
126 | break; |
127 | case 'S': |
128 | case 'A': |
129 | superuser = TRI_NO; |
130 | break; |
131 | case 'r': |
132 | createrole = TRI_YES; |
133 | break; |
134 | case 'R': |
135 | createrole = TRI_NO; |
136 | break; |
137 | case 'i': |
138 | inherit = TRI_YES; |
139 | break; |
140 | case 'I': |
141 | inherit = TRI_NO; |
142 | break; |
143 | case 'l': |
144 | login = TRI_YES; |
145 | break; |
146 | case 'L': |
147 | login = TRI_NO; |
148 | break; |
149 | case 'c': |
150 | conn_limit = pg_strdup(optarg); |
151 | break; |
152 | case 'P': |
153 | pwprompt = true; |
154 | break; |
155 | case 'E': |
156 | /* no-op, accepted for backward compatibility */ |
157 | break; |
158 | case 1: |
159 | replication = TRI_YES; |
160 | break; |
161 | case 2: |
162 | replication = TRI_NO; |
163 | break; |
164 | case 3: |
165 | interactive = true; |
166 | break; |
167 | default: |
168 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), progname); |
169 | exit(1); |
170 | } |
171 | } |
172 | |
173 | switch (argc - optind) |
174 | { |
175 | case 0: |
176 | break; |
177 | case 1: |
178 | newuser = argv[optind]; |
179 | break; |
180 | default: |
181 | pg_log_error("too many command-line arguments (first is \"%s\")" , |
182 | argv[optind + 1]); |
183 | fprintf(stderr, _("Try \"%s --help\" for more information.\n" ), progname); |
184 | exit(1); |
185 | } |
186 | |
187 | if (newuser == NULL) |
188 | { |
189 | if (interactive) |
190 | { |
191 | simple_prompt("Enter name of role to add: " , |
192 | newuser_buf, sizeof(newuser_buf), true); |
193 | newuser = newuser_buf; |
194 | } |
195 | else |
196 | { |
197 | if (getenv("PGUSER" )) |
198 | newuser = getenv("PGUSER" ); |
199 | else |
200 | newuser = get_user_name_or_exit(progname); |
201 | } |
202 | } |
203 | |
204 | if (pwprompt) |
205 | { |
206 | char pw2[100]; |
207 | |
208 | simple_prompt("Enter password for new role: " , |
209 | newpassword_buf, sizeof(newpassword_buf), false); |
210 | simple_prompt("Enter it again: " , pw2, sizeof(pw2), false); |
211 | if (strcmp(newpassword_buf, pw2) != 0) |
212 | { |
213 | fprintf(stderr, _("Passwords didn't match.\n" )); |
214 | exit(1); |
215 | } |
216 | newpassword = newpassword_buf; |
217 | } |
218 | |
219 | if (superuser == 0) |
220 | { |
221 | if (interactive && yesno_prompt("Shall the new role be a superuser?" )) |
222 | superuser = TRI_YES; |
223 | else |
224 | superuser = TRI_NO; |
225 | } |
226 | |
227 | if (superuser == TRI_YES) |
228 | { |
229 | /* Not much point in trying to restrict a superuser */ |
230 | createdb = TRI_YES; |
231 | createrole = TRI_YES; |
232 | } |
233 | |
234 | if (createdb == 0) |
235 | { |
236 | if (interactive && yesno_prompt("Shall the new role be allowed to create databases?" )) |
237 | createdb = TRI_YES; |
238 | else |
239 | createdb = TRI_NO; |
240 | } |
241 | |
242 | if (createrole == 0) |
243 | { |
244 | if (interactive && yesno_prompt("Shall the new role be allowed to create more new roles?" )) |
245 | createrole = TRI_YES; |
246 | else |
247 | createrole = TRI_NO; |
248 | } |
249 | |
250 | if (inherit == 0) |
251 | inherit = TRI_YES; |
252 | |
253 | if (login == 0) |
254 | login = TRI_YES; |
255 | |
256 | conn = connectDatabase("postgres" , host, port, username, prompt_password, |
257 | progname, echo, false, false); |
258 | |
259 | initPQExpBuffer(&sql); |
260 | |
261 | printfPQExpBuffer(&sql, "CREATE ROLE %s" , fmtId(newuser)); |
262 | if (newpassword) |
263 | { |
264 | char *encrypted_password; |
265 | |
266 | appendPQExpBufferStr(&sql, " PASSWORD " ); |
267 | |
268 | encrypted_password = PQencryptPasswordConn(conn, |
269 | newpassword, |
270 | newuser, |
271 | NULL); |
272 | if (!encrypted_password) |
273 | { |
274 | pg_log_error("password encryption failed: %s" , |
275 | PQerrorMessage(conn)); |
276 | exit(1); |
277 | } |
278 | appendStringLiteralConn(&sql, encrypted_password, conn); |
279 | PQfreemem(encrypted_password); |
280 | } |
281 | if (superuser == TRI_YES) |
282 | appendPQExpBufferStr(&sql, " SUPERUSER" ); |
283 | if (superuser == TRI_NO) |
284 | appendPQExpBufferStr(&sql, " NOSUPERUSER" ); |
285 | if (createdb == TRI_YES) |
286 | appendPQExpBufferStr(&sql, " CREATEDB" ); |
287 | if (createdb == TRI_NO) |
288 | appendPQExpBufferStr(&sql, " NOCREATEDB" ); |
289 | if (createrole == TRI_YES) |
290 | appendPQExpBufferStr(&sql, " CREATEROLE" ); |
291 | if (createrole == TRI_NO) |
292 | appendPQExpBufferStr(&sql, " NOCREATEROLE" ); |
293 | if (inherit == TRI_YES) |
294 | appendPQExpBufferStr(&sql, " INHERIT" ); |
295 | if (inherit == TRI_NO) |
296 | appendPQExpBufferStr(&sql, " NOINHERIT" ); |
297 | if (login == TRI_YES) |
298 | appendPQExpBufferStr(&sql, " LOGIN" ); |
299 | if (login == TRI_NO) |
300 | appendPQExpBufferStr(&sql, " NOLOGIN" ); |
301 | if (replication == TRI_YES) |
302 | appendPQExpBufferStr(&sql, " REPLICATION" ); |
303 | if (replication == TRI_NO) |
304 | appendPQExpBufferStr(&sql, " NOREPLICATION" ); |
305 | if (conn_limit != NULL) |
306 | appendPQExpBuffer(&sql, " CONNECTION LIMIT %s" , conn_limit); |
307 | if (roles.head != NULL) |
308 | { |
309 | SimpleStringListCell *cell; |
310 | |
311 | appendPQExpBufferStr(&sql, " IN ROLE " ); |
312 | |
313 | for (cell = roles.head; cell; cell = cell->next) |
314 | { |
315 | if (cell->next) |
316 | appendPQExpBuffer(&sql, "%s," , fmtId(cell->val)); |
317 | else |
318 | appendPQExpBufferStr(&sql, fmtId(cell->val)); |
319 | } |
320 | } |
321 | appendPQExpBufferChar(&sql, ';'); |
322 | |
323 | if (echo) |
324 | printf("%s\n" , sql.data); |
325 | result = PQexec(conn, sql.data); |
326 | |
327 | if (PQresultStatus(result) != PGRES_COMMAND_OK) |
328 | { |
329 | pg_log_error("creation of new role failed: %s" , PQerrorMessage(conn)); |
330 | PQfinish(conn); |
331 | exit(1); |
332 | } |
333 | |
334 | PQclear(result); |
335 | PQfinish(conn); |
336 | exit(0); |
337 | } |
338 | |
339 | |
340 | static void |
341 | help(const char *progname) |
342 | { |
343 | printf(_("%s creates a new PostgreSQL role.\n\n" ), progname); |
344 | printf(_("Usage:\n" )); |
345 | printf(_(" %s [OPTION]... [ROLENAME]\n" ), progname); |
346 | printf(_("\nOptions:\n" )); |
347 | printf(_(" -c, --connection-limit=N connection limit for role (default: no limit)\n" )); |
348 | printf(_(" -d, --createdb role can create new databases\n" )); |
349 | printf(_(" -D, --no-createdb role cannot create databases (default)\n" )); |
350 | printf(_(" -e, --echo show the commands being sent to the server\n" )); |
351 | printf(_(" -g, --role=ROLE new role will be a member of this role\n" )); |
352 | printf(_(" -i, --inherit role inherits privileges of roles it is a\n" |
353 | " member of (default)\n" )); |
354 | printf(_(" -I, --no-inherit role does not inherit privileges\n" )); |
355 | printf(_(" -l, --login role can login (default)\n" )); |
356 | printf(_(" -L, --no-login role cannot login\n" )); |
357 | printf(_(" -P, --pwprompt assign a password to new role\n" )); |
358 | printf(_(" -r, --createrole role can create new roles\n" )); |
359 | printf(_(" -R, --no-createrole role cannot create roles (default)\n" )); |
360 | printf(_(" -s, --superuser role will be superuser\n" )); |
361 | printf(_(" -S, --no-superuser role will not be superuser (default)\n" )); |
362 | printf(_(" -V, --version output version information, then exit\n" )); |
363 | printf(_(" --interactive prompt for missing role name and attributes rather\n" |
364 | " than using defaults\n" )); |
365 | printf(_(" --replication role can initiate replication\n" )); |
366 | printf(_(" --no-replication role cannot initiate replication\n" )); |
367 | printf(_(" -?, --help show this help, then exit\n" )); |
368 | printf(_("\nConnection options:\n" )); |
369 | printf(_(" -h, --host=HOSTNAME database server host or socket directory\n" )); |
370 | printf(_(" -p, --port=PORT database server port\n" )); |
371 | printf(_(" -U, --username=USERNAME user name to connect as (not the one to create)\n" )); |
372 | printf(_(" -w, --no-password never prompt for password\n" )); |
373 | printf(_(" -W, --password force password prompt\n" )); |
374 | printf(_("\nReport bugs to <pgsql-bugs@lists.postgresql.org>.\n" )); |
375 | } |
376 | |