From 6ff7de52add7e28650a1669fb21b5a1747b1749e Mon Sep 17 00:00:00 2001 From: subin <200516bb@gmail.com> Date: Tue, 25 Jul 2023 01:39:57 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20User=20LocalAuth=20=EB=B6=84=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=8F=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/user.py | 27 +++++----- app/decorators/login_required.py | 4 +- app/models/user.py | 2 - app/utils/user.py | 84 ++++++++++++++++++++++++-------- 4 files changed, 80 insertions(+), 37 deletions(-) diff --git a/app/api/user.py b/app/api/user.py index d2b1399..8fac3ea 100644 --- a/app/api/user.py +++ b/app/api/user.py @@ -6,7 +6,7 @@ import os import requests -from app.models.user import SocialAuth, User +from app.models.user import LocalAuth, SocialAuth, User from app.utils.user import UserDTO, UserService from app.decorators.login_required import login_required from app.utils.error_handler import InvalidTokenError @@ -74,14 +74,12 @@ def post(self): """ content = request.json user_dto = UserDTO( - uid=content['id'], - password=content['password'], name=content['name'], gender=content['gender'], birth=content['birth'], phone=content['phone'] ) - UserService.create_user(user_dto) + UserService.create_local_user(user_dto, content['id'], content['password']) return { 'message': '회원가입 되었습니다' }, 200 @@ -92,7 +90,7 @@ def get(self, id): """ IdDupCheck """ - dupUserId = User.query.filter_by(uid=id).first() + dupUserId = LocalAuth.query.filter_by(id=id).first() if dupUserId is None: return { 'dup': False }, 200 return { 'dup' : True }, 200 @@ -109,24 +107,24 @@ def post(self): """ content = request.json - u = User.query.filter_by(uid=content['id']).first() + u = LocalAuth.query.filter_by(id=content['id']).first() if u is None: return { 'message' : '아이디가 존재하지 않습니다.' }, 400 - if u.deleted_at is not None: + if u.user.deleted_at is not None: return { 'message' : '탈퇴한 회원입니다.' }, 400 if bcrypt.checkpw(content['password'].encode('UTF-8'), u.password.encode('UTF-8')): access_payload = { - 'id': str(u.id), + 'id': str(u.user_id), 'access_token_exp': (datetime.datetime.now() + datetime.timedelta(minutes=60*24)).isoformat() } access_token = jwt.encode(access_payload, os.getenv('SECRET_KEY'), algorithm="HS256") refresh_payload = { - 'id': str(u.id), + 'id': str(u.user_id), 'refresh_token_exp': (datetime.datetime.now() + datetime.timedelta(minutes=60*24*60)).isoformat() } refresh_token = jwt.encode(refresh_payload, os.getenv('SECRET_KEY'), algorithm="HS256") - return { 'access_token': access_token, 'refresh_token': refresh_token, 'user_id': u.uid }, 200 + return { 'access_token': access_token, 'refresh_token': refresh_token, 'user_id': u.id }, 200 else: return { 'message' : '비밀번호를 잘못 입력하였습니다.' }, 400 @@ -145,7 +143,8 @@ def post(self): if u is None: return { 'message' : '유저가 존재하지 않습니다.' }, 400 - return { 'message': u.uid }, 200 + user = LocalAuth.query.filter_by(user_id=u.id).first() + return { 'message': user.id }, 200 @user.route('/find/password', methods=['PUT']) class FindPassword(Resource): @@ -161,7 +160,7 @@ def put(self): if token is None: return { 'message' : 'token을 입력해주세요.' }, 400 - u = User.query.filter_by(reset_pw=token).first() + u = LocalAuth.query.join(User, User.id == LocalAuth.user_id).filter(User.reset_pw==token).first() if u is None: return { 'message' : '인증에 실패했습니다.' }, 400 @@ -180,7 +179,7 @@ def post(self): CheckInform """ content = request.json - u = User.query.filter_by(uid=content['id'], phone=content['phone']).first() + u = User.query.join(LocalAuth, LocalAuth.user_id == User.id).filter(LocalAuth.id == content['id'], User.phone == content['phone']).first() if u is None: return { 'message' : False }, 200 @@ -230,7 +229,7 @@ def post(self): CheckPassword """ content = request.json - user = g.user + user = g.local_auth if bcrypt.checkpw(content['password'].encode('UTF-8'), user.password.encode('UTF-8')): return { 'message' : True }, 200 else: diff --git a/app/decorators/login_required.py b/app/decorators/login_required.py index f5cb3e2..2bf71fb 100644 --- a/app/decorators/login_required.py +++ b/app/decorators/login_required.py @@ -3,7 +3,7 @@ import jwt import datetime from flask import g, request -from app.models.user import User +from app.models.user import LocalAuth, User from app.utils.error_handler import InvalidTokenError @@ -21,9 +21,11 @@ def decorated_function(*args, **kwargs): if datetime.datetime.fromisoformat(access_token_exp) < datetime.datetime.now(): raise InvalidTokenError("access token expired", 403, 403) u = User.query.filter_by(id=user_id).first() + local_auth = LocalAuth.query.filter_by(user_id=user_id).first() if u is not None and u.deleted_at is None: g.user_id = user_id g.user = u + g.local_auth = local_auth else: g.user = None raise InvalidTokenError("user not found") diff --git a/app/models/user.py b/app/models/user.py index 1d7c289..0837fce 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -5,8 +5,6 @@ class User(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - uid = db.Column(db.String(100), unique=True, nullable=False) - password = db.Column(db.String(200), nullable=False) name = db.Column(db.String(150), nullable=False) gender = db.Column(db.String(10), nullable=False) birth = db.Column(db.DateTime(), nullable=False) diff --git a/app/utils/user.py b/app/utils/user.py index 456cc72..2a187d8 100644 --- a/app/utils/user.py +++ b/app/utils/user.py @@ -4,7 +4,7 @@ from app.utils.error_handler import SignUpFail, UserFail import datetime from app.models import db -from app.models.user import SocialAuth, User, Notification, UserNotification +from app.models.user import LocalAuth, SocialAuth, User, Notification, UserNotification import bcrypt import re from flask import g @@ -12,26 +12,18 @@ @dataclass class UserDTO: id: Union[UUID, None] - uid: str - password: str name: str gender: str birth: datetime phone: str - def __init__(self, uid, password, name, gender, birth, phone, id=None): - self.uid = uid - self.password = password + def __init__(self, name, gender, birth, phone, id=None): self.name = name self.gender = gender self.birth = birth self.phone = phone self.id = id - if not uid: - raise SignUpFail("아이디는 필수 입력 항목입니다.") - if not password: - raise SignUpFail("비밀번호는 필수 입력 항목입니다.") if not name: raise SignUpFail("이름은 필수 입력 항목입니다.") if not gender: @@ -44,8 +36,6 @@ def __init__(self, uid, password, name, gender, birth, phone, id=None): def to_model(self) -> User: return User( id=self.id, - uid=self.uid, - password=self.password, name=self.name, gender=self.gender, birth=self.birth, @@ -102,7 +92,7 @@ def find_password(user, password): def update_password(password): - user = User.query.filter_by(id=g.user_id).first() + user = LocalAuth.query.filter_by(user_id=g.user_id).first() if user is None: raise UserFail("존재하지 않는 유저입니다.") @@ -120,11 +110,59 @@ def create_user(user) -> 'UserDTO': """ 새로운 유저를 생성합니다. """ + # uid_pattern = r'^[a-z0-9]{6,15}$' + # pw_pattern = r'^[a-zA-Z0-9!@#$%^&*()_+{}|:"<>?~\[\]\\;\',./]{8,16}$' + phone_pattern = r'^01([0|1|6|7|8|9])?([0-9]{3,4})?([0-9]{4})$' + # uid_reg = bool(re.match(uid_pattern, user.uid)) + # pw_reg = bool(re.match(pw_pattern, user.password)) + phone_reg = bool(re.match(phone_pattern, user.phone)) + # if not uid_reg: + # raise SignUpFail("아이디 형식이 잘못되었습니다. (6~15 영문소, 숫)") + # if not pw_reg: + # raise SignUpFail("비밀번호 형식이 잘못되었습니다. (8~16 영문대소, 숫, 특수)") + if not phone_reg: + raise SignUpFail("전화번호 형식이 잘못되었습니다. (01012345678 형식))") + + # dup_user_id = User.query.filter_by(uid=user.uid).first() + dup_phone = User.query.filter_by(phone=user.phone).first() + + # if dup_user_id is not None: + # raise SignUpFail("중복된 아이디가 존재합니다.") + if dup_phone is not None: + raise SignUpFail("중복된 전화번호가 존재합니다.") + + # new_password = bcrypt.hashpw(user.password.encode('UTF-8'), bcrypt.gensalt()) + + user_dto = UserDTO( + id=None, + name=user.name, + gender=user.gender, + birth=user.birth, + phone=user.phone + ) + user_dto.save() + + notifications = Notification.query.all() + for notification in notifications: + user_notification = UserNotification( + user_id=user_dto.id, + notification_id=notification.id, + is_enabled=True + ) + db.session.add(user_notification) + db.session.commit() + return user_dto + + + def create_local_user(user, id, password) -> 'UserDTO': + """ + 새로운 로컬 유저를 생성합니다. + """ uid_pattern = r'^[a-z0-9]{6,15}$' pw_pattern = r'^[a-zA-Z0-9!@#$%^&*()_+{}|:"<>?~\[\]\\;\',./]{8,16}$' phone_pattern = r'^01([0|1|6|7|8|9])?([0-9]{3,4})?([0-9]{4})$' - uid_reg = bool(re.match(uid_pattern, user.uid)) - pw_reg = bool(re.match(pw_pattern, user.password)) + uid_reg = bool(re.match(uid_pattern, id)) + pw_reg = bool(re.match(pw_pattern, password)) phone_reg = bool(re.match(phone_pattern, user.phone)) if not uid_reg: raise SignUpFail("아이디 형식이 잘못되었습니다. (6~15 영문소, 숫)") @@ -133,7 +171,7 @@ def create_user(user) -> 'UserDTO': if not phone_reg: raise SignUpFail("전화번호 형식이 잘못되었습니다. (01012345678 형식))") - dup_user_id = User.query.filter_by(uid=user.uid).first() + dup_user_id = LocalAuth.query.filter_by(id=id).first() dup_phone = User.query.filter_by(phone=user.phone).first() if dup_user_id is not None: @@ -141,12 +179,10 @@ def create_user(user) -> 'UserDTO': if dup_phone is not None: raise SignUpFail("중복된 전화번호가 존재합니다.") - new_password = bcrypt.hashpw(user.password.encode('UTF-8'), bcrypt.gensalt()) + new_password = bcrypt.hashpw(password.encode('UTF-8'), bcrypt.gensalt()) user_dto = UserDTO( id=None, - uid=user.uid, - password=new_password.decode('UTF-8'), name=user.name, gender=user.gender, birth=user.birth, @@ -154,6 +190,14 @@ def create_user(user) -> 'UserDTO': ) user_dto.save() + local_user = LocalAuth( + id=id, + user_id=user_dto.id, + password=new_password.decode('UTF-8'), + ) + db.session.add(local_user) + db.session.commit() + notifications = Notification.query.all() for notification in notifications: user_notification = UserNotification( @@ -165,7 +209,7 @@ def create_user(user) -> 'UserDTO': db.session.commit() return user_dto - + def create_social_auth(user_dto, content): social_user = SocialAuth( id=content['id'],