class Attachment(Resource): @spec('/attachments/<string:attachment_id>', "Delete Image Attachment", header_params=Swagger.Params.Authorization, responses=[ ApiResponse(200, "Succeed", dict(result=True)), ApiResponse.error(404, "Image Not Found"), ApiResponse.error(403, "Image Uploaded by another user"), ApiResponse.error(500, "s3 delete error. try again") ]) @check_auth def delete(self, attachment_id): img = ImageAttachmentModel.objects.with_id(attachment_id) if img is None: exceptionReport(g.user.user_db, get_path(), get_path_args(), get_args()) raise InvalidUsage("Image Not Found", 404) if img.user_id != g.user.user_db.user_id: exceptionReport(g.user.user_db, get_path(), get_path_args(), get_args()) raise InvalidUsage("Image Uploaded by another user", 403) try: s3.delete_object(Bucket=app.config['ATTACHMENT_S3_BUCKET'], Key=img.s3filename) except: exceptionReport(g.user.user_db, get_path(), get_path_args(), get_args()) raise InvalidUsage("s3 delete error. try again", 500) img.delete() return dict(result=True)
class KakaoSignup(Resource): @spec('/kakao/signup', 'New User Signup With Kakao', body_params=[ ApiParam('kakao_auth_token', 'auth token from kakao open sdk', required=True), ApiParam('email', 'user email', required=True), ApiParam('pwd', 'user password'), ApiParam('name', 'real name'), ApiParam('birthday', 'birthday', type='date'), ApiParam('gender', 'gender (male/female)', default='', constraints=[EnumConstraint(['', 'male', 'female'])]), ApiParam('place', 'office or home'), ApiParam('space', 'Where Arom will be installed'), ApiParam('purpose', 'Why use Arom'), ApiParam('prefer_scents', 'Prefer Scents', type='array', item=ApiParam("item", "item", type="string")) ], responses=[ ApiResponse(200, 'User Signup Succeed', dict(result=True)), ApiResponse( 500, 'User Signup Fail because of DB Issue', {'message': '[email protected] signup failed. Try again.'}), ApiResponse.error(403, 'Authorization Failed (from kakao server)'), ApiResponse.error(403, 'Already existing kakao user'), *signup_responses, ]) def post(self): args = get_args() kauth_token = args['kakao_auth_token'] resp = KakaoApi.user_info(kauth_token) if 'code' in resp: raise InvalidUsage("Authorization Failed", 403) kakao_id = str(resp['id']) properties = resp['properties'] profile_image = properties['profile_image'] nickname = properties['nickname'] thumbnail_image = properties['thumbnail_image'] if UserModel.objects(kakao_id=kakao_id).first() is not None: raise InvalidUsage("Already existing kakao user", 403) signup(args, random_pw=True, validate_pw=False, kakao_id=kakao_id) return {'result': True}
class FacebookSignup(Resource): @spec('/facebook/signup', 'New User Signup With Facebook', body_params=[ ApiParam('facebook_auth_token', 'auth token from facebook open sdk', required=True), ApiParam('email', 'user email', required=True), ApiParam('name', 'real name'), ApiParam('birthday', 'birthday', type='date'), ApiParam('gender', 'gender (male/female)', default='', constraints=[EnumConstraint(['', 'male', 'female'])]), ApiParam('place', 'office or home'), ApiParam('space', 'Where Arom will be installed'), ApiParam('purpose', 'Why use Arom'), ApiParam('prefer_scents', 'Prefer Scents', type='array', item=ApiParam("item", "item", type="string")) ], responses=[ ApiResponse(200, 'User Signup Succeed', dict(result=True)), ApiResponse( 500, 'User Signup Fail because of DB Issue', {'message': '[email protected] signup failed. Try again.'}), ApiResponse.error(403, 'Authorization Failed (from facebook server)'), ApiResponse.error(403, 'Already existing facebook user'), *signup_responses, ]) def post(self): args = get_args() fbauth_token = args['facebook_auth_token'] token_resp = FacebookApi.debug_token(fbauth_token) if 'error' in token_resp or not token_resp['data']['is_valid'] or \ token_resp['data']['app_id'] != app.config['FACEBOOK_APP_ID']: raise InvalidUsage("Authroization Failed", 403) facebook_id = str(token_resp['data']['user_id']) if UserModel.objects(facebook_id=facebook_id).first() is not None: raise InvalidUsage("Already existing facebook user", 403) signup(args, random_pw=True, validate_pw=False, facebook_id=facebook_id) return {'result': True}
class Login(Resource): @spec( '/auth/login', 'User Login', body_params=[ ApiParam('email', 'user email', required=True, constraints=[LengthConstraint(5, 100)]), ApiParam('pwd', 'user password', required=True, constraints=[LengthConstraint(4)]), ], responses=[ ApiResponse(200, 'Login Succeed', token_example), ApiResponse(403, 'User Login Fail because email not signed up', {'message': '[email protected] is not signed up user.'}), ApiResponse(400, 'User Login Fail because email/pwd invalid', {'message': 'Password is invalid.'}), ApiResponse.error(406, 'Expired Temporary Password') ]) def post(self): args = get_args() email = args['email'].lower() pwd = args['pwd'] user = User(email, pwd=pwd) if not user.is_user(): err = email + ' is not signed up user.' raise InvalidUsage(err, status_code=403) is_tmp_pwd = user.check_tmp_pwd(pwd) if not user.check_pwd(pwd) and not is_tmp_pwd: err = 'Password is invalid.' raise InvalidUsage(err, status_code=400) if is_tmp_pwd and user.is_expired_tmp_pwd(): err = 'Expired Temporary Password' raise InvalidUsage(err, status_code=406) auth_token, refresh_token, exp_time = user.generate_auth_token() user.authenticated = True result = { 'auth_token': auth_token, 'refresh_token': refresh_token, 'exp_time': exp_time } if is_tmp_pwd: result['used_tmp_pwd'] = is_tmp_pwd return result
class ResetPassword(Resource): @spec('/auth/reset_password', 'Request reset password mail', body_params=[ ApiParam('email', 'email to reset password', required=True) ], responses=[ ApiResponse(200, 'Email sent', dict(result=True)), ApiResponse.error(404, 'User not found'), ApiResponse.error(500, 'Email Server Error') ]) def post(self): args = get_args() user_model = UserModel.objects(email=args['email']).first() if user_model is None: raise InvalidUsage('User not found', 404) tmp_password = gen_pwd() body = render_template("pwd_reset_mail.template.html", name=user_model.name, password=tmp_password) try: charset = 'UTF-8' response = apitools.email_client.send_email( Destination={'ToAddresses': [user_model.email]}, Message=dict( Subject=dict(Data="[아롬] 임시 비밀번호 발급", Charset='utf8'), Body=dict(Html=dict(Charset='utf8', Data=str(body)))), Source=app.config['CONTACT_EMAIL']) except Exception as ex: raise InvalidUsage("Email Server Error: {}".format(ex), 500) user_model.tmp_password = hash_pwd(tmp_password) user_model.tmp_password_valid_period = datetime.datetime.now() + \ app.config['PASSWORD_RESET_EXPIRE_DURATION'] user_model.save() return dict(result=True)
class DeviceRegister(Resource): @spec('/devices/<string:device_id>/register', 'Register Device ID in User DB Document', header_params=[*Swagger.Params.Authorization], path_params=[ApiParam('device_id', 'Device ID')], responses=[ ApiResponse(200, 'Register Device Succeed', shadow_example), ApiResponse( 401, 'Unauthenticated Device', dict(message='This user is not owner of this device.')), ApiResponse.error(406, 'Expired Temporary Code'), ApiResponse.error(409, "Already Registered Device") ]) @check_auth def post(self, device_id): user_db = g.user.user_db user_id = user_db.user_id res = iot_client.get_thing_shadow(thingName=device_id) payload = json.loads(res['payload'].read()) state = payload['state'] reported = state.get('reported', {}) desired = state.get('desired', {}) if device_id in user_db.devices: exceptionReport(g.user.user_db, get_path(), get_path_args(), get_args()) raise InvalidUsage('Already Registered Device', 409) if user_id == reported['owner_id']: user_db.update(**{'set__devices__' + device_id: device_id}) user_db.reload() return state else: exceptionReport(g.user.user_db, get_path(), get_path_args(), get_args()) raise InvalidUsage('This user is not owner of this device.', status_code=401)
birthday=birthday, gender=gender, place=place, space=space, purpose=purpose, prefer_scents=prefer_scents).save() try: user = UserModel.objects.get(email=email) except UserModel.DoesNotExist: err = email + ' signup failed. Try again.' raise InvalidUsage(err, status_code=500) signup_responses = [ ApiResponse.error(400, "Password is required"), ApiResponse.error(400, "Email is required"), ApiResponse.error(400, "Password is not secure one"), ApiResponse.error(403, "[email protected] already exists"), ApiResponse.error(403, "Email is not valid") ] response_example = dict(user_id='User ID', email='User E-Mail', name='User Name', birthday='2017-12-31T23:59:59', gender='Female', picture='User Profile Picture img_src', devices={'Device ID': 'Device Name'})
class AttachmentList(Resource): @spec('/attachments', 'Get My Attachment List', header_params=Swagger.Params.Authorization, query_params=Swagger.Params.Page, responses=[ ApiResponse( 200, "Succeed", dict(attachments=[attachment_example], limit=20, total_size=100)) ]) @check_auth def get(self): args = get_args() offset = args['offset'] limit = args['limit'] user_id = g.user.user_db.user_id query = ImageAttachmentModel.objects(user_id=user_id) attachments = query[offset:offset + limit] return dict( attachments=seq(attachments).map(lambda x: x.marshall()).list(), limit=limit, total_size=len(query)) @spec('/attachments', 'Post Image Attachment', header_params=Swagger.Params.Authorization, body_type="data", body_params=[ApiParam("image", type="file", required=True)], responses=[ ApiResponse(200, "Succeed", attachment_example), ApiResponse.error(400, "Invalid Image Type"), ApiResponse.error(500, "s3 upload error. try again") ]) @check_auth def post(self): args = get_args() image = args['image'] if not image.content_type.startswith('image'): exceptionReport(g.user.user_db, get_path(), get_path_args(), get_args()) raise InvalidUsage("Invalid Image Type", 400) extension = image.filename.split('.')[-1] image_model = ImageAttachmentModel(user_id=g.user.user_db.user_id, extension=extension, orignal_name=image.filename) image_model.save() key = image_model.s3filename upload_config = TransferConfig(use_threads=False) try: result = s3.upload_fileobj(image, app.config['ATTACHMENT_S3_BUCKET'], key, ExtraArgs={ "ACL": "public-read", "ContentType": image.content_type }, Config=upload_config) except: image_model.delete() exceptionReport(g.user.user_db, get_path(), get_path_args(), get_args()) raise InvalidUsage("s3 upload error. try again", 500) return image_model.marshall()