Esempio n. 1
0
 def validate(self, data, typeof=dict, required=[]):
     if not isinstance(data, typeof):
         raise InvalidUsage(400)
     for key in required:
         if data.get(key) is None:
             raise InvalidUsage(400)
     return data
Esempio n. 2
0
    async def post(self, request):
        user = await self.server.tools.authorize(
            request.headers.get('Authorization'),
            permissions=['OWNER'],
            bots=False)

        data = self.validate(await request.json(), required=['name'])

        if len(data['name']) > 32:
            raise InvalidUsage(400, 'Bot name too long')

        connection = await self.server.database.acquire()
        try:
            async with connection.cursor() as cur:
                await cur.execute('SELECT * FROM `bots` WHERE `name` = %s',
                                  (data['name'], ))
                if await cur.fetchone():
                    raise InvalidUsage(400, 'Bot name in use')

                bid = self.server.snowflake.generate()
                token = self.server.token.generate(bid)

                await cur.execute(
                    'INSERT INTO `bots` (`id`, `name`, `permissions`, `snowflake`, `secret`) VALUES (%s, %s, %s, %s, %s)',
                    (bid, data['name'], 0, token['snowflake'],
                     token['secret']))
        finally:
            self.server.database.release(connection)

        return Response(200, {'token': token['token']})
    async def get(self, request, itype, sid):
        bot = await self.server.tools.authorize(
            request.headers.get('Authorization'),
            bot_permissions=['OWNER'],
            bots=True)

        if itype not in self.allowed:
            raise InvalidUsage(400, 'Invalid Id Type')

        itype = ThresholdIdTypes.get(itype.upper()[:-1])

        thresholds = {}
        connection = await self.server.database.acquire()
        try:
            async with connection.cursor() as cur:
                if await cur.execute(
                        'SELECT * FROM `thresholds` WHERE `id` = %s AND `id_type` = %s',
                    (sid, itype.value)):
                    found = await cur.fetchone()
                    thresholds.update({
                        k.value: found[k.value]
                        for k in PerspectiveAttributes
                    })
                else:
                    raise InvalidUsage(
                        404, 'Thresholds not found for this id and id type')
        finally:
            self.server.database.release(connection)

        return Response(200, thresholds)
Esempio n. 4
0
    async def dispatch(self, request: Request):
        method = self.methods.get(request.method.lower())
        if not method:
            raise InvalidUsage(405)

        args_wanted = list(inspect.signature(method).parameters.keys())
        args_available = request.match_info.copy()
        args_available.update({'request': request})

        args_unsatisfied = set(args_wanted) - set(args_available.keys())
        if args_unsatisfied:
            raise InvalidUsage(400)

        for arg, arg_type in self.types.items():
            if arg not in args_wanted:
                continue

            if arg_type == 'snowflake':
                if args_available[arg] in ['@me', '@all']:
                    continue
                try:
                    args_available[arg] = int(args_available[arg])
                except:
                    raise InvalidUsage(400,
                                       '{} is not a snowflake'.format(arg))

        return await method(
            **{arg: args_available[arg]
               for arg in args_wanted})
Esempio n. 5
0
 async def middleware_handler(request):
     try:
         return await handler(request)
     except web.HTTPException as e:
         return InvalidUsage(e.status)
     except InvalidUsage as e:
         return e
     except Exception as e:
         return InvalidUsage(500, str(e))
Esempio n. 6
0
    async def get(self, request, uid):
        user = await self.server.tools.authorize(
            request.headers.get('Authorization'), bot_permissions=['OWNER'])

        if uid == '@me':
            if user['bot']:
                if not user.get('user'):
                    raise InvalidUsage(
                        400,
                        'Cannot use @me with bots without specifying user id in the token'
                    )

                uid = user['user']['id']
            else:
                uid = user['id']

        if uid != user.get('user', user)['id'] and not Permissions.check_any(
                user.get('user', user)['permissions'],
            ['OWNER', 'SUPERADMIN', 'ADMIN']):
            raise InvalidUsage(401)

        requested = {}
        connection = await self.server.database.acquire()
        try:
            async with connection.cursor() as cur:
                if user['bot'] and user.get(
                        'user', None) and uid == user['user']['id']:
                    requested.update(user['user'])
                elif not user['bot'] and uid == user['id']:
                    requested.update({
                        'id': user['id'],
                        'username': user['username'],
                        'discriminator': user['discriminator'],
                        'avatar_hash': user['avatar_hash'],
                        'refreshed': user['refreshed'],
                        'permissions': user['permissions']
                    })
                else:
                    await cur.execute('SELECT * FROM `users` WHERE `id` = %s',
                                      (uid, ))
                    requested.update(await cur.fetchone())

                #update information from discord if not requested from bot
        finally:
            self.server.database.release(connection)

        if not requested:
            raise InvalidUsage(404, 'User not found')
        else:
            requested['discriminator'] = '{:04}'.format(
                requested['discriminator'])
            return Response(200, requested)
