def _validate_tba_auth_key(self): """ Tests the presence of a X-TBA-Auth-Key header or URL param. """ x_tba_auth_key = self.request.headers.get("X-TBA-Auth-Key") if x_tba_auth_key is None: x_tba_auth_key = self.request.get('X-TBA-Auth-Key') self.auth_owner = None self.auth_owner_key = None self.auth_description = None if not x_tba_auth_key: account = self._user_bundle.account if account: self.auth_owner = account.key.id() self.auth_owner_key = account.key elif 'thebluealliance.com' in self.request.headers.get("Origin", ""): self.auth_owner = 'The Blue Alliance' else: self._errors = json.dumps({"Error": "X-TBA-Auth-Key is a required header or URL param. Please get an access key at http://www.thebluealliance.com/account."}) self.abort(401) if self.auth_owner: logging.info("Auth owner: {}, LOGGED IN".format(self.auth_owner)) else: auth = ApiAuthAccess.get_by_id(x_tba_auth_key) if auth and auth.is_read_key: self.auth_owner = auth.owner.id() self.auth_owner_key = auth.owner self.auth_description = auth.description logging.info("Auth owner: {}, X-TBA-Auth-Key: {}".format(self.auth_owner, x_tba_auth_key)) else: self._errors = json.dumps({"Error": "X-TBA-Auth-Key is invalid. Please get an access key at http://www.thebluealliance.com/account."}) self.abort(401)
def post(self, event_key): event_key = event_key.lower( ) # Normalize keys to lower case (TBA convention) # Start by allowing admins to edit any event user_has_auth = (self._user_bundle.user and self._user_bundle.is_current_user_admin) if not user_has_auth and self._user_bundle.user: # See if this user has any auth keys granted to its account now = datetime.datetime.now() auth_tokens = ApiAuthAccess.query( ApiAuthAccess.owner == self._user_bundle.account.key, ApiAuthAccess.event_list == ndb.Key(Event, event_key), ndb.OR(ApiAuthAccess.expiration == None, ApiAuthAccess.expiration >= now)).fetch() user_has_auth = any( self._validate_auth(auth, event_key) is None for auth in auth_tokens) if not user_has_auth: # If not, check if auth id/secret were passed as headers auth_id = self.request.headers.get('X-TBA-Auth-Id') if not auth_id: self._errors = json.dumps({ "Error": "Must provide a request header parameter 'X-TBA-Auth-Id'" }) self.abort(400) auth_sig = self.request.headers.get('X-TBA-Auth-Sig') if not auth_sig: self._errors = json.dumps({ "Error": "Must provide a request header parameter 'X-TBA-Auth-Sig'" }) self.abort(400) auth = ApiAuthAccess.get_by_id(auth_id) expected_sig = md5.new('{}{}{}'.format( auth.secret if auth else None, self.request.path, self.request.body)).hexdigest() if not auth or expected_sig != auth_sig: logging.info("Auth sig: {}, Expected sig: {}".format( auth_sig, expected_sig)) self._errors = json.dumps( {"Error": "Invalid X-TBA-Auth-Id and/or X-TBA-Auth-Sig!"}) self.abort(401) # Checks event key is valid, correct auth types, and expiration error = self._validate_auth(auth, event_key) if error: self._errors = json.dumps({"Error": error}) self.abort(401) try: self._process_request(self.request, event_key) except ParserInputException, e: self._errors = json.dumps({"Error": e.message}) self.abort(400)
def post(self, event_key): auth_id = self.request.headers.get('X-TBA-Auth-Id') if not auth_id: self._errors = json.dumps({"Error": "Must provide a request header parameter 'X-TBA-Auth-Id'"}) self.abort(400) auth_sig = self.request.headers.get('X-TBA-Auth-Sig') if not auth_sig: self._errors = json.dumps({"Error": "Must provide a request header parameter 'X-TBA-Auth-Sig'"}) self.abort(400) auth = ApiAuthAccess.get_by_id(auth_id) if not auth or md5.new('{}{}{}'.format(auth.secret, self.request.path, self.request.body)).hexdigest() != auth_sig: self._errors = json.dumps({"Error": "Invalid X-TBA-Auth-Id and/or X-TBA-Auth-Sig!"}) self.abort(400) allowed_event_keys = [ekey.id() for ekey in auth.event_list] if event_key not in allowed_event_keys: self._errors = json.dumps({"Error": "Only allowed to edit events: {}".format(', '.join(allowed_event_keys))}) self.abort(400) try: self._process_request(self.request, event_key) except ParserInputException, e: self._errors = json.dumps({"Error": e.message}) self.abort(400)
def post(self, auth_id): self._require_admin() auth = ApiAuthAccess.get_by_id(auth_id) auth_types_enum = [] if self.request.get('allow_edit_teams'): auth_types_enum.append(AuthType.EVENT_TEAMS) if self.request.get('allow_edit_matches'): auth_types_enum.append(AuthType.EVENT_MATCHES) if self.request.get('allow_edit_rankings'): auth_types_enum.append(AuthType.EVENT_RANKINGS) if self.request.get('allow_edit_alliances'): auth_types_enum.append(AuthType.EVENT_ALLIANCES) if self.request.get('allow_edit_awards'): auth_types_enum.append(AuthType.EVENT_AWARDS) if self.request.get('allow_edit_match_video'): auth_types_enum.append(AuthType.MATCH_VIDEO) if not auth: auth = ApiAuthAccess( id=auth_id, description=self.request.get('description'), secret=''.join(random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for _ in range(64)), event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')], auth_types_enum=auth_types_enum, ) else: auth.description = self.request.get('description') auth.event_list = event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')] auth.auth_types_enum = auth_types_enum auth.put() self.redirect("/admin/api_auth/manage")
def post(self, event_key): auth_id = self.request.headers.get('X-TBA-Auth-Id') if not auth_id: self._errors = json.dumps({"Error": "Must provide a request header parameter 'X-TBA-Auth-Id'"}) self.abort(400) auth_sig = self.request.headers.get('X-TBA-Auth-Sig') if not auth_sig: self._errors = json.dumps({"Error": "Must provide a request header parameter 'X-TBA-Auth-Sig'"}) self.abort(400) auth = ApiAuthAccess.get_by_id(auth_id) expected_sig = md5.new('{}{}{}'.format(auth.secret if auth else None, self.request.path, self.request.body)).hexdigest() if not auth or expected_sig != auth_sig: logging.info("Auth sig: {}, Expected sig: {}".format(auth_sig, expected_sig)) self._errors = json.dumps({"Error": "Invalid X-TBA-Auth-Id and/or X-TBA-Auth-Sig!"}) self.abort(400) allowed_event_keys = [ekey.id() for ekey in auth.event_list] if event_key not in allowed_event_keys: self._errors = json.dumps({"Error": "Only allowed to edit events: {}".format(', '.join(allowed_event_keys))}) self.abort(400) missing_auths = self.REQUIRED_AUTH_TYPES.difference(set(auth.auth_types_enum)) if missing_auths != set(): self._errors = json.dumps({"Error": "You do not have permission to edit: {}. If this is incorrect, please contact TBA admin.".format(",".join([AuthType.type_names[ma] for ma in missing_auths]))}) self.abort(400) try: self._process_request(self.request, event_key) except ParserInputException, e: self._errors = json.dumps({"Error": e.message}) self.abort(400)
def post(self, event_key): auth_id = self.request.headers.get('X-TBA-Auth-Id') if not auth_id: self._errors = json.dumps({"Error": "Must provide a request header parameter 'X-TBA-Auth-Id'"}) self.abort(400) auth_sig = self.request.headers.get('X-TBA-Auth-Sig') if not auth_sig: self._errors = json.dumps({"Error": "Must provide a request header parameter 'X-TBA-Auth-Sig'"}) self.abort(400) auth = ApiAuthAccess.get_by_id(auth_id) if not auth or md5.new('{}{}{}'.format(auth.secret, self.request.path, self.request.body)).hexdigest() != auth_sig: self._errors = json.dumps({"Error": "Invalid X-TBA-Auth-Id and/or X-TBA-Auth-Sig!"}) self.abort(400) allowed_event_keys = [ekey.id() for ekey in auth.event_list] if event_key not in allowed_event_keys: self._errors = json.dumps({"Error": "Only allowed to edit events: {}".format(', '.join(allowed_event_keys))}) self.abort(400) missing_auths = self.REQUIRED_AUTH_TYPES.difference(set(auth.auth_types_enum)) if missing_auths != set(): self._errors = json.dumps({"Error": "You do not have permission to edit: {}. If this is incorrect, please contact TBA admin.".format(",".join([AuthType.type_names[ma] for ma in missing_auths]))}) self.abort(400) try: self._process_request(self.request, event_key) except ParserInputException, e: self._errors = json.dumps({"Error": e.message}) self.abort(400)
def get(self, auth_id): self._require_admin() auth = ApiAuthAccess.get_by_id(auth_id) self.template_values.update({"auth": auth}) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/api_delete_auth.html') self.response.out.write(template.render(path, self.template_values))
def post(self): self._require_registration() key_id = self.request.get('key_id') auth = ApiAuthAccess.get_by_id(key_id) if auth and auth.owner == self.user_bundle.account.key: auth.key.delete() self.redirect('/account?status=read_key_delete_success') else: self.redirect('/account?status=read_key_delete_failure')
def get(self, auth_id): self._require_admin() auth = ApiAuthAccess.get_by_id(auth_id) self.template_values.update({ "auth": auth }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/api_delete_auth.html') self.response.out.write(template.render(path, self.template_values))
def post(self, auth_id): self._require_admin() logging.warning("Deleting auth: %s at the request of %s / %s" % (auth_id, self.user_bundle.user.user_id(), self.user_bundle.user.email())) auth = ApiAuthAccess.get_by_id(auth_id) auth.key.delete() self.redirect("/admin/api_auth/manage")
def get(self, auth_id): self._require_admin() auth = ApiAuthAccess.get_by_id(auth_id) auth.event_list_str = ','.join([event_key.id() for event_key in auth.event_list]) self.template_values.update({ "auth": auth, }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/api_edit_auth.html') self.response.out.write(template.render(path, self.template_values))
def post(self, auth_id): self._require_admin() logging.warning("Deleting auth: %s at the request of %s / %s" % ( auth_id, self.user_bundle.user.user_id(), self.user_bundle.user.email())) auth = ApiAuthAccess.get_by_id(auth_id) auth.key.delete() self.redirect("/admin/api_auth/manage")
def _validate_tba_auth_key(self): """ Tests the presence of a X-TBA-Auth-Key header or URL param. """ with TraceContext() as root: with root.span("ApiBaseController._validate_tba_auth_key"): x_tba_auth_key = self.request.headers.get("X-TBA-Auth-Key") if x_tba_auth_key is None: x_tba_auth_key = self.request.get('X-TBA-Auth-Key') self.auth_owner = None self.auth_owner_key = None self.auth_description = None if not x_tba_auth_key: account = self._user_bundle.account if account: self.auth_owner = account.key.id() self.auth_owner_key = account.key elif 'thebluealliance.com' in self.request.headers.get( "Origin", ""): self.auth_owner = 'The Blue Alliance' else: self._errors = { "Error": "X-TBA-Auth-Key is a required header or URL param. Please get an access key at http://www.thebluealliance.com/account." } self.abort(401) if self.auth_owner: logging.info("Auth owner: {}, LOGGED IN".format( self.auth_owner)) else: auth = ApiAuthAccess.get_by_id(x_tba_auth_key) if auth and auth.is_read_key: self.auth_owner = auth.owner.id() self.auth_owner_key = auth.owner self.auth_description = auth.description if self.REQUIRE_ADMIN_AUTH and not auth.allow_admin: self._errors = { "Error": "X-TBA-Auth-Key does not have required permissions" } self.abort(401) logging.info( "Auth owner: {}, X-TBA-Auth-Key: {}".format( self.auth_owner, x_tba_auth_key)) else: self._errors = { "Error": "X-TBA-Auth-Key is invalid. Please get an access key at http://www.thebluealliance.com/account." } self.abort(401)
def post(self, auth_id): self._require_admin() auth = ApiAuthAccess.get_by_id(auth_id) auth_types_enum = [] if self.request.get('allow_edit_teams'): auth_types_enum.append(AuthType.EVENT_TEAMS) if self.request.get('allow_edit_matches'): auth_types_enum.append(AuthType.EVENT_MATCHES) if self.request.get('allow_edit_rankings'): auth_types_enum.append(AuthType.EVENT_RANKINGS) if self.request.get('allow_edit_alliances'): auth_types_enum.append(AuthType.EVENT_ALLIANCES) if self.request.get('allow_edit_awards'): auth_types_enum.append(AuthType.EVENT_AWARDS) if self.request.get('allow_edit_match_video'): auth_types_enum.append(AuthType.MATCH_VIDEO) if self.request.get('owner', None): owner = Account.query(Account.email == self.request.get('owner')).fetch() owner_key = owner[0].key if owner else None else: owner_key = None if self.request.get('expiration', None): expiration = datetime.strptime(self.request.get('expiration'), '%Y-%m-%d') else: expiration = None if not auth: auth = ApiAuthAccess( id=auth_id, description=self.request.get('description'), owner=owner_key, expiration=expiration, secret=''.join(random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for _ in range(64)), event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')], auth_types_enum=auth_types_enum, ) else: auth.description = self.request.get('description') auth.event_list = event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')] auth.auth_types_enum = auth_types_enum auth.owner = owner_key auth.expiration = expiration auth.put() self.redirect("/admin/api_auth/manage")
def get(self, auth_id): self._require_admin() auth = ApiAuthAccess.get_by_id(auth_id) auth.event_list_str = ','.join( [event_key.id() for event_key in auth.event_list]) self.template_values.update({ "auth": auth, }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/api_edit_auth.html') self.response.out.write(template.render(path, self.template_values))
def post(self, event_key): event_key = event_key.lower() # Normalize keys to lower case (TBA convention) # Start by allowing admins to edit any event user_has_auth = self._user_bundle.user and self._user_bundle.is_current_user_admin if not user_has_auth and self._user_bundle.user: # See if this user has any auth keys granted to its account now = datetime.datetime.now() auth_tokens = ApiAuthAccess.query( ApiAuthAccess.owner == self._user_bundle.account.key, ApiAuthAccess.event_list == ndb.Key(Event, event_key), ndb.OR(ApiAuthAccess.expiration == None, ApiAuthAccess.expiration >= now), ).fetch() user_has_auth = any(self._validate_auth(auth, event_key) is None for auth in auth_tokens) if not user_has_auth: # If not, check if auth id/secret were passed as headers auth_id = self.request.headers.get("X-TBA-Auth-Id") if not auth_id: self._errors = json.dumps({"Error": "Must provide a request header parameter 'X-TBA-Auth-Id'"}) self.abort(400) auth_sig = self.request.headers.get("X-TBA-Auth-Sig") if not auth_sig: self._errors = json.dumps({"Error": "Must provide a request header parameter 'X-TBA-Auth-Sig'"}) self.abort(400) auth = ApiAuthAccess.get_by_id(auth_id) expected_sig = md5.new( "{}{}{}".format(auth.secret if auth else None, self.request.path, self.request.body) ).hexdigest() if not auth or expected_sig != auth_sig: logging.info("Auth sig: {}, Expected sig: {}".format(auth_sig, expected_sig)) self._errors = json.dumps({"Error": "Invalid X-TBA-Auth-Id and/or X-TBA-Auth-Sig!"}) self.abort(401) # Checks event key is valid, correct auth types, and expiration error = self._validate_auth(auth, event_key) if error: self._errors = json.dumps({"Error": error}) self.abort(401) try: self._process_request(self.request, event_key) except ParserInputException, e: self._errors = json.dumps({"Error": e.message}) self.abort(400)
def post(self, auth_id): self._require_admin() auth = ApiAuthAccess.get_by_id(auth_id) if not auth: auth = ApiAuthAccess( id=auth_id, description=self.request.get('description'), secret=''.join(random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for _ in range(64)), event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')], ) else: auth.description = self.request.get('description') auth.event_list = event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')] auth.put() self.redirect("/admin/api_auth/manage")
def post(self, auth_id): self._require_admin() auth = ApiAuthAccess.get_by_id(auth_id) auth_types_enum = [] if self.request.get('allow_edit_teams'): auth_types_enum.append(AuthType.EVENT_TEAMS) if self.request.get('allow_edit_matches'): auth_types_enum.append(AuthType.EVENT_MATCHES) if self.request.get('allow_edit_rankings'): auth_types_enum.append(AuthType.EVENT_RANKINGS) if self.request.get('allow_edit_alliances'): auth_types_enum.append(AuthType.EVENT_ALLIANCES) if self.request.get('allow_edit_awards'): auth_types_enum.append(AuthType.EVENT_AWARDS) if self.request.get('allow_edit_match_video'): auth_types_enum.append(AuthType.MATCH_VIDEO) if not auth: auth = ApiAuthAccess( id=auth_id, description=self.request.get('description'), secret=''.join( random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for _ in range(64)), event_list=[ ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',') ], auth_types_enum=auth_types_enum, ) else: auth.description = self.request.get('description') auth.event_list = event_list = [ ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',') ] auth.auth_types_enum = auth_types_enum auth.put() self.redirect("/admin/api_auth/manage")
def _validate_tba_auth_key(self): """ Tests the presence of a X-TBA-Auth-Key header or URL param. """ x_tba_auth_key = self.request.headers.get("X-TBA-Auth-Key") if x_tba_auth_key is None: x_tba_auth_key = self.request.get('X-TBA-Auth-Key') self.auth_owner = None self.auth_owner_key = None self.auth_description = None if not x_tba_auth_key: account = self._user_bundle.account if account: self.auth_owner = account.key.id() self.auth_owner_key = account.key elif 'thebluealliance.com' in self.request.headers.get( "Origin", ""): self.auth_owner = 'The Blue Alliance' else: self._errors = json.dumps({ "Error": "X-TBA-Auth-Key is a required header or URL param. Please get an access key at http://www.thebluealliance.com/account." }) self.abort(401) if self.auth_owner: logging.info("Auth owner: {}, LOGGED IN".format(self.auth_owner)) else: auth = ApiAuthAccess.get_by_id(x_tba_auth_key) if auth and auth.is_read_key: self.auth_owner = auth.owner.id() self.auth_owner_key = auth.owner self.auth_description = auth.description logging.info("Auth owner: {}, X-TBA-Auth-Key: {}".format( self.auth_owner, x_tba_auth_key)) else: self._errors = json.dumps({ "Error": "X-TBA-Auth-Key is invalid. Please get an access key at http://www.thebluealliance.com/account." }) self.abort(401)
def _validate_tba_auth_key(self): """ Tests the presence of a X-TBA-Auth-Key header or URL param. """ with TraceContext() as root: with root.span("ApiBaseController._validate_tba_auth_key"): x_tba_auth_key = self.request.headers.get("X-TBA-Auth-Key") if x_tba_auth_key is None: x_tba_auth_key = self.request.get('X-TBA-Auth-Key') self.auth_owner = None self.auth_owner_key = None self.auth_description = None if not x_tba_auth_key: account = self._user_bundle.account if account: self.auth_owner = account.key.id() self.auth_owner_key = account.key elif 'thebluealliance.com' in self.request.headers.get("Origin", ""): self.auth_owner = 'The Blue Alliance' else: self._errors = {"Error": "X-TBA-Auth-Key is a required header or URL param. Please get an access key at http://www.thebluealliance.com/account."} self.abort(401) if self.auth_owner: logging.info("Auth owner: {}, LOGGED IN".format(self.auth_owner)) else: auth = ApiAuthAccess.get_by_id(x_tba_auth_key) if auth and auth.is_read_key: self.auth_owner = auth.owner.id() self.auth_owner_key = auth.owner self.auth_description = auth.description if self.REQUIRE_ADMIN_AUTH and not auth.allow_admin: self._errors = {"Error": "X-TBA-Auth-Key does not have required permissions"} self.abort(401) logging.info("Auth owner: {}, X-TBA-Auth-Key: {}".format(self.auth_owner, x_tba_auth_key)) else: self._errors = {"Error": "X-TBA-Auth-Key is invalid. Please get an access key at http://www.thebluealliance.com/account."} self.abort(401)
def post(self, event_key): event_key = event_key.lower( ) # Normalize keys to lower case (TBA convention) # Make sure we are processing for a valid event first # (it's fine to do this before auth, since leaking the existence of an # event isn't really that big a deal) self.event = Event.get_by_id(event_key) if not self.event: self._errors = json.dumps( {"Error": "Event {} not found".format(event_key)}) self.abort(404) # Start by allowing admins to edit any event user_is_admin = (self._user_bundle.user and self._user_bundle.is_current_user_admin) # Also grant access if the user as the EVENTWIZARD permission and this # is a current year offseason event account = self._user_bundle.account current_year = datetime.datetime.now().year user_has_permission = (self.event.event_type_enum == EventType.OFFSEASON and self.event.year == current_year and account is not None and AccountPermissions.OFFSEASON_EVENTWIZARD in account.permissions) user_has_auth = (user_is_admin or user_has_permission) if not user_has_auth and self._user_bundle.user: # See if this user has any auth keys granted to its account now = datetime.datetime.now() auth_tokens = ApiAuthAccess.query( ApiAuthAccess.owner == account.key, ApiAuthAccess.event_list == ndb.Key(Event, event_key), ndb.OR(ApiAuthAccess.expiration == None, ApiAuthAccess.expiration >= now)).fetch() user_has_auth = any( self._validate_auth(auth, event_key) is None for auth in auth_tokens) if not user_has_auth: # If not, check if auth id/secret were passed as headers auth_id = self.request.headers.get('X-TBA-Auth-Id') if not auth_id: self._errors = json.dumps({ "Error": "Must provide a request header parameter 'X-TBA-Auth-Id'" }) self.abort(400) auth_sig = self.request.headers.get('X-TBA-Auth-Sig') if not auth_sig: self._errors = json.dumps({ "Error": "Must provide a request header parameter 'X-TBA-Auth-Sig'" }) self.abort(400) auth = ApiAuthAccess.get_by_id(auth_id) expected_sig = md5.new('{}{}{}'.format( auth.secret if auth else None, self.request.path, self.request.body)).hexdigest() if not auth or expected_sig != auth_sig: logging.info("Auth sig: {}, Expected sig: {}".format( auth_sig, expected_sig)) self._errors = json.dumps( {"Error": "Invalid X-TBA-Auth-Id and/or X-TBA-Auth-Sig!"}) self.abort(401) # Checks event key is valid, correct auth types, and expiration error = self._validate_auth(auth, event_key) if error: self._errors = json.dumps({"Error": error}) self.abort(401) try: self._process_request(self.request, event_key) except ParserInputException, e: self._errors = json.dumps({"Error": e.message}) self.abort(400)
def post(self, event_key): event_key = event_key.lower() # Normalize keys to lower case (TBA convention) # Make sure we are processing for a valid event first # (it's fine to do this before auth, since leaking the existence of an # event isn't really that big a deal) self.event = Event.get_by_id(event_key) if not self.event: self._errors = json.dumps({"Error": "Event {} not found".format(event_key)}) self.abort(404) # Start by allowing admins to edit any event user_is_admin = (self._user_bundle.user and self._user_bundle.is_current_user_admin) # Also grant access if the user as the EVENTWIZARD permission and this # is a current year offseason event account = self._user_bundle.account current_year = datetime.datetime.now().year user_has_permission = (self.event.event_type_enum == EventType.OFFSEASON and self.event.year == current_year and account is not None and AccountPermissions.OFFSEASON_EVENTWIZARD in account.permissions) user_has_auth = (user_is_admin or user_has_permission) if not user_has_auth and self._user_bundle.user: # See if this user has any auth keys granted to its account now = datetime.datetime.now() auth_tokens = ApiAuthAccess.query(ApiAuthAccess.owner == account.key, ApiAuthAccess.event_list == ndb.Key(Event, event_key), ndb.OR(ApiAuthAccess.expiration == None, ApiAuthAccess.expiration >= now)).fetch() user_has_auth = any(self._validate_auth(auth, event_key) is None for auth in auth_tokens) if not user_has_auth: # If not, check if auth id/secret were passed as headers auth_id = self.request.headers.get('X-TBA-Auth-Id') if not auth_id: self._errors = json.dumps({"Error": "Must provide a request header parameter 'X-TBA-Auth-Id'"}) self.abort(400) auth_sig = self.request.headers.get('X-TBA-Auth-Sig') if not auth_sig: self._errors = json.dumps({"Error": "Must provide a request header parameter 'X-TBA-Auth-Sig'"}) self.abort(400) auth = ApiAuthAccess.get_by_id(auth_id) expected_sig = md5.new('{}{}{}'.format(auth.secret if auth else None, self.request.path, self.request.body)).hexdigest() if not auth or expected_sig != auth_sig: logging.info("Auth sig: {}, Expected sig: {}".format(auth_sig, expected_sig)) self._errors = json.dumps({"Error": "Invalid X-TBA-Auth-Id and/or X-TBA-Auth-Sig!"}) self.abort(401) # Checks event key is valid, correct auth types, and expiration error = self._validate_auth(auth, event_key) if error: self._errors = json.dumps({"Error": error}) self.abort(401) try: self._process_request(self.request, event_key) except ParserInputException, e: self._errors = json.dumps({"Error": e.message}) self.abort(400)
def post(self, auth_id): self._require_admin() auth = ApiAuthAccess.get_by_id(auth_id) auth_types_enum = [ AuthType.READ_API ] if auth and AuthType.READ_API in auth.auth_types_enum else [] if self.request.get('allow_edit_teams'): auth_types_enum.append(AuthType.EVENT_TEAMS) if self.request.get('allow_edit_matches'): auth_types_enum.append(AuthType.EVENT_MATCHES) if self.request.get('allow_edit_rankings'): auth_types_enum.append(AuthType.EVENT_RANKINGS) if self.request.get('allow_edit_alliances'): auth_types_enum.append(AuthType.EVENT_ALLIANCES) if self.request.get('allow_edit_awards'): auth_types_enum.append(AuthType.EVENT_AWARDS) if self.request.get('allow_edit_match_video'): auth_types_enum.append(AuthType.MATCH_VIDEO) if self.request.get('allow_edit_info'): auth_types_enum.append(AuthType.EVENT_INFO) if self.request.get('allow_edit_zebra_motionworks'): auth_types_enum.append(AuthType.ZEBRA_MOTIONWORKS) if self.request.get('owner', None): owner = Account.query( Account.email == self.request.get('owner')).fetch() owner_key = owner[0].key if owner else None else: owner_key = None if self.request.get('expiration', None): expiration = datetime.strptime(self.request.get('expiration'), '%Y-%m-%d') else: expiration = None if self.request.get('event_list_str'): split_events = self.request.get('event_list_str', '').split(',') event_list = [ ndb.Key(Event, event_key.strip()) for event_key in split_events ] else: event_list = [] if not auth: auth = ApiAuthAccess( id=auth_id, description=self.request.get('description'), owner=owner_key, expiration=expiration, allow_admin=True if self.request.get('allow_admin') else False, secret=''.join( random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for _ in range(64)), event_list=event_list, auth_types_enum=auth_types_enum, ) else: auth.description = self.request.get('description') auth.event_list = event_list auth.auth_types_enum = auth_types_enum auth.owner = owner_key auth.expiration = expiration auth.allow_admin = True if self.request.get( 'allow_admin') else False auth.put() self.redirect("/admin/api_auth/manage")
def post(self, event_key): event_key = event_key.lower( ) # Normalize keys to lower case (TBA convention) if not (self._user_bundle.user and self._user_bundle.is_current_user_admin ): # Allow admins to use without auth keys auth_id = self.request.headers.get('X-TBA-Auth-Id') if not auth_id: self._errors = json.dumps({ "Error": "Must provide a request header parameter 'X-TBA-Auth-Id'" }) self.abort(400) auth_sig = self.request.headers.get('X-TBA-Auth-Sig') if not auth_sig: self._errors = json.dumps({ "Error": "Must provide a request header parameter 'X-TBA-Auth-Sig'" }) self.abort(400) auth = ApiAuthAccess.get_by_id(auth_id) expected_sig = md5.new('{}{}{}'.format( auth.secret if auth else None, self.request.path, self.request.body)).hexdigest() if not auth or expected_sig != auth_sig: logging.info("Auth sig: {}, Expected sig: {}".format( auth_sig, expected_sig)) self._errors = json.dumps( {"Error": "Invalid X-TBA-Auth-Id and/or X-TBA-Auth-Sig!"}) self.abort(401) allowed_event_keys = [ekey.id() for ekey in auth.event_list] if event_key not in allowed_event_keys: self._errors = json.dumps({ "Error": "Only allowed to edit events: {}".format( ', '.join(allowed_event_keys)) }) self.abort(401) missing_auths = self.REQUIRED_AUTH_TYPES.difference( set(auth.auth_types_enum)) if missing_auths != set(): self._errors = json.dumps({ "Error": "You do not have permission to edit: {}. If this is incorrect, please contact TBA admin." .format(",".join( [AuthType.type_names[ma] for ma in missing_auths])) }) self.abort(401) if auth.expiration and auth.expiration < datetime.datetime.now(): self._errors = json.dumps({ "Error": "These keys expired on {}. Contact TBA admin to make changes" .format(auth.expiration) }) self.abort(401) try: self._process_request(self.request, event_key) except ParserInputException, e: self._errors = json.dumps({"Error": e.message}) self.abort(400)