def request_vhost(locker, hostname, path, user=None, desc=''): """Request hostname as a vhost for the given locker and path. Throws a UserError if the request is invalid, otherwise returns a human-readable status message and sends a zephyr.""" locker = locker.encode('utf-8') hostname, reqtype = validate_hostname(hostname, locker) path = path.encode('utf-8') path = validate_path(path) message = "The hostname '%s' is now configured." % hostname if user is None: user = current_user() if reqtype == 'moira': # actually_create_vhost does this check for other reqtypes check_if_already_exists(hostname, locker) t = queue.Ticket.create(locker, hostname, path, requestor=user, purpose=desc) short = hostname[:-len('.mit.edu')] mail.create_ticket(subject="scripts-vhosts CNAME request: %s" % short, body="""Heyas, %(user)s requested %(hostname)s for locker '%(locker)s' path %(path)s. Go to %(url)s to approve it. Purpose: %(desc)s Love, ~Scripts Pony """ % dict(short=short, user=user, locker=locker, hostname=hostname, desc=desc, path=path, url=tg.request.host_url + tg.url('/ticket/%s' % t.id)), id=t.id, requestor=user) message = "We will request the hostname %s; mit.edu hostnames generally take 2-3 business days to become active." % hostname else: # Actually create the vhost actually_create_vhost(locker, hostname, path) if is_sudoing(): sudobit = '+scripts-pony-acl' forbit = ' (for %s)' % user else: sudobit = '' forbit = '' logmessage = "%s%s requested %s for locker '%s' path '%s'%s (Purpose: %s)" % ( current_user(), sudobit, hostname, locker, path, forbit, desc) log.info(logmessage) return message
def addEvent(self, type, state, by=None, target=None, subject=None, body=None): if by is None: by = auth.current_user() event = Event(ticket=self, type=type, target=target, subject=subject, body=body, by=by) DBSession.add(event) if state != self.state: self.state = state pat = "%s's %s changed the ticket re: %s to %s" else: pat = "%s's %s left the ticket re: %s as %s" try: url = "%s%s" % (tg.request.host_url, tg.url("/queue")) except: # Default to something sane if we're not in the context of a request url = "https://pony.scripts.mit.edu:444/queue" log.zwrite(pat % (by, type, self.hostname, state), instance=self.id, zsig=url)
def reject(self, id, subject=None, body=None, token=None, silent=False): t = queue.Ticket.get(int(id)) if t.state != 'open': flash("This ticket's not open!") redirect('/ticket/%s' % id) if t.rtid is None: flash("This ticket has no RT ID!") redirect('/ticket/%s' % id) if (subject and body) or silent: if token != auth.token(): flash("Invalid token!") else: # Send mail and records it as an event if not silent: mail.send_correspondence(subject, body, t.rtid, auth.current_user()) t.addEvent(type=u'mail', state=u'rejected', target=u'user', subject=subject, body=body) flash("Ticket rejected; mail sent to user.") else: mail.send_comment(subject, "Ticket rejected silently.\n\n" + body, t.id, t.rtid, auth.current_user()) t.addEvent(type=u'mail', state=u'rejected', target=u'rt', subject=subject, body=body) flash("Ticket rejected silently.") redirect('/queue') return dict(tickets=[t], action=url('/reject/%s' % id), subject="Re: Request for hostname %s" % t.hostname, body="""Hello, Unfortunately, the hostname %(hostname)s is not available. You can go to http://pony.scripts.mit.edu/ to request a different one. Sorry for the inconvenience, -%(first)s /set status=rejected """ % dict(hostname=t.hostname, first=auth.first_name()), submit='Send to %s' % t.requestor, extra_buttons={'silent': 'Send as Comment'})
def new(self, locker, hostname="", path="", desc="", token=None, confirmed=False, personal_ok=False, requestor=None, **kwargs): personal = locker == auth.current_user() if confirmed: auth.scripts_team_sudo() else: requestor = None if request.response_ext: locker += request.response_ext if hostname: if token != auth.token(): flash("Invalid token!") elif not desc and not confirmed: flash("Please specify the purpose of this hostname.") elif requestor is not None and not requestor.strip(): flash("Please specify requestor.") elif personal and not personal_ok: flash( "Please acknowledge that your hostname will be served from your personal locker and will be deleted when you leave MIT." ) else: try: status = vhosts.request_vhost(locker, hostname, path, user=requestor, desc=desc) except vhosts.UserError as e: flash(e.message) else: flash(status) if confirmed: redirect("/queue") else: redirect("/index/" + locker) else: try: auth.validate_locker(locker, sudo_ok=True) except auth.AuthError as e: flash(e.message) redirect("/") return dict( locker=locker, hostname=hostname, path=path, desc=desc, confirmed=confirmed, personal=personal, )
def delete(locker, vhost): """Deletes an existing vhost owned by the locker.""" if vhost.lower().endswith(".mit.edu") and not vhost.lower().endswith( "." + locker + ".scripts.mit.edu" ): raise UserError("You cannot delete " + vhost + "!") locker = locker.encode("utf-8") scriptsVhostName = get_vhost_name(locker, vhost) conn.delete_s(scriptsVhostName) log.info("%s deleted vhost '%s' (locker '%s')." % (current_user(), vhost, locker))
def create(locker, hostname, path, requestor=None, purpose=""): if requestor is None: requestor = auth.current_user() t = Ticket(requestor=requestor, hostname=hostname, locker=locker, path=path, state="open", purpose=purpose) session.flush() t.addEvent(type='request', state="open", target='us') return t
def index(self, locker=None, sudo=False): """Handle the front-page.""" if locker is not None and request.response_ext: locker += request.response_ext olocker = locker hosts = None user = auth.current_user() https = auth.is_https() # Find or create the associated user info object. # TODO: is there a find_or_create sqlalchemy method? if user: if sudo and auth.on_scripts_team(): # override_template(self.index, 'mako:scripts.templates.confirm') # return dict(action=url('/new/'+locker),title="Really use Scripts Team bits to request a hostname as locker '%s'?"%locker,question="Only do this in response to a user support request, and after checking to make sure that the request comes from someone authorized to make requests for the locker.", # backurl=url('/index')) redirect("/new/%s?confirmed=true" % locker) try: user_info = ( DBSession.query(UserInfo).filter(UserInfo.user == user).one() ) except NoResultFound: user_info = UserInfo(user) DBSession.add(user_info) else: user_info = None if user is not None: if locker is None: locker = user try: hosts = vhosts.list_vhosts(locker) hosts.sort(key=lambda k: k[0]) except auth.AuthError as e: flash(e.message) # User has been deauthorized from this locker if locker in user_info.lockers: user_info.lockers.remove(locker) DBSession.add(user_info) if olocker is not None: return self.index() else: return dict(hosts={}, locker=locker, user_info=user_info) else: # Append locker to the list in user_info if it's not there if not locker in user_info.lockers: user_info.lockers.append(locker) user_info.lockers.sort() DBSession.add(user_info) flash('You can administer the "%s" locker.' % locker) return dict(hosts=hosts, locker=locker, user_info=user_info, https=https)
def set_path(locker, vhost, path): """Sets the path of an existing vhost owned by the locker.""" path = validate_path(path) if vhost == locker + '.scripts.mit.edu': raise UserError("You cannot reconfigure " + vhost + "!") path = path.encode('utf-8') locker = locker.encode('utf-8') scriptsVhostName = get_vhost_name(locker, vhost) conn.modify_s(scriptsVhostName, [(ldap.MOD_REPLACE, 'scriptsVhostDirectory', [path])]) log.info("%s set path for vhost '%s' (locker '%s') to '%s'." % (current_user(), vhost, locker, path))
def approve(self, id, subject=None, body=None, token=None, silent=False): t = queue.Ticket.get(int(id)) if t.state != 'open': flash("This ticket's not open!") redirect('/ticket/%s' % id) if t.rtid is None: flash("This ticket has no RT ID!") redirect('/ticket/%s' % id) if subject and body: if token != auth.token(): flash("Invalid token!") else: try: vhosts.actually_create_vhost(t.locker, t.hostname, t.path) except vhosts.UserError, e: flash(e.message) else: if not silent: # Send mail and records it as an event mail.send_comment(subject, body, t.id, t.rtid, auth.current_user(), 'accounts-internal') t.addEvent(type='mail', state='moira', target='accounts-internal', subject=subject, body=body) flash( "Ticket approved; mail sent to accounts-internal.") else: mail.send_comment( subject, "Ticket approved silently.\n\n" + body, t.id, t.rtid, auth.current_user()) t.addEvent(type='mail', state='dns', target='us') flash("Ticket approved silently.") redirect('/queue')
def new(self, locker, hostname='', path='', desc='', token=None, confirmed=False, personal_ok=False, requestor=None): personal = locker == auth.current_user() if confirmed: auth.scripts_team_sudo() else: requestor = None if pylons.request.response_ext: locker += pylons.request.response_ext if hostname: if token != auth.token(): flash("Invalid token!") elif not desc and not confirmed: flash("Please specify the purpose of this hostname.") elif requestor is not None and not requestor.strip(): flash("Please specify requestor.") elif personal and not personal_ok: flash( "Please acknowledge that your hostname will be served from your personal locker and will be deleted when you leave MIT." ) else: try: status = vhosts.request_vhost(locker, hostname, path, user=requestor, desc=desc) except vhosts.UserError, e: flash(e.message) else: flash(status) if confirmed: redirect('/queue') else: redirect('/index/' + locker)
def add_alias(locker, hostname, alias): locker = locker.encode('utf-8') hostname = hostname.lower().encode('utf-8') if hostname.endswith('.mit.edu'): raise UserError("You can't add aliases to .mit.edu hostnames!") if alias.lower().encode('utf-8').endswith('.mit.edu'): raise UserError( "You can't add .mit.edu aliases to non-.mit.edu hostnames!") alias, reqtype = validate_hostname(alias, locker) if reqtype != 'external': raise RuntimeError( "We didn't catch that something wasn't a .mit.edu hostname.") check_if_already_exists(alias, locker) # If we got here, we're good scriptsVhostName = get_vhost_name(locker, hostname) conn.modify_s(scriptsVhostName, [(ldap.MOD_ADD, 'scriptsVhostAlias', [alias])]) log.info("%s added alias '%s' to '%s' (locker '%s')." % (current_user(), alias, hostname, locker))
def set_pool(locker, vhost, pool): """Sets the pool of an existing vhost owned by the locker.""" locker = locker.encode("utf-8") pool = pool.encode("utf-8") scriptsVhostName = get_vhost_name(locker, vhost) info = get_vhost_info(locker, vhost) if pool == "unchanged": pass elif pool == "default": if not info['poolIPv4']: return conn.modify_s(scriptsVhostName, [(ldap.MOD_DELETE, "scriptsVhostPoolIPv4", None)]) else: if info['poolIPv4'] == pool: return res = conn.search_s( "ou=Pools,dc=scripts,dc=mit,dc=edu", ldap.SCOPE_ONELEVEL, ldap.filter.filter_format( "(&(objectClass=scriptsVhostPool)(scriptsVhostPoolIPv4=%s))", [pool]), ["description", "scriptsVhostPoolUserSelectable"], ) if not res or not res[0][1].get('scriptsVhostPoolUserSelectable'): name = pool if res: name = res[0][1].get('description', name) raise UserError("You cannot switch to the %s pool!" % (name,)) conn.modify_s( scriptsVhostName, [(ldap.MOD_REPLACE, "scriptsVhostPoolIPv4", [pool])] ) log.info( "%s set pool for vhost '%s' (locker '%s') to '%s'." % (current_user(), vhost, locker, pool) )
def request_vhost(locker, hostname, path, user=None, desc=""): """Request hostname as a vhost for the given locker and path. Throws a UserError if the request is invalid, otherwise returns a human-readable status message and sends a zephyr.""" locker = locker.encode("utf-8") hostname, reqtype = validate_hostname(hostname, locker) path = path.encode("utf-8") path = validate_path(path) message = "The hostname '%s' is now configured." % hostname if user is None: user = current_user() if reqtype == "moira": # actually_create_vhost does this check for other reqtypes check_if_already_exists(hostname, locker) t = queue.Ticket.create(locker, hostname, path, requestor=user, purpose=desc) short = hostname[: -len(".mit.edu")] out = rt.call( "ticket/new", Queue="Scripts", Subject="scripts-vhosts CNAME request: %s" % short, Text="""Heyas, %(user)s requested %(hostname)s for locker '%(locker)s' path %(path)s. Go to %(url)s to approve it. Purpose: %(desc)s Love, ~Scripts Pony """ % dict( short=short, user=user, locker=locker, hostname=hostname, desc=desc, path=path, url=tg.request.host_url + tg.url("/ticket/%s" % t.id), ), Requestor=user, AdminCc=rt.ponyaddr(), ) for line in out.splitlines(): if line.startswith("# Ticket ") and line.endswith(" created."): t.rtid = int(line[len("# Ticket ") : -len(" created.")]) break else: raise RuntimeError("Could not open an RT ticket") message = ( "We will request the hostname %s; mit.edu hostnames generally take 2-3 business days to become active." % hostname ) else: # Actually create the vhost actually_create_vhost(locker, hostname, path) if is_sudoing(): sudobit = "+scripts-pony-acl" forbit = " (for %s)" % user else: sudobit = "" forbit = "" logmessage = "%s%s requested %s for locker '%s' path '%s'%s (Purpose: %s)" % ( current_user(), sudobit, hostname, locker, path, forbit, desc, ) log.info(logmessage) return message