def from_project(cls, ssh_key, project, owner): # calculate the hash of the ssh_key+project.key.id() import hashlib m = hashlib.md5() m.update(ssh_key) m.update(str(project.key.id())) ssh_key_hash = m.hexdigest() # do we have this wisp already? if not owner: entry = cls.query().filter(cls.ssh_key_hash == ssh_key_hash).get() else: entry = cls.query().filter(cls.ssh_key_hash == ssh_key_hash, cls.owner == owner.key).get() # create if we didn't find it if not entry: # generate new token and create new entry token = "%s" % generate_token(size=16, caselimit=True) entry = Wisp(name=project.name, ssh_key=ssh_key, ssh_key_hash=ssh_key_hash, token=token, project=project.key, owner=owner.key) entry.put() return entry
def from_stock(cls, ssh_key, post_creation, dynamic_image_url, image_disk_format, image_container_format, owner): # calculate the hash of the ssh_key+post_creation+dynamic_image_url import hashlib m = hashlib.md5() m.update(ssh_key) m.update(post_creation) m.update(dynamic_image_url) ssh_key_hash = m.hexdigest() # do we have this wisp already? if not owner: entry = cls.query().filter(cls.ssh_key_hash == ssh_key_hash).get() else: entry = cls.query().filter(cls.ssh_key_hash == ssh_key_hash, cls.owner == owner.key).get() # create if we didn't find it if not entry: # generate new token and create new entry token = "%s" % generate_token(size=16, caselimit=True) entry = Wisp(name='anonymous', ssh_key=ssh_key, ssh_key_hash=ssh_key_hash, post_creation=post_creation, dynamic_image_url=dynamic_image_url, image_disk_format=image_disk_format, image_container_format=image_container_format, token=token, owner=owner.key) entry.put() return entry
def get(self): # next url handling (in the event of veering off path to a page) url = self.request.get('next') if url: page = NextPages(url=url, npid=utils.generate_token(size=12).lower()) page.put() # card for slack if "slackbot" in self.request.headers.get('User-Agent').lower(): return self.redirect(self.uri_for('slack-card')) # login with github only at this point try: scope = 'user:email' github_helper = github.GithubAuth(scope, npid=page.npid) # create a github login url and go login_url = github_helper.get_authorize_url() self.redirect(login_url) except Exception as ex: # add error notice for user TODO self.auth.unset_session() self.redirect('https://lucidworks.com/labs')
def get(self): # basics ip = self.request.remote_addr # setup channel to do page refresh channel_token = generate_token() refresh_channel = channel.create_channel(channel_token) # various pulldown initialization self.form.flavor.choices=[] self.form.wisp.choices=[] self.form.cloud.choices=[] # add list of user's flavors, if any flavors = Flavor.flavors_with_instances_on_sale() for flavor in flavors: self.form.flavor.choices.insert(0, (flavor.name, flavor.description)) # used for determining element layout wisp = None cloud = None # if the user is logged in, we build out the list of their wisps if self.user_id: # lookup user's auth info and wisps user_info = User.get_by_id(long(self.user_id)) wisps = Wisp.get_by_user(user_info.key) for wisp in wisps: self.form.wisp.choices.insert(0, (wisp.key.id(), wisp.name)) # load clouds clouds = Cloud.get_by_user(user_info.key) # create default cloud if none if not clouds: cloud = Cloud.create_default(user_info.key) clouds.append(cloud) self.add_message("Default cloud created.", 'success') for cloud in clouds: self.form.cloud.choices.insert(0, (cloud.key.id(), cloud.name)) # params build out (injects the last wisp, if there was one) params = { 'remote_ip': ip, 'wisp': wisp, 'cloud': cloud, 'refresh_channel': refresh_channel, 'channel_token': channel_token } return self.render_template('lab/launcher.html', **params)
def invite(cls, email, group, invitor): # do we have this combo already? entry = cls.query().filter(cls.email == email, cls.group == group).get() if not entry: # generate new token and create new entry token = "%s" % generate_token(size=16, caselimit=True) entry = GroupMembers(group=group, email=email, invitor=invitor, token=token, active=False) entry.put() return entry
def invite(cls, email, group, invitor): # do we have this combo already? entry = cls.query().filter(cls.email == email, cls.group == group).get() if not entry: # generate new token and create new entry token = "%s" % generate_token(size=16, caselimit=True) entry = GroupMembers( group = group, email = email, invitor = invitor, token = token, active = False ) entry.put() return entry
def from_stock( cls, ssh_key, post_creation, dynamic_image_url, image_disk_format, image_container_format, owner ): # calculate the hash of the ssh_key+post_creation+dynamic_image_url import hashlib m = hashlib.md5() m.update(ssh_key) m.update(post_creation) m.update(dynamic_image_url) ssh_key_hash = m.hexdigest() # do we have this wisp already? if not owner: entry = cls.query().filter( cls.ssh_key_hash == ssh_key_hash ).get() else: entry = cls.query().filter( cls.ssh_key_hash == ssh_key_hash, cls.owner == owner.key ).get() # create if we didn't find it if not entry: # generate new token and create new entry token = "%s" % generate_token(size=16, caselimit=True) entry = Wisp( name = 'anonymous', ssh_key = ssh_key, ssh_key_hash = ssh_key_hash, post_creation = post_creation, dynamic_image_url = dynamic_image_url, image_disk_format = image_disk_format, image_container_format = image_container_format, token = token, owner = owner.key ) entry.put() return entry
def get(self): # load user information user_info = User.get_by_id(long(self.user_id)) generate_api_token = self.request.get('generate_api_token') if generate_api_token == "1": user_info.api_token = utils.generate_token() user_info.put() return self.redirect_to('account-settings') # form fields self.form.username.data = user_info.username self.form.name.data = user_info.name self.form.email.data = user_info.email self.form.company.data = user_info.company self.form.country.data = user_info.country self.form.timezone.data = user_info.timezone self.form.ssh_key.data = user_info.ssh_key # extras params = {} params['tfenabled'] = user_info.tfenabled params['api_token'] = user_info.api_token # create holder token to setup 2FA - this will continue until user enables 2fa if user_info.tfenabled == False: secret = pyotp.random_base32() totp = pyotp.TOTP(secret) qrcode = totp.provisioning_uri("%s-%s" % (config.app_name, user_info.email)) params['qrcode'] = qrcode params['secret'] = secret # update the user's key user_info.tfsecret = secret user_info.put() return self.render_template('user/settings.html', **params)
def from_project( cls, ssh_key, project, owner ): # calculate the hash of the ssh_key+project.key.id() import hashlib m = hashlib.md5() m.update(ssh_key) m.update(str(project.key.id())) ssh_key_hash = m.hexdigest() # do we have this wisp already? if not owner: entry = cls.query().filter( cls.ssh_key_hash == ssh_key_hash ).get() else: entry = cls.query().filter( cls.ssh_key_hash == ssh_key_hash, cls.owner == owner.key ).get() # create if we didn't find it if not entry: # generate new token and create new entry token = "%s" % generate_token(size=16, caselimit=True) entry = Wisp( name = project.name, ssh_key = ssh_key, ssh_key_hash = ssh_key_hash, token = token, project = project.key, owner = owner.key ) entry.put() return entry
def post(self): # request basics ip = self.request.remote_addr # response, type, cross posting params = {} self.response.headers['Content-Type'] = "application/json" self.response.headers['Access-Control-Allow-Origin'] = '*' # check if this IP has any other bids open instancebid = InstanceBid.get_incomplete_by_ip(ip) # check we have an instancebid already if instancebid: # validate wisp if instancebid.wisp == None: instancebid.key.delete() return error_response(self, "Deleting bid because no wisp was associated.", 403, params) # load the payment address if instancebid.instance: instancebid.address = instancebid.instance.get().address instancebid.ask = instancebid.instance.get().ask else: # we should have an instance assosciated, so bail on this one instancebid.key.delete() return error_response(self, "Deleting bid because no instance was associated.", 403, params) params['response'] = "error" params['message'] = "The calling IP address already has an instance reservation in progress." params['instancebid'] = instancebid self.response.set_status(403) return self.render_template('api/bid.json', **params) # load POSTed JSON try: request = json.loads(self.request.body) except Exception as ex: return error_response(self, "Failure in parsing request JSON.", 403, params) # load optional values or defaults # ipv4 (allow default) if 'requires_ipv4' in request: requires_ipv4 = request['requires_ipv4'] else: requires_ipv4 = 0 # ipv6 (allow default) if 'requires_ipv6' in request: requires_ipv6 = request['requires_ipv6'] else: requires_ipv6 = 0 # providers (allow default) if 'providers' in request: providers = request['providers'] else: providers = [{u'id': 1, u'name': u'All Providers'}] # flavors (required) if 'flavor' in request: flavor_name = request['flavor'] flavor = Flavor.get_by_name(flavor_name) # check if flavor was found if not flavor: return error_response(self, "Flavor not found.", 403, params) else: return error_response(self, "Flavor name is required.", 403, params) # cloud (optional) if 'cloud_id' in request: cloud_id = request['cloud_id'] cloud = Cloud.get_by_id(long(cloud_id)) # check if cloud was found if not cloud: return error_response(self, "Cloud ID not found.", 403, params) else: cloud = None # disallow both a wisp and a callback_url if 'wisp_id' in request and 'callback_url' in request: return error_response(self, "A wisp and a callback URL may not be used together.", 403, params) # require either a wisp or a callback_url if 'wisp_id' not in request and 'callback_url' not in request: return error_response(self, "A valid wisp or a callback URL is required.", 403, params) # load the wisp, if there is one if 'wisp_id' in request: wisp_id = request['wisp_id'] wisp = Wisp.get_by_id(long(wisp_id)) else: wisp = None # load the callback URL, if there is one if 'callback_url' in request: callback_url = request['callback_url'] elif wisp: callback_url = wisp.callback_url else: callback_url = "" # test we have a callback_url or a valid image in the wisp if callback_url > "": try: result = urlfetch.fetch(callback_url, deadline=5) if result.status_code > 399: return error_response(self, "The callback URL is unreachable.", 403, params) # test result's image URL except Exception as ex: return error_response(self, "The callback URL is unreachable.", 403, params) elif wisp: if wisp.image == None and wisp.dynamic_image_url == None and wisp.project == None: return error_response(self, "A valid wisp or a callback URL is required.", 403, params) # grab a new bid hash to use for the new bid token = generate_token(size=16) name = "smr-%s" % generate_token(size=8) # create a new bid instancebid = InstanceBid() instancebid.token = token instancebid.name = name instancebid.need_ipv4_address = bool(requires_ipv4) instancebid.need_ipv6_address = bool(requires_ipv6) instancebid.flavor = flavor.key instancebid.remote_ip = ip instancebid.appliances = providers # providers is already JSON instancebid.status = 0 instancebid.callback_url = callback_url # expires in 5 minutes epoch_time = int(time.time()) instancebid.expires = datetime.fromtimestamp(epoch_time+300) # add wisp, if present if wisp: instancebid.wisp = wisp.key # add cloud, if present if cloud: instancebid.cloud = cloud.key # update instancebid.put() # sleep for dev if config.debug: time.sleep(2) # reserve the instance InstanceBid.reserve_instance_by_token(instancebid.token) # get the address, if you got an instance if instancebid.instance: address = instancebid.instance.get().address ask = instancebid.instance.get().ask else: # no instance was reserved instancebid.key.delete() return error_response(self, "No valid instances were returned.", 403, params) # hack address and ask into instancebid object for template (not stored) instancebid.address = address instancebid.ask = ask # build out the response params['response'] = "success" params['message'] = "A new instance bid has been created." params['instancebid'] = instancebid # return response and include cross site POST headers self.response.set_status(201) return self.render_template('api/bid.json', **params)