| 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 | |