Esempio n. 7
0
    async def get(self, request, site):
        method = getattr(self, 'site_{}'.format(site))
        if not method:
            raise InvalidUsage(404, 'oauth2 type not supported')

        session = await self.server.router.get_session(request)
        return await method(request, session)
Esempio n. 8
0
    async def get(self, request):
        #implement using timestamp
        data = {}

        connection = await self.server.database.acquire()
        try:
            async with connection.cursor() as cur:
                await cur.execute(
                    ' '.join([
                        'SELECT', ', '.join([
                            '`count`', '`started`', ', '.join([
                                '`{}`'.format(attribute.value)
                                for attribute in PerspectiveAttributes
                            ])
                        ]), 'FROM `muck_averages` WHERE',
                        '`timestamp` = %s AND `context_type` = %s AND `context_id` = %s AND `user_id` = %s'
                    ]), [0, ContextTypes.GLOBAL.value, 0, 0])
                data['scores'] = await cur.fetchone()
                if not data['scores']:
                    raise InvalidUsage(404, 'No data found')

                data['count'] = data['scores'].pop('count')
                data['started'] = data['scores'].pop('started')
                for key in data['scores'].keys():
                    data['scores'][key] = round(
                        float(data['scores'][key]) / data['count'], 10)
        finally:
            self.server.database.release(connection)

        return Response(200, data)
Esempio n. 9
0
    async def site_discord(self, request):
        if not self.oauth2.get('discord'):
            raise InvalidUsage(500, 'Server missing Discord Oauth2 Config')

        return Redirect(302,
                        'https://discordapp.com/oauth2/authorize',
                        params={
                            'scope': 'bot',
                            'client_id': self.oauth2.get('discord').get('id')
                        })
    async def put(self, request, itype, sid):
        bot = await self.server.tools.authorize(
            request.headers.get('Authorization'),
            bot_permissions=['OWNER'],
            bots=True)

        if itype not in self.allowed:
            raise InvalidUsage(400, 'Invalid Id Type')

        itype = ThresholdIdTypes.get(itype.upper()[:-1])

        data = self.validate(await request.json())

        thresholds = {}
        for key in data:
            threshold = PerspectiveAttributes.get(key.upper())
            if threshold:
                thresholds[threshold.value] = data[key]
            else:
                raise InvalidUsage(400, 'Invalid Attribute: {}'.format(key))

        connection = await self.server.database.acquire()
        try:
            async with connection.cursor() as cur:
                ithresholds = thresholds.items()
                await cur.execute(
                    ' '.join([
                        'INSERT INTO `thresholds`',
                        '(`id`, `id_type`, {})'.format(', '.join([
                            '`{}`'.format(k) for k, v in ithresholds
                        ])), 'VALUES', '(%s, %s, {})'.format(', '.join([
                            '%s' for a in range(len(thresholds))
                        ])), 'ON DUPLICATE KEY UPDATE', ', '.join([
                            '`{k}` = ROUND(VALUES(`{k}`), 2)'.format(k=k)
                            for k, v in ithresholds
                        ])
                    ]), [sid, itype.value] + [v for k, v in ithresholds])
        finally:
            self.server.database.release(connection)

        return Response(204)
    async def get(self, request, site):
        if not self.oauth2.get('token_uri'):
            raise InvalidUsage(500, 'Site not configured properly')

        session = await self.server.router.get_session(request)
        method = getattr(self, 'site_{}'.format(site))
        if not method:
            raise InvalidUsage(404)

        user_id, state_id, connection = await method(request, session)
        state_token = self.server.token.generate(state_id)

        async with connection.cursor() as cur:
            await cur.execute(
                'INSERT INTO `token_states` (id, user_id, snowflake, secret) VALUES (%s, %s, %s, %s)',
                (state_id, user_id, state_token['snowflake'],
                 state_token['secret']))
        self.server.database.release(connection)

        return Redirect(302,
                        self.oauth2.get('token_uri'),
                        params={'token': state_token['token']})
