def _v1_auth(self, token_url): creds = self.creds headers = {} headers['X-Auth-User'] = creds['username'] headers['X-Auth-Key'] = creds['password'] tenant = creds.get('tenant') if tenant: headers['X-Auth-Tenant'] = tenant resp, resp_body = self._do_request(token_url, 'GET', headers=headers) if resp.status in (200, 204): try: self.management_url = resp['x-server-management-url'] self.auth_token = resp['x-auth-token'] except KeyError: raise exception.AuthorizationFailure() elif resp.status == 305: raise exception.AuthorizationRedirect(resp['location']) elif resp.status == 400: raise exception.AuthBadRequest() elif resp.status == 401: raise exception.NotAuthorized() elif resp.status == 404: raise exception.AuthUrlNotFound(url=token_url) else: raise Exception(_('Unexpected response: %s' % resp.status))
def image_get(context, image_id, session=None): """Get an image or raise if it does not exist.""" session = session or get_session() try: #NOTE(bcwaldon): this is to prevent false matches when mysql compares # an integer to a string that begins with that integer image_id = int(image_id) except (TypeError, ValueError): raise exception.NotFound("No image found") try: image = session.query(models.Image).\ options(joinedload(models.Image.properties)).\ options(joinedload(models.Image.members)).\ filter_by(deleted=_deleted(context)).\ filter_by(id=image_id).\ one() except exc.NoResultFound: raise exception.NotFound("No image found with ID %s" % image_id) # Make sure they can look at it if not context.is_image_visible(image): raise exception.NotAuthorized("Image not visible to you") return image
def process_request(self, req): """ Extract any authentication information in the request and construct an appropriate context from it. A few scenarios exist: 1. If X-Auth-Token is passed in, then consult TENANT and ROLE headers to determine permissions. 2. An X-Auth-Token was passed in, but the Identity-Status is not confirmed. For now, just raising a NotAuthorized exception. 3. X-Auth-Token is omitted. If we were using Keystone, then the tokenauth middleware would have rejected the request, so we must be using NoAuth. In that case, assume that is_admin=True. """ # TODO(sirp): should we be using the glance_tokeauth shim from # Keystone here? If we do, we need to make sure it handles the NoAuth # case auth_tok = req.headers.get('X-Auth-Token', req.headers.get('X-Storage-Token')) if auth_tok: if req.headers.get('X-Identity-Status') == 'Confirmed': # 1. Auth-token is passed, check other headers user = req.headers.get('X-User') tenant = req.headers.get('X-Tenant') roles = [ r.strip() for r in req.headers.get('X-Role', '').split(',') ] is_admin = 'Admin' in roles else: # 2. Indentity-Status not confirmed # FIXME(sirp): not sure what the correct behavior in this case # is; just raising NotAuthorized for now raise exception.NotAuthorized() else: # 3. Auth-token is ommited, assume NoAuth user = None tenant = None roles = [] is_admin = True req.context = self.make_context(auth_tok=auth_tok, user=user, tenant=tenant, roles=roles, is_admin=is_admin)
def _v2_auth(self, token_url): creds = self.creds creds = { "auth": { "tenantName": creds['tenant'], "passwordCredentials": { "username": creds['username'], "password": creds['password'] } } } headers = {} headers['Content-Type'] = 'application/json' req_body = json.dumps(creds) resp, resp_body = self._do_request(token_url, 'POST', headers=headers, body=req_body) if resp.status == 200: resp_auth = json.loads(resp_body)['access'] # FIXME(sirp): for now just using the first endpoint we get back # from the service catalog for glance, and using the public url. for service in resp_auth['serviceCatalog']: if service['type'] == 'image': glance_endpoint = service['endpoints'][0]['publicURL'] break else: raise exception.NoServiceEndpoint() self.management_url = glance_endpoint self.auth_token = resp_auth['token']['id'] elif resp.status == 305: raise exception.RedirectException(resp['location']) elif resp.status == 400: raise exception.AuthBadRequest(url=token_url) elif resp.status == 401: raise exception.NotAuthorized() elif resp.status == 404: raise exception.AuthUrlNotFound(url=token_url) else: raise Exception(_('Unexpected response: %s') % resp.status)
def image_member_get(context, member_id, session=None): """Get an image member or raise if it does not exist.""" session = session or get_session() try: member = session.query(models.ImageMember).\ options(joinedload(models.ImageMember.image)).\ filter_by(deleted=_deleted(context)).\ filter_by(id=member_id).\ one() except exc.NoResultFound: raise exception.NotFound("No membership found with ID %s" % member_id) # Make sure they can look at it if not context.is_image_visible(member.image): raise exception.NotAuthorized("Image not visible to you") return member
def delete(self, location): """ Takes a `glance.store.location.Location` object that indicates where to find the image file to delete :location `glance.store.location.Location` object, supplied from glance.store.location.get_location_from_uri() :raises NotFound if image does not exist :raises NotAuthorized if cannot delete because of permissions """ loc = location.store_location fn = loc.path if os.path.exists(fn): try: logger.debug(_("Deleting image at %(fn)s") % locals()) os.unlink(fn) except OSError: raise exception.NotAuthorized( _("You cannot delete file %s") % fn) else: raise exception.NotFound(_("Image file %s does not exist") % fn)
def enforce(self, context, action, target): """Verifies that the action is valid on the target in this context. :param context: Glance request context :param action: String representing the action to be checked :param object: Dictionary representing the object of the action. :raises: `glance.common.exception.NotAuthorized` :returns: None """ self.load_rules() match_list = ('rule:%s' % action, ) credentials = { 'roles': context.roles, 'user': context.user, 'tenant': context.tenant, } try: policy.enforce(match_list, target, credentials) except policy.NotAuthorized: raise exception.NotAuthorized(action=action)
def image_get(context, image_id, session=None, force_show_deleted=False): """Get an image or raise if it does not exist.""" session = session or get_session() try: query = session.query(models.Image).\ options(joinedload(models.Image.properties)).\ options(joinedload(models.Image.members)).\ filter_by(id=image_id) # filter out deleted images if context disallows it if not force_show_deleted and not can_show_deleted(context): query = query.filter_by(deleted=False) image = query.one() except exc.NoResultFound: raise exception.NotFound("No image found with ID %s" % image_id) # Make sure they can look at it if not context.is_image_visible(image): raise exception.NotAuthorized("Image not visible to you") return image
def check_mutate_authorization(context, image_ref): if not context.is_image_mutable(image_ref): logger.info(_("Attempted to modify image user did not own.")) msg = _("You do not own this image") raise exception.NotAuthorized(msg)
def _do_request(self, method, url, body, headers): """ Connects to the server and issues a request. Handles converting any returned HTTP error status codes to OpenStack/Glance exceptions and closing the server connection. Returns the result data, or raises an appropriate exception. :param method: HTTP method ("GET", "POST", "PUT", etc...) :param url: urlparse.ParsedResult object with URL information :param body: data to send (as string, filelike or iterable), or None (default) :param headers: mapping of key/value pairs to add as headers :note If the body param has a read attribute, and method is either POST or PUT, this method will automatically conduct a chunked-transfer encoding and use the body as a file object or iterable, transferring chunks of data using the connection's send() method. This allows large objects to be transferred efficiently without buffering the entire body in memory. """ if url.query: path = url.path + "?" + url.query else: path = url.path try: connection_type = self.get_connection_type() headers = headers or {} if 'x-auth-token' not in headers and self.auth_tok: headers['x-auth-token'] = self.auth_tok c = connection_type(url.hostname, url.port, **self.connect_kwargs) def _pushing(method): return method.lower() in ('post', 'put') def _simple(body): return body is None or isinstance(body, basestring) def _filelike(body): return hasattr(body, 'read') def _sendbody(connection, iter): connection.endheaders() for sent in iter: # iterator has done the heavy lifting pass def _chunkbody(connection, iter): connection.putheader('Transfer-Encoding', 'chunked') connection.endheaders() for chunk in iter: connection.send('%x\r\n%s\r\n' % (len(chunk), chunk)) connection.send('0\r\n\r\n') # Do a simple request or a chunked request, depending # on whether the body param is file-like or iterable and # the method is PUT or POST # if not _pushing(method) or _simple(body): # Simple request... c.request(method, path, body, headers) elif _filelike(body) or self._iterable(body): c.putrequest(method, path) for header, value in headers.items(): c.putheader(header, value) iter = self.image_iterator(c, headers, body) if self._sendable(body): # send actual file without copying into userspace _sendbody(c, iter) else: # otherwise iterate and chunk _chunkbody(c, iter) else: raise TypeError('Unsupported image type: %s' % body.__class__) res = c.getresponse() status_code = self.get_status_code(res) if status_code in self.OK_RESPONSE_CODES: return res elif status_code in self.REDIRECT_RESPONSE_CODES: raise exception.RedirectException(res.getheader('Location')) elif status_code == httplib.UNAUTHORIZED: raise exception.NotAuthorized(res.read()) elif status_code == httplib.FORBIDDEN: raise exception.NotAuthorized(res.read()) elif status_code == httplib.NOT_FOUND: raise exception.NotFound(res.read()) elif status_code == httplib.CONFLICT: raise exception.Duplicate(res.read()) elif status_code == httplib.BAD_REQUEST: raise exception.Invalid(res.read()) elif status_code == httplib.MULTIPLE_CHOICES: raise exception.MultipleChoices(body=res.read()) elif status_code == httplib.INTERNAL_SERVER_ERROR: raise Exception("Internal Server error: %s" % res.read()) else: raise Exception("Unknown error occurred! %s" % res.read()) except (socket.error, IOError), e: raise exception.ClientConnectionError(e)
def _do_request(self, method, action, body=None, headers=None, params=None): """ Connects to the server and issues a request. Handles converting any returned HTTP error status codes to OpenStack/Glance exceptions and closing the server connection. Returns the result data, or raises an appropriate exception. :param method: HTTP method ("GET", "POST", "PUT", etc...) :param action: part of URL after root netloc :param body: string of data to send, or None (default) :param headers: mapping of key/value pairs to add as headers :param params: dictionary of key/value pairs to add to append to action :note If the body param has a read attribute, and method is either POST or PUT, this method will automatically conduct a chunked-transfer encoding and use the body as a file object, transferring chunks of data using the connection's send() method. This allows large objects to be transferred efficiently without buffering the entire body in memory. """ if type(params) is dict: # remove any params that are None for (key, value) in params.items(): if value is None: del params[key] action += '?' + urllib.urlencode(params) try: connection_type = self.get_connection_type() headers = headers or {} if 'x-auth-token' not in headers and self.auth_tok: headers['x-auth-token'] = self.auth_tok c = connection_type(self.host, self.port) if self.doc_root: action = '/'.join([self.doc_root, action.lstrip('/')]) # Do a simple request or a chunked request, depending # on whether the body param is a file-like object and # the method is PUT or POST if hasattr(body, 'read') and method.lower() in ('post', 'put'): # Chunk it, baby... c.putrequest(method, action) for header, value in headers.items(): c.putheader(header, value) c.putheader('Transfer-Encoding', 'chunked') c.endheaders() chunk = body.read(self.CHUNKSIZE) while chunk: c.send('%x\r\n%s\r\n' % (len(chunk), chunk)) chunk = body.read(self.CHUNKSIZE) c.send('0\r\n\r\n') else: # Simple request... c.request(method, action, body, headers) res = c.getresponse() status_code = self.get_status_code(res) if status_code in (httplib.OK, httplib.CREATED, httplib.ACCEPTED, httplib.NO_CONTENT): return res elif status_code == httplib.UNAUTHORIZED: raise exception.NotAuthorized(res.read()) elif status_code == httplib.FORBIDDEN: raise exception.NotAuthorized(res.read()) elif status_code == httplib.NOT_FOUND: raise exception.NotFound(res.read()) elif status_code == httplib.CONFLICT: raise exception.Duplicate(res.read()) elif status_code == httplib.BAD_REQUEST: raise exception.Invalid(res.read()) elif status_code == httplib.MULTIPLE_CHOICES: raise exception.MultipleChoices(body=res.read()) elif status_code == httplib.INTERNAL_SERVER_ERROR: raise Exception("Internal Server error: %s" % res.read()) else: raise Exception("Unknown error occurred! %s" % res.read()) except (socket.error, IOError), e: raise exception.ClientConnectionError("Unable to connect to " "server. Got error: %s" % e)