diff --git a/mitama/app/app.py b/mitama/app/app.py index 773783f..63ab663 100644 --- a/mitama/app/app.py +++ b/mitama/app/app.py @@ -54,7 +54,7 @@ def __call__(self, request): def set_middleware(self, middlewares): self.app.middlewares.extend(middlewares) def convert_fullurl(self, req, url): - scheme= req.scheme + scheme = req.scheme hostname = req.host path = self.path if path[0] != '/': diff --git a/mitama/command/run.py b/mitama/command/run.py index 3edef45..3259b5d 100755 --- a/mitama/command/run.py +++ b/mitama/command/run.py @@ -11,6 +11,7 @@ class Command: def handle(self, argv = None): + config = get_from_project_dir() try: port = argv[0] except IndexError: @@ -18,7 +19,6 @@ def handle(self, argv = None): port = config.port else: port = '8080' - config = get_from_project_dir() if not hasattr(config, 'ssl'): config.ssl = False app_registry = AppRegistry() diff --git a/mitama/http/__init__.py b/mitama/http/__init__.py index 34f3fef..d08c560 100644 --- a/mitama/http/__init__.py +++ b/mitama/http/__init__.py @@ -1,6 +1,7 @@ import base64, re import ssl -from http.server import ThreadingHTTPServer +#from http.server import ThreadingHTTPServer +from http.server import HTTPServer from socketserver import StreamRequestHandler from .request import Request from .response import Response @@ -19,12 +20,14 @@ def handle(self): response = app(request) response.start(request, self.wfile) def serve(): - with ThreadingHTTPServer(('', int(port)), RequestHandler) as server: + #with ThreadingHTTPServer(('', int(port)), RequestHandler) as server: + with HTTPServer(('', int(port)), RequestHandler) as server: server.serve_forever() th_nossl = threading.Thread(name = 'nossl', target = serve) if ssl != False: def ssl_serve(): - with ThreadingHTTPServer(('', int(ssl['port'])), SSLRequestHandler) as server: + #with ThreadingHTTPServer(('', int(ssl['port'])), SSLRequestHandler) as server: + with HTTPServer(('', int(ssl['port'])), SSLRequestHandler) as server: if ssl!=False: ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) crtx.load_cert_chain(ssl['cert'], ssl['key']) diff --git a/mitama/http/request.py b/mitama/http/request.py index bd299ff..9f92a94 100644 --- a/mitama/http/request.py +++ b/mitama/http/request.py @@ -1,6 +1,7 @@ import cgi import http import http.cookies +import io from urllib.parse import parse_qs from yarl import URL @@ -21,13 +22,27 @@ def get(self, key): class _RequestPayload(): def __init__(self, field_storage): self._field_storage = field_storage + def __contains__(self, key): + if key not in self._field_storage: + return False + elif isinstance(self._field_storage[key].file, io.BytesIO): + return (self._field_storage[key].filename or '') != '' + else: + return len(self._field_storage[key].value) > 0 def __getitem__(self, key): if key not in self._field_storage: raise KeyError - elif self._field_storage[key].file is None: + elif (self._field_storage[key].filename or '') != '': + return self._field_storage[key] + elif len(self._field_storage[key].value) > 0: return self._field_storage[key].value else: - return self._field_storage[key] + raise KeyError + def get(self, key, default = None): + if key in self: + return self[key] + else: + return default @classmethod def parse_body(cls, rfile, content_type, length): environ = { @@ -43,7 +58,7 @@ def parse_body(cls, rfile, content_type, length): class Request(): MessageClass = http.client.HTTPMessage - def __init__(self, method, path, version, headers, rfile): + def __init__(self, method, path, version, headers, ssl, rfile): self._method = method self._raw_path = path self._url = URL(path) @@ -60,9 +75,11 @@ def __init__(self, method, path, version, headers, rfile): self._query[k] = v else: self._path = path[0] - self._query = None + self._query = {} self._version = version self._headers = headers + self._secure = ssl + self._host = self._headers.get('Host') self._rfile = rfile self._data = dict() def __setitem__(self, key, value): @@ -74,6 +91,12 @@ def __delitem__(self, key): def get(self, key): return self._data[key] if key in self._data else None @property + def scheme(self): + return 'https' if self._secure else 'http' + @property + def host(self): + return self._host + @property def method(self): return self._method @property @@ -106,9 +129,9 @@ def cookies(self): self._cookies = _Cookies(C) return self._cookies def post(self): - content_type = self._headers.get('Content-Type') - length = int(self._headers.get('Content-Length')) - if content_type in ('multipart/form-data', 'application/x-www-form-urlencoded'): + content_type = self._headers.get('Content-Type', '') + length = int(self._headers.get('Content-Length', 0)) + if content_type.startswith('multipart/form-data') or content_type.startswith('application/x-www-form-urlencoded'): parsed_body = _RequestPayload.parse_body(self._rfile, content_type, length) elif content_type == 'application/json': payload = self._rfile.read() @@ -128,7 +151,7 @@ def session(self): self['mitama_session'] = sess return sess @classmethod - def parse_stream(cls, rfile): + def parse_stream(cls, rfile, ssl = False): words = rfile.readline().decode().rstrip('\r\n').split(' ') version = words[-1] if not 2 <= len(words) <= 3: @@ -144,4 +167,4 @@ def parse_stream(cls, rfile): raise Exception('') except http.client.HTTPException as err: raise Exception('') - return cls(command, path, version, headers, rfile) + return cls(command, path, version, headers, ssl, rfile) diff --git a/mitama/permission.py b/mitama/permission.py index e0bb6ef..a5177f1 100644 --- a/mitama/permission.py +++ b/mitama/permission.py @@ -3,6 +3,7 @@ from mitama.nodes import User, Group, Relation from mitama.db.types import Column, Integer, Node from sqlalchemy.ext.declarative import declared_attr +from sqlalchemy.schema import UniqueConstraint class PermissionMixin(object): '''パーミッションのモデルの実装を支援します @@ -34,6 +35,13 @@ class SomePermission(PermissionMixin, db.Model): @declared_attr def __tablename__(cls): return '__'+cls.__name__.lower()+'_permission' + @declared_attr + def __table_args__(cls): + if hasattr(cls, 'target'): + unique = UniqueConstraint('node', 'target', name='unique') + else: + unique = UniqueConstraint('node', name='unique') + return (unique, ) node = Column(Node) targetUpPropagate = False targetDownPropagate = False diff --git a/mitama/portal/controller.py b/mitama/portal/controller.py index 893fe25..1026a45 100644 --- a/mitama/portal/controller.py +++ b/mitama/portal/controller.py @@ -52,14 +52,15 @@ def signup(self, request): user = User() user.password = password_hash(data['password']) if invite.editable: - user.screen_name = data['screen_name'] - user.name = data['name'] + user.screen_name = data.get('screen_name', '') + user.name = data.get('name', '') user.icon = data['icon'].file.read() if "icon" in data else invite.icon else: user.screen_name = invite.screen_name user.name = invite.name user.icon = invite.icon user.create() + invite.delete() UpdateUserPermission.accept(user, user) sess["jwt_token"] = get_jwt(user) return Response.redirect( @@ -69,10 +70,10 @@ def signup(self, request): error = str(err) return Response.render(template, { 'error': error, - "name": data["name"], - "screen_name": data["screen_name"], - "password": data["password"], - "icon": data["icon"].file.read(), + "name": data.get("name", invite.name), + "screen_name": data.get("screen_name", invite.screen_name), + "password": data.get("password", ''), + "icon": data.get("icon").file.read(), 'editable': invite.editable }) return Response.render(template, { @@ -130,8 +131,8 @@ def create(self, req): try: icon = post["icon"].file.read() if "icon" in post else load_noimage_user() invite = Invite() - invite.name = post['name'] - invite.screen_name = post['screen_name'] + invite.name = post.get('name', '') + invite.screen_name = post.get('screen_name', '') invite.icon = icon invite.token = str(uuid4()) invite.editable = 'editable' in post @@ -145,8 +146,8 @@ def create(self, req): error = str(err) return Response.render(template, { 'invites': invites, - "name": post["name"], - "screen_name": post["screen_name"], + "name": post.get("name", ''), + "screen_name": post.get("screen_name", ''), "icon": icon, 'error': error }) @@ -221,8 +222,8 @@ def update(self, req): return Response.render(template, { "error": error, "user": user, - "screen_name": post["screen_name"], - "name": post["name"], + "screen_name": post.get("screen_name", user.screen_name), + "name": post.get("name", user.name), "icon": icon, }) return Response.render(template, { @@ -258,16 +259,19 @@ def create(self, req): group.screen_name = post['screen_name'] group.icon = post['icon'].file.read() if "icon" in post else None group.create() + print('created') if "parent" in post and post['parent'] != '': Group.retrieve(int(post['parent'])).append(group) group.append(req.user) + print('append') UpdateGroupPermission.accept(req.user, group) + print('accept') return Response.redirect(self.app.convert_url("/groups")) except Exception as err: error = str(err) return Response.render(template, { 'groups': groups, - "icon": post["icon"].file.read() if "icon" in post else None, + "icon": load_noimage_group(), 'error': error }) return Response.render(template, { @@ -347,9 +351,9 @@ def update(self, req): 'all_groups': groups, 'all_users': users, "group": group, - "screen_name": post["screen_name"], - "name": post["name"], - "icon": icon, + "screen_name": post.get("screen_name", ''), + "name": post.get("name", ''), + "icon": group.icon, }) return Response.render(template, { "group": group, diff --git a/mitama/portal/templates/signup.html b/mitama/portal/templates/signup.html index 0318b71..7b412c0 100644 --- a/mitama/portal/templates/signup.html +++ b/mitama/portal/templates/signup.html @@ -6,12 +6,12 @@ {% block content %}