Esempio n. 12
0
    async def post(self, request):
        try:
            data = self.validate(await request.json())
            token = self.server.token.split(data.get('token', None))
        except:
            raise InvalidUsage(400)

        connection = await self.server.database.acquire()
        try:
            async with connection.cursor() as cur:
                await cur.execute(
                    'SELECT * FROM `token_states` WHERE `id` = %s',
                    (token['user_id'], ))
                state = await cur.fetchone()
                if not state or not self.server.token.compare(
                        token['hmac'], state['snowflake'], state['secret']):
                    raise InvalidUsage(400)

                await cur.execute('SELECT `id` FROM `users` WHERE `id` = %s',
                                  state['user_id'])
                user = await cur.fetchone()
                if not user:
                    raise InvalidUsage(500, 'lol no user found')

                session_token = self.server.token.generate(user['id'])
                await cur.execute(
                    'INSERT INTO `token_sessions` (`id`, `user_id`, `secret`, `last_used`) VALUES (%s, %s, %s, %s)',
                    (session_token['snowflake'], user['id'],
                     session_token['secret'], 0))
                await cur.execute('DELETE FROM `token_states` WHERE `id` = %s',
                                  (token['user_id'], ))
        finally:
            self.server.database.release(connection)

        if session_token:
            return Response(200, {'token': session_token['token']})
        else:
            raise InvalidUsage(400)
Esempio n. 13
0
    async def site_discord(self, request, session):
        if not self.oauth2.get('discord'):
            raise InvalidUsage(500, 'Server missing Discord oauth2 config')

        params = {
            'scope': 'identify guilds',
            'response_type': 'code',
            'client_id': self.oauth2['discord'].get('id'),
            'state': self.server.snowflake.generate(),
            'redirect_uri': self.oauth2['discord'].get('redirect_uri')
        }

        session['state'] = params['state']

        return Redirect(302, 'https://discordapp.com/api/oauth2/authorize',
                        params)
Esempio n. 14
0
    async def get(self, request):
        user = await self.server.tools.authorize(
            request.headers.get('Authorization'))
        if user['bot']:
            raise InvalidUsage(401, 'Bots cannot use this endpoint.')

        connection = await self.server.database.acquire()
        try:
            async with connection.cursor() as cur:
                await cur.execute(
                    'DELETE FROM `token_sessions` WHERE `id` = %s',
                    (user['token']['snowflake'], ))
        finally:
            self.server.database.release(connection)

        return Response(204)
	async def get(self, request, itype, sid):
		if itype not in self.allowed_types:
			raise InvalidUsage(404)
		

		where = ['`timestamp` = %s']
		values = [0]

		if itype == 'guilds':
			where.append('`context_type` = %s')
			values.append(ContextTypes.GUILDS.value)
			
			where.append('`context_id` = %s')
			values.append(sid)
		elif itype == 'channels':
			where.append('`context_type` = %s')
			values.append(ContextTypes.CHANNELS.value)

			where.append('`context_id` = %s')
			values.append(sid)
		elif itype == 'users':
			where.append('`user_id` = %s')
			values.append(sid)

			context_id = request.query.get('context_id', None)
			context_type = request.query.get('context_type', None)

			if context_id is None or context_type is None:
				if context_id is None and context_type is None:
					context_id = 0
					context_type = ContextTypes.GLOBAL
				else:
					raise InvalidUsage(400, 'Context id and type cannot both be empty when one is passed in.')
			else:
				context_type = ContextTypes.get(context_type.upper())
				if not context_type:
					raise InvalidUsage(400, 'Invalid Context Type')
			
			where.append('`context_type` = %s')
			values.append(context_type.value)

			where.append('`context_id` = %s')
			values.append(context_id)
		
		response = {}
		connection = await self.server.database.acquire()
		try:
			async with connection.cursor() as cur:
				await cur.execute(
					' '.join([
						'SELECT',
						', '.join([
							'`count`',
							'`started`',
							', '.join([
								'`{}`'.format(attribute.value) for attribute in PerspectiveAttributes
							])
						]),
						'FROM `muck_averages` WHERE',
						' AND '.join(where)
					]),
					values
				)

				response['scores'] = await cur.fetchone()
				if not response['scores']:
					raise InvalidUsage(404, 'No data found')
				
				response['count'] = response['scores'].pop('count')
				response['started'] = response['scores'].pop('started')
				for key in response['scores'].keys():
					response['scores'][key] = round(float(response['scores'][key]) / response['count'], 10)
		finally:
			self.server.database.release(connection)
		
		return Response(200, response)
