def main(args, opts): jdb.reset_encoding(sys.stdout, 'utf-8') errs = [] chklist = {} try: form, svc, dbg, cur, sid, sess, parms, cfg = jmcgi.parseform() except Exception as e: jmcgi.err_page([str(e)]) fv = form.getfirst fl = form.getlist if not sess or sess.priv != 'A': users = [] else: sql = "SELECT * FROM users ORDER BY userid" sesscur = jdb.dbOpenSvc(cfg, svc, session=True, nokw=True) users = jdb.dbread(sesscur, sql) L('cgi.users').debug('read %d rows from table "user"' % (len(users), )) jmcgi.jinja_page("users.jinja", users=users, session=sess, cfg=cfg, parms=parms, svc=svc, dbg=dbg, sid=sid, this_page='user.py', result=fv('result'))
def get_user(uid, svc, cfg): cur = jdb.dbOpenSvc(cfg, svc, session=True, nokw=True) sql = "SELECT * FROM users WHERE userid=%s" users = jdb.dbread(cur, sql, (uid, )) # 'userid' is primary key of users table so we should never # receive more than one row. assert len(users) <= 1, "Too many rows received" return users[0] if users else None
def main(args, opts): jdb.reset_encoding(sys.stdout, 'utf-8') errs = [] so = None stats = {} try: form, svc, dbg, cur, sid, sess, parms, cfg = jmcgi.parseform() except Exception as e: jmcgi.err_page([str(e)]) fv = form.getfirst fl = form.getlist # The form values "userid", "fullname", "email", "priv", # "disabled", come from editable fields in the user.py # page. # The values "new" (i.e. create) and "delete" are also user # boolean input from user.py indicating what the user wants # to do. Neither of them set indicates an update action on # an existing account. Both of them set is an error. # The form value "subjid" identifies the user whose data was # originally loaded into the user.py form and is the user any # update or delete will be performed on. If it differs from # sess.userid it indcates the action is to e done on the # account of someone other than the logged user themself # and is prohibited unless the logged in user has "admin" # privilege. For a new (create) action, "subjid" is ignored # and the new user is created with the id given in "userid". # Set 'action' to "n" (new), "u" (update), "d" (delete) or # "x" (invalid) according to the values of fv('new') and # fv('delete') that were received from as url parameters # from the User form. action = {(0,0):'u', (0,1):'d', (1,0):'n', (1,1):'x'}\ [(bool(fv('new')),bool(fv('delete')))] L('cgi.userupd').debug( "new=%r, delete=%r, action=%r, subjid=%r, userid=%r" % (fv('new'), fv('delete'), action, fv('subjid'), fv('userid'))) # NOTE: The jmcgi.err_page() calls below do not return, # jmcgi.err_page() calls sys.exit(). if not sess: jmcgi.err_page([], prolog="<p>You must login before you can change your " "user settings.</p>") if fv('subjid') != sess.userid and sess.priv != 'A': jmcgi.err_page( [], prolog="<p>You do not have sufficient privilege to alter " "settings for anyone other than yourself.</p>") if action in ('nd') and sess.priv != 'A': jmcgi.err_page( [], prolog="<p>You do not have sufficient privilege to create " "or delete users.</p>") if action == 'x': jmcgi.err_page( [], prolog="<p>\"New user\" and \"Delete\" are incompatible.</p>") errors = [] # Get the id of the user we will be updating. If creating a # new user, 'subjid' should not exist and thus 'subj' will be # None which has the beneficial effect of causing gen_sql_- # params() to generate change parameters for every form value # which is what we want when creating a user. subj = jmcgi.get_user(fv('subjid'), svc, cfg) if action in 'nu': # "new" or "update" action... if action == 'u': L('cgi.userupd').debug("update user %r" % sanitize_o(subj)) else: L('cgi.userupd').debug("create user %r" % fv('userid')) if action == 'n' and \ (subj or fv('userid')==sess.userid or jmcgi.get_user(fv('userid'), svc, cfg)): # This is the creation of a new user (fv('userid')). # The userid must not already exist. The tests for # subj and sess.userid are simply to avoid an expensive # get_user() call when we already know the user exists. errors.append("Account name %s is already in use." % fv('userid')) if action == 'u' and fv('userid')!=subj.userid \ and (fv('userid')==sess.userid \ or jmcgi.get_user(fv('userid'), svc, cfg)): # This is an update of an existing user. # If the new userid (fv('userid')) is the same as the # subj.userid it's not being changed and is ok. If # different then it must not be the same as an exiting # userid. The test for sess.userid is simply to avoid # an expensive get_user() call when we already know # that user exists. errors.append("Account name %s is already in use." % fv('userid')) # Get the parameters we'll need for the sql statement used # to update the user/sessions database. collist, values, err \ = gen_sql_params (sess.priv=='A', subj, fv('pw1'), fv('pw2'), fv('userid'), fv('fullname'), fv('email'), fv('priv'), fv('disabled')) errors.extend(err) L('cgi.userupd').debug("collist: %r" % collist) L('cgi.userupd').debug("values: %r" % sanitize_v(values, collist)) else: # "delete" action... # We ignore changes made to the form fields since we # are going to delete the user, they are irrelevant. # Except for one: the "userid" field. If that was # changed we treat it as an error due to the risk that # the user thought the changed userid will be deleted # which is not what will happen (we delete the "subjid" # user.) values = [] if fv('userid') != fv('subjid'): errors.append("Can't delete user when userid has been changed.") if not subj: errors.append("User '%s' doesn't exist." % fv('subjid')) if errors: jmcgi.err_page( errs=errors, prolog="The following errors were found in the changes " "you requested. Please use your browser's Back button " "to return to the user page, correct them, and resubmit " "your changes.") update_session = None result = None summary = 'not available' if action == 'n': # Create new user... cols = ','.join(c for c, p in collist) pmarks = ','.join(p for c, p in collist) sql = "INSERT INTO users(%s) VALUES (%s)" % (cols, pmarks) values_sani = sanitize_v(values, collist) summary = "added user \"%s\"" \ % ''.join(p for c,p in collist if c=='userid') elif action == 'd': # Delete existing user... sql = "DELETE FROM users WHERE userid=%s" values.append(fv('subjid')) values_sani = values summary = "deleted user \"%s\"" % fv('subjid') else: # Update existing user... if not collist: result = 'nochange' else: if subj and subj.userid == sess.userid \ and fv('userid') and fv('userid'): update_session = fv('userid') updclause = ','.join(("%s=%s" % (c, p)) for c, p in collist) sql = "UPDATE users SET %s WHERE userid=%%s" % updclause values.append(fv('subjid')) values_sani = sanitize_v(values, collist) summary = "updated user %s (%s)" \ % (fv('subjid'), ','.join([c for c,p in collist])) if result != 'nochange': sesscur = jdb.dbOpenSvc(cfg, svc, session=True, nokw=True) L('cgi.userupd.db').debug("sql: %r" % sql) # 'values_sani' should be the same as values but with any # password text masked out. L('cgi.userupd.db').debug("args: %r" % values_sani) sesscur.execute(sql, values) sesscur.connection.commit() #FIXME: trap db errors and try to figure out what went # wrong in terms that a user can remediate. if update_session: L('cgi.userupd').debug("update sess.userid: %r->%r" % (sess.userid, update_session)) sess.userid = update_session L('cgi.userupd').info(summary) result = 'success' # If the user is not an admin we send them back to their # settings page (using 'userid' since it they may have changed # it.) For admin users it's more complcatd because they might # have deleted the user. Easiest for us to send him/her back # to the user list page which we know always exists. if sess.priv == 'A': return_to = 'users.py?' else: return_to = 'user.py?u=%s' % fv('userid') jmcgi.redirect(urlbase() + "%s&svc=%s&sid=%s&dbg=%s&result=%s" % (return_to, svc, sid, dbg, result))
def parseform(readonly=False): """\ Do some routine tasks that are needed for (most) every page, specifically: * Call cgi.FieldStorage to parse parameters. * Extract the svc parameter, validate it, and open the requested database. * Get session id and handle log or logout requests. Return an 8-tuple of: form (cgi.FieldStorage obj) svc (string) -- Checked svc value. dbg (int) -- true if "dbg" url param has true value, else 0. cur (dbapi cursor) -- Open cursor for database defined by 'svc'. sid (string) -- session.id in hexidecimal form or "". sess (Session inst.) -- Session for sid or None. params (dict) -- Received and decoded form parameters. cfg (Config inst.) -- Config object from reading config.ini. """ cfg = initcgi("config.ini") # Read config, initialize logging. L('cgi.jmcgi').debug("parseform: called in %s" % sys.modules['__main__'].__file__) errs = [] sess = None sid = '' cur = None svc = None def_svc = cfg['web'].get('DEFAULT_SVC', 'jmdict') if def_svc.startswith('db_'): def_svc = def_svc[3:] check_server_status(cfg.get('web', 'STATUS_DIR')) form = cgi.FieldStorage() L('cgi.jmcgi').debug("query_string: %s" % os.environ.get('QUERY_STRING')) for k in form.keys(): v = ['***']*len(form.getlist(k)) if k in ('password','pw1','pw2') \ else form.getlist(k) L('cgi.jmcgi').debug("parseform: form key %s=%r" % (k, v)) dbg = int(form.getfirst('dbg') or '0') svc = form.getfirst('svc') or def_svc #L('cgi.jmcgi').debug("parseform: svc=%s" % svc) usid = form.getfirst('sid') or '' # No SID is "", not None. try: svc = safe(svc) except ValueError: errs.append('svc=' + svc) if not errs: try: cur = jdb.dbOpenSvc(cfg, svc) except KeyError as e: errs.append('svc=' + str(e)) if errs: raise ValueError(';'.join(errs)) # Login, logout, and session identification... scur = jdb.dbOpenSvc(cfg, svc, session=True, nokw=True) action = form.getfirst('loginout') # Will be None, "login" or "logout" sid = get_sid_from_cookie() or '' sid_from_cookie = bool(sid) if usid: sid = usid # Use sid from url if available. L('cgi.jmcgi').debug("parseform: sid=%s, from_cookie=%s, action=%s" % (sid, sid_from_cookie, action)) uname = form.getfirst('username') or '' pw = form.getfirst('password') or '' sid, sess = get_session(scur, action, sid, uname, pw) L('cgi.jmcgi').debug("parseform: %s session, sid=%s" % ("got" if sess else "no", sid)) if sid: set_sid_cookie(sid, delete=(action == "logout")) if sid_from_cookie: sid = '' scur.connection.close() # Collect the form parameters. Caller is expected to pass # them to the page template which will use them in the login # section as hidden parms so the page can be recreated after # a login. We leave out any param name "result" since that # is used as a confirmation message by the userupd.py page # and best to see it only once. parms = [(k, v) for k in list(form.keys()) if k not in ('loginout', 'username', 'password', 'result') for v in form.getlist(k)] return form, svc, dbg, cur, sid, sess, parms, cfg