Merge branch 'permission-refactor' into 'master'

Permission refactor

Closes #115

See merge request litecord/litecord!68
This commit is contained in:
Luna 2020-07-29 21:30:38 +00:00
commit bdf7d875ba
7 changed files with 68 additions and 9 deletions

View File

@ -46,6 +46,7 @@ from litecord.embed.messages import process_url_embed, msg_update_embeds
from litecord.common.channels import channel_ack
from litecord.pubsub.user import dispatch_user
from litecord.permissions import get_permissions, Permissions
from litecord.errors import GuildNotFound
log = Logger(__name__)
bp = Blueprint("channels", __name__)
@ -397,13 +398,16 @@ async def _process_overwrites(guild_id: int, channel_id: int, overwrites: list)
if target.is_user:
perms = Permissions(overwrite["allow"] & ~overwrite["deny"])
assert target.user_id is not None
await _dispatch_action(guild_id, channel_id, target.user_id, perms)
elif target.is_role:
assert target.role_id is not None
user_ids.extend(await app.storage.get_role_members(target.role_id))
for user_id in user_ids:
perms = await get_permissions(user_id, channel_id)
assert target.user_id is not None
await _dispatch_action(guild_id, channel_id, target.user_id, perms)
@ -675,7 +679,12 @@ async def ack_channel(channel_id, message_id):
async def delete_read_state(channel_id):
"""Delete the read state of a channel."""
user_id = await token_check()
try:
await channel_check(user_id, channel_id)
except GuildNotFound:
# ignore when guild isn't found because we're deleting the
# read state regardless.
pass
await app.db.execute(
"""

View File

@ -23,6 +23,7 @@ from litecord.auth import token_check
from litecord.schemas import validate, USER_SETTINGS, GUILD_SETTINGS
from litecord.blueprints.checks import guild_check
from litecord.pubsub.user import dispatch_user
from litecord.errors import UserNotFound
bp = Blueprint("users_settings", __name__)
@ -129,6 +130,26 @@ async def patch_guild_settings(guild_id: int):
return jsonify(settings)
@bp.route("/@me/notes/<int:target_id>", methods=["GET"])
async def get_note(target_id: int):
"""Get a single note from a user."""
user_id = await token_check()
note = await app.db.fetchval(
"""
SELECT note
FROM notes
WHERE user_id = $1 AND target_id = $2
""",
user_id,
target_id,
)
if note is None:
raise UserNotFound()
return jsonify({"user_id": user_id, "note_user_id": target_id, "note": note})
@bp.route("/@me/notes/<int:target_id>", methods=["PUT"])
async def put_note(target_id: int):
"""Put a note to a user.

View File

@ -104,9 +104,7 @@ async def majority_region(user_id: int) -> Optional[str]:
return _majority_region_count(regions)
@bp.route("/regions", methods=["GET"])
async def voice_regions():
"""Return voice regions."""
async def _all_regions():
user_id = await token_check()
best_region = await majority_region(user_id)
@ -116,3 +114,16 @@ async def voice_regions():
region["optimal"] = region["id"] == best_region
return jsonify(regions)
@bp.route("/regions", methods=["GET"])
async def voice_regions():
"""Return voice regions."""
return await _all_regions()
@bp.route("/guilds/<int:guild_id>/regions", methods=["GET"])
async def guild_voice_regions():
"""Return voice regions."""
# we return the same list as the normal /regions route on purpose.
return await _all_regions()

View File

@ -142,6 +142,10 @@ class WebhookNotFound(NotFound):
error_code = 10015
class UserNotFound(NotFound):
error_code = 10013
class Ratelimited(LitecordError):
status_code = 429

View File

@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import ctypes
from typing import Optional
from typing import Optional, Union
from quart import current_app as app
@ -77,8 +77,10 @@ class Permissions(ctypes.Union):
_fields_ = [("bits", _RawPermsBits), ("binary", ctypes.c_uint64)]
def __init__(self, val: int):
self.binary = val
def __init__(self, val: Union[str, int]):
# always coerce to int, even when the user gives us a str, because
# python ints are infinity-sized (yes, yes, the memory concerns, yes)
self.binary = int(val)
def __repr__(self):
return f"<Permissions binary={self.binary}>"
@ -87,7 +89,7 @@ class Permissions(ctypes.Union):
return self.binary
ALL_PERMISSIONS = Permissions(0b01111111111101111111110111111111)
ALL_PERMISSIONS = Permissions(0b01111111111111111111111111111111)
EMPTY_PERMISSIONS = Permissions(0)

View File

@ -469,6 +469,10 @@ class Storage:
def _overwrite_convert(row):
drow = dict(row)
drow["allow_new"] = str(drow["allow"])
drow["deny_new"] = str(drow["deny"])
drow["allow"] = drow["allow"] & ((2 << 31) - 1)
drow["deny"] = drow["deny"] & ((2 << 31) - 1)
target_type = drow["target_type"]
drow["type"] = "member" if target_type == 0 else "role"
@ -673,7 +677,12 @@ class Storage:
if not row:
return None
return dict(row)
drow = dict(row)
drow["permissions_new"] = str(drow["permissions"])
drow["permissions"] = drow["permissions"] & ((2 << 31) - 1)
return drow
async def get_role_data(self, guild_id: int) -> List[Dict[str, Any]]:
"""Get role list information on a guild."""

View File

@ -0,0 +1,3 @@
-- channel_overwrites table already has allow and deny as bigints.
alter table roles
alter column permissions type bigint;