Esempio n. 16
0
 async def site_discord(self, request):
     if not self.oauth2.get('discord', None):
         raise InvalidUsage(500, 'Discord link not set up')
     return Redirect(302, self.oauth2['discord'].get('invite'))
Esempio n. 17
0
    async def site_github(self, request):
        if not self.oauth2.get('github', None):
            raise InvalidUsage(500, 'Github link not set up')

        return Redirect(302, self.oauth2['github'].get('url'))
Esempio n. 18
0
	async def authorize(self, token, permissions=[], bot_permissions=[], userandbot=True, bots=None):
		try:
			token = self.token.split(token)
		except Exception as e:
			raise InvalidUsage(401)
		
		response = {'bot': False, 'token': token}

		connection = await self.database.acquire()
		try:
			async with connection.cursor() as cur:
				if token['type'] == 'user':
					if bots is not None and bots:
						raise InvalidUsage(401, 'Only bots can use this endpoint.')
					await cur.execute('SELECT * FROM `users` WHERE `id` = %s', (token['user_id'],))
					user = await cur.fetchone()
					if not user:
						raise InvalidUsage(401)
					await cur.execute('SELECT `id`, `secret` FROM `token_sessions` WHERE `id` = %s', (token['snowflake'],))
					session = await cur.fetchone()
					if not session or not self.token.compare(token['hmac'], session['id'], session['secret']):
						raise InvalidUsage(401)

					await cur.execute('UPDATE `token_sessions` SET `last_used` = %s WHERE `id` = %s', (time.time(), session['id']))
					response.update(user)
				elif token['type'] == 'bot':
					if bots is not None and not bots:
						raise InvalidUsage(401, 'Bots cannot use this endpoint.')

					await cur.execute('SELECT * FROM `bots` WHERE `id` = %s', (token['bot_id'],))
					bot = await cur.fetchone()
					if not bot or not self.token.compare(token['hmac'], bot['snowflake'], bot['secret']):
						raise InvalidUsage(401)
	
					response.update({
						'bot': True,
						'id': bot['id'],
						'name': bot['name'],
						'permissions': bot['permissions']
					})
					if token.get('user_id'):
						await cur.execute('SELECT * FROM `users` WHERE `id` = %s', (token['user_id'],))
						user = await cur.fetchone()
						if not user:
							raise InvalidUsage(401, 'User not found')
						response.update({'user': user})
		finally:
			self.database.release(connection)

		if not response:
			raise InvalidUsage(401)
		
		if bot_permissions:
			if response['bot'] and not Permissions.check_any(response['permissions'], bot_permissions):
				raise InvalidUsage(401)
		
		if permissions and not (response['bot'] and not userandbot):
			if response['bot']:
				if not response.get('user'):
					raise InvalidUsage(401, 'Bots cannot use this endpoint')
				check = Permissions.check_any(response['user']['permissions'], permissions)
			else:
				check = Permissions.check_any(response['permissions'], permissions)

			if not check:
				raise InvalidUsage(401)
	
		return response
Esempio n. 19
0
 async def get(self, request, site):
     method = getattr(self, 'site_{}'.format(site))
     if not method:
         raise InvalidUsage(404)
     return await method(request)
Esempio n. 20
0
    async def post(self, request):
        bot = await self.server.tools.authorize(
            request.headers.get('Authorization'), bots=True)

        if not self.perspective:
            raise InvalidUsage(500, 'Server missing perspective API key')

        store = bool(request.query.get('store', None) == 'true')

        data = self.validate(await request.json(), required=['content'])
        data['guild_id'] = data.get('guild_id', None)
        data['channel_id'] = data.get('channel_id', None)
        if store:
            if not Permissions.check(bot['permissions'], 'OWNER'):
                raise InvalidUsage(401)
            data = self.validate(
                data,
                required=['message_id', 'channel_id', 'user_id', 'timestamp'])
            data['edited'] = bool(data.get('edited', False))
            try:
                data['message_id'] = int(data['message_id'])
                data['channel_id'] = int(data['channel_id'])
                data['user_id'] = int(data['user_id'])
                data['timestamp'] = int(data['timestamp'])
            except:
                raise InvalidUsage(400)

        try:
            data['guild_id'] = data['guild_id'] and int(data['guild_id'])
            data['channel_id'] = data['channel_id'] and int(data['channel_id'])
        except:
            raise InvalidUsage(400)

        if not data['content']:
            #if they send in a blank content lol
            raise InvalidUsage(400)

        data['content'] = data['content'].lower()
        mhash = hashlib.sha256(data['content'].encode()).hexdigest()

        response = {}
        connection = await self.server.database.acquire()
        try:
            async with connection.cursor() as cur:
                if store:
                    if await cur.execute(
                            'SELECT * FROM `muck_messages` WHERE `message_id` = %s AND `timestamp` = %s AND `edited` = %s',
                        (data['message_id'], data['timestamp'],
                         data['edited'])):
                        raise InvalidUsage(400,
                                           'Message already inside database.')

                await cur.execute(
                    'SELECT * FROM `muck_cache` WHERE `hash` = %s', (mhash, ))
                scores = await cur.fetchone()

                if not scores or scores['analyzed'] < int(
                        time.time() - self.max_cache_timestamp):
                    http = await self.server.httpclient.googleapi_perspective(
                        self.perspective['token'], data['content'],
                        self.attributes, True)
                    if http['status'] != 200:
                        if not http['json']:
                            raise InvalidUsage(
                                http['status'],
                                'Google\'s API errored out with this status.')
                        else:
                            raise InvalidUsage(
                                http['status'],
                                http['data']['error']['message'])
                    http = http['data']

                    scores = {'hash': mhash}
                    for key in http['attributeScores'].keys():
                        scores[key.lower()] = round(
                            http['attributeScores'][key]['summaryScore']
                            ['value'], 10)

                    scores['analyzed'] = int(time.time())

                    iscores = scores.items()
                    await cur.execute(
                        ' '.join([
                            'INSERT INTO `muck_cache` ',
                            '({}) '.format(', '.join([
                                '`{}`'.format(k) for k, v in iscores
                            ])), 'VALUES ',
                            '({})'.format(', '.join(['%s'
                                                     for k, v in iscores])),
                            'ON DUPLICATE KEY UPDATE', ', '.join([
                                '`{k}` = VALUES(`{k}`)'.format(k=k)
                                for k, v in iscores if k != 'hash'
                            ])
                        ]), [v for k, v in iscores])

                response['hash'] = scores.pop('hash', mhash)
                response['analyzed'] = scores.pop('analyzed')
                response['scores'] = scores

                if store:
                    await cur.execute(
                        'INSERT INTO `muck_messages` (`message_id`, `guild_id`, `channel_id`, `user_id`, `hash`, `timestamp`, `edited`) VALUES (%s, %s, %s, %s, %s, %s, %s)',
                        (data['message_id'], data['guild_id'],
                         data['channel_id'], data['user_id'], mhash,
                         data['timestamp'], data['edited']))

                    self.server.loop.create_task(
                        self.average(data['timestamp'], data['guild_id'],
                                     data['channel_id'], data['user_id'],
                                     scores))

                if not store:
                    for ttype in ['guild', 'channel']:
                        tid = data.get('{}_id'.format(ttype), None)
                        if not tid:
                            continue

                        if await cur.execute(
                                'SELECT * FROM `thresholds` WHERE `id` = %s AND `id_type` = %s',
                            (tid, ThresholdIdTypes.get(ttype.upper()).value)):
                            thresholds = await cur.fetchone()
                            response[ttype] = {
                                'id': tid,
                                'passed': False,
                                'thresholds': {}
                            }
                            for attribute in thresholds:
                                if not scores.get(attribute) or not thresholds[
                                        attribute]:
                                    continue

                                if scores[attribute] >= thresholds[attribute]:
                                    response[ttype]['passed'] = True
                                    response[ttype]['thresholds'][
                                        attribute] = thresholds[attribute]
        finally:
            self.server.database.release(connection)

        if store:
            return Response(204)
        else:
            return Response(200, response)
    async def site_discord(self, request, session):
        if not self.oauth2.get('discord'):
            raise InvalidUsage(500, 'Server missing Discord Oauth2 Config')

        server_state = session.get('state')
        if not server_state or not request.query.get('state') or int(
                server_state) != int(request.query.get('state')):
            raise InvalidUsage(400, 'Invalid State')

        if request.query.get('error'):
            raise InvalidUsage(400, request.query['error'])
        if not request.query.get('code'):
            raise InvalidUsage(400, 'Missing Code')

        response = await self.server.httpclient.discord_post_oauth2_token(
            client_id=self.oauth2['discord'].get('id'),
            client_secret=self.oauth2['discord'].get('secret'),
            grant_type='authorization_code',
            code=request.query.get('code'),
            redirect_uri=self.oauth2['discord'].get('redirect_uri'))

        if response['status'] != 200:
            if response['json']:
                raise InvalidUsage(response['status'],
                                   response['data']['error'])
            else:
                raise InvalidUsage(500, 'Discord API Error')
        response = response['data']

        scope = response.get('scope', '').split(' ')
        if 'identify' not in scope or 'guilds' not in scope:
            raise InvalidUsage(400, 'Missing Identify or Guilds in scope.')

        oauth2 = {
            'scope': response['scope'],
            'token_type': response['token_type'],
            'access_token': response['access_token'],
            'refresh_token': response['refresh_token'],
            'expires_in': response['expires_in'],
            'refreshed': int(time.time())
        }

        token = '{} {}'.format(oauth2['token_type'], oauth2['access_token'])

        response = await self.server.httpclient.discord_get_users_me(token)
        if response['status'] != 200:
            if response['json']:
                raise InvalidUsage(response['data']['code'],
                                   response['data']['message'])
            else:
                raise InvalidUsage(500, 'Discord API Error')
        response = response['data']

        user = {
            'id': response['id'],
            'username': response['username'],
            'discriminator': int(response['discriminator']),
            'avatar_hash': response['avatar'],
            'refreshed': int(time.time())
        }

        connection = await self.server.database.acquire()
        try:
            async with connection.cursor() as cur:
                if (await cur.execute('SELECT * FROM `users` WHERE `id` = %s',
                                      (user['id'], ))):
                    old = await cur.fetchone()

                    iuser = user.items()
                    await cur.execute(
                        'UPDATE `users` SET ' + '{} '.format(', '.join(
                            ['`{}` = {}'.format(k, '%s')
                             for k, v in iuser])) + 'WHERE `id` = %s',
                        [v for k, v in iuser] + [user['id']])

                    ioauth2 = oauth2.items()
                    await cur.execute(
                        'UPDATE `oauth2` SET ' + '{} '.format(', '.join(
                            ['`{}` = {}'.format(k, '%s')
                             for k, v in ioauth2])) + 'WHERE `user_id` = %s',
                        [v for k, v in ioauth2] + [user['id']])
                else:
                    oauth2['user_id'] = user['id']

                    iuser = user.items()
                    ioauth2 = oauth2.items()
                    await cur.execute(
                        'INSERT INTO `users` ' + '({}) '.format(', '.join(
                            ['`{}`'.format(k)
                             for k, v in iuser])) + 'VALUES ' +
                        '({})'.format(', '.join(['%s' for k, v in iuser])),
                        [v for k, v in iuser])
                    await cur.execute(
                        'INSERT INTO `oauth2` ' + '({}) '.format(', '.join(
                            ['`{}`'.format(k)
                             for k, v in ioauth2])) + 'VALUES ' +
                        '({})'.format(', '.join(['%s' for k, v in ioauth2])),
                        [v for k, v in ioauth2])
        except Exception as roof:
            self.server.database.release(connection)
            raise roof

        return (user['id'], server_state, connection)