Exemple #1
0
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)
Exemple #2
0
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
Exemple #3
0
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}
Exemple #5
0
class RefreshAuthToken(Resource):
    @spec('/auth/refresh_token',
          'Refresh User Auth Token and User Refresh Token',
          body_params=[
              ApiParam('refresh_token',
                       'refresh token for user',
                       required=True)
          ],
          responses=[
              ApiResponse(200, 'Refresh Auth Token Success', token_example),
              ApiResponse(401, 'Refresh Auth Token Failed',
                          dict(message='Refresh Token failed'))
          ])
    def post(self):
        args = get_args()
        auth_token = request.headers.get('authorization')
        refresh_token = args['refresh_token']

        if auth_token and refresh_token:
            auth_token = auth_token.encode()
            refresh_token = refresh_token.encode()
            try:
                user_id = jwt.decode(auth_token,
                                     app.config['SECRET_KEY'],
                                     algorithms='HS512',
                                     options=dict(verify_exp=False))['sub']
            except jwt.InvalidTokenError:
                raise InvalidUsage('Auth Token is invalid.', status_code=401)

            try:
                user_db = UserModel.objects.get(user_id=user_id)
            except UserModel.DoesNotExist:
                raise InvalidUsage('This user does not exist.',
                                   status_code=401)

            if refresh_token == user_db.refresh_token.encode():
                user = User(user_db.email, user_id=user_id)
                auth_token, refresh_token, exp_time = user.generate_auth_token(
                )
                return {
                    'auth_token': auth_token,
                    'refresh_token': refresh_token,
                    'exp_time': exp_time
                }
            else:
                raise InvalidUsage('Refresh Token is invalid.',
                                   status_code=401)
        else:
            raise InvalidUsage('Token is not found.', status_code=401)
Exemple #6
0
class TokenValidation(Resource):
    @spec('/auth/tokenvalidate',
          'Check whether token is valid or not',
          header_params=[*Swagger.Params.Authorization],
          responses=[
              ApiResponse(200, 'Token Validation Success', dict(result=True)),
              ApiResponse(401, 'Token is not Valid ',
                          {'message': 'Auth Token is not valid.'}),
              ApiResponse(500, 'Tocken Validate Fail because of DB Issue',
                          {'message': 'Token does not deleted. Try Again.'}),
          ])
    @check_auth
    def get(self):

        return dict(result=True)
class FacebookLogin(Resource):
    @spec('/facebook/login',
          'Facebook User Login',
          body_params=[
              ApiParam('facebook_auth_token',
                       'auth token from facebook open sdk',
                       required=True)
          ],
          responses=[
              ApiResponse(
                  200, 'Login Succeed', {
                      'result': True,
                      'auth_token': 'Auth Token',
                      'refresh_token': 'Refresh Token',
                      'exp_time': '2017-12-31T23:59:59'
                  }),
              ApiResponse(
                  404, 'User Not Found. Facebook token is valid but no user.',
                  {'message': 'User Not Found'}),
              ApiResponse(403, 'Authorization Failed (from facebook server)',
                          {'message': 'Authorization Failed'}),
          ])
    def post(self):
        args = get_args()
        fbauth_token = args['facebook_auth_token']

        resp = FacebookApi.debug_token(fbauth_token)
        if 'error' in resp or not resp['data']['is_valid'] or \
                resp['data']['app_id'] != app.config['FACEBOOK_APP_ID']:
            raise InvalidUsage("Authroization Failed", 403)
        facebook_id = str(resp['data']['user_id'])
        try:
            user_model = UserModel.objects.get(facebook_id=facebook_id)
        except UserModel.DoesNotExist:
            raise InvalidUsage("User Not Found", 404)

        user = User()
        user.user_db = user_model
        auth_token, refresh_token, exp_time = user.generate_auth_token()
        user.authenticated = True

        return {
            'result': True,
            'auth_token': auth_token,
            'refresh_token': refresh_token,
            'exp_time': exp_time
        }
Exemple #8
0
class KakaoLogin(Resource):
    @spec('/kakao/login',
          'Kakao User Login',
          body_params=[
              ApiParam('kakao_auth_token',
                       'auth token from kakao open sdk',
                       required=True)
          ],
          responses=[
              ApiResponse(
                  200, 'Login Succeed', {
                      'result': True,
                      'auth_token': 'Auth Token',
                      'refresh_token': 'Refresh Token',
                      'exp_time': '2017-12-31T23:59:59'
                  }),
              ApiResponse(404,
                          'User Not Found. Kakao token is valid but no user.',
                          {'message': 'User Not Found'}),
              ApiResponse(403, 'Authorization Failed (from kakao server)',
                          {'message': 'Authorization Failed'}),
          ])
    def post(self):
        args = get_args()
        kauth_token = args['kakao_auth_token']

        resp = KakaoApi.access_token_info(kauth_token)
        if 'code' in resp:
            raise InvalidUsage("Authroization Failed", 403)
        kakao_id = str(resp['id'])
        try:
            user_model = UserModel.objects.get(kakao_id=kakao_id)
        except UserModel.DoesNotExist:
            raise InvalidUsage("User Not Found", 404)

        user = User()
        user.user_db = user_model
        auth_token, refresh_token, exp_time = user.generate_auth_token()
        user.authenticated = True

        return {
            'result': True,
            'auth_token': auth_token,
            'refresh_token': refresh_token,
            'exp_time': exp_time
        }
Exemple #9
0
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)
Exemple #11
0
class UserExists(Resource):
    @spec(
        '/user/exists',
        'Check whether user exists or not',
        query_params=[ApiParam('email', 'email to find user', required=True)],
        responses=[ApiResponse(200, 'User presence', dict(exists=True))])
    def get(self):
        args = get_args()
        user_model = UserModel.objects(email=args['email'].lower()).first()
        if user_model is None:
            return dict(exists=False)

        return dict(exists=True)
Exemple #12
0
class Signup(Resource):
    @spec('/auth/signup',
          'New User Signup',
          body_params=[
              ApiParam('email',
                       'user email',
                       required=True,
                       constraints=[LengthConstraint(5, 100)]),
              ApiParam('pwd',
                       'user password',
                       required=True,
                       constraints=[LengthConstraint(4)]),
              ApiParam('name', 'real name', constraints=[LengthConstraint(1)]),
              ApiParam('birthday', 'birthday', type='date-time'),
              ApiParam('gender',
                       'gender (male/female)',
                       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)),
              *signup_responses,
              ApiResponse(
                  500, 'User Signup Fail because of DB Issue',
                  {'message': '[email protected] signup failed. Try again.'})
          ])
    def post(self):
        args = get_args()

        signup(args, random_pw=False, validate_pw=not app.config['TESTING'])

        return dict(result=True)
Exemple #13
0
class Logout(Resource):
    @spec('/auth/logout',
          'User Logout',
          header_params=[*Swagger.Params.Authorization],
          responses=[
              ApiResponse(200, 'User Logout Success', dict(result=True)),
              ApiResponse(401, 'User Logout Fail because of Auth Issue',
                          {'message': 'Auth Token is not found.'}),
              ApiResponse(500, 'User Logout Fail because of DB Issue',
                          {'message': 'Token does not deleted. Try Again.'}),
          ])
    @check_auth
    def post(self):
        g.user.user_db.update(unset__auth_token=1)
        if g.user.user_db.access_token:
            g.user.user_db.update(unset__access_token=1)
        g.user.user_db.reload()
        if g.user.user_db.auth_token or g.user.user_db.access_token:
            raise InvalidUsage('Token does not deleted. Try Again.',
                               status_code=500)
        g.user.authenticated = False

        return dict(result=True)
Exemple #14
0
class UserInfo(Resource):
    @spec('/auth/user_info',
          'Get User Info',
          header_params=[*Swagger.Params.Authorization],
          query_params=[ApiParam('user_id', 'user_id to find user')],
          responses=[
              ApiResponse(200, 'User Information',
                          {'user_info': response_example}),
              ApiResponse(401, 'Get User Info Fail because of Auth Issue',
                          {'message': 'Auth Token is not found.'}),
              ApiResponse(404,
                          'Get User Info Fail because User Does Not Exist',
                          {'message': 'User does not exist.'}),
              ApiResponse(406, 'Get User Info Fail because of DB Issue',
                          {'message': 'User Info cannot be found. Try Again.'})
          ])
    @check_auth
    def get(self):
        args = get_args()
        if args.get('user_id', None):
            try:
                user_db = UserModel.objects.get(user_id=args.get('user_id'))
            except UserModel.doesNotExist:
                raise InvalidUsage('User does not exist.', status_code=404)

            return dict(
                user_info=dict(name=user_db.name, picture=user_db.picture))
        else:
            user = g.user.user_db
            return dict(user_info=user.marshall())

    @spec('/auth/user_info',
          'Update User Info',
          header_params=[*Swagger.Params.Authorization],
          body_params=[
              ApiParam('pwd',
                       'user password',
                       constraints=[LengthConstraint(4)]),
              ApiParam('name', 'real name', constraints=[LengthConstraint(1)]),
              ApiParam('birthday', 'birthday', type='date-time'),
              ApiParam('gender',
                       'gender (male/female)',
                       default='',
                       constraints=[EnumConstraint(['', 'male', 'female'])]),
              ApiParam('picture', 'profile image')
          ],
          responses=[
              ApiResponse(200, 'User Information',
                          {'user_info': response_example}),
              ApiResponse(401, 'Put User Info Fail because of Auth Issue',
                          {'message': 'Auth Token is not found.'}),
              ApiResponse(406, 'Put User Info Fail because of DB Issue',
                          {'message': 'User Info cannot be found. Try Again.'})
          ])
    @check_auth
    def put(self):
        args = get_args()
        user = g.user.user_db

        if args.get('pwd', None):
            user.password = hash_pwd(args.get('pwd'))
        if args.get('name', None):
            user.name = args.get('name')
        if args.get('birthday', None):
            user.birthday = arrow.get(args.get('birthday')).datetime
        if args.get('gender', None):
            user.gender = args.get('gender')
        if args.get('picture', None):
            user.picture = args.get('picture')

        user.save()

        return dict(user_info=user.marshall())
Exemple #15
0
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()
Exemple #16
0
              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 DeviceState(Resource):
    @spec('/devices/<string:device_id>/state',
          'Get Current State of Device',
          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.'))
          ])
    @check_device
    def get(self, device_id):
        payload = json.dumps(
            {'state': {
                'desired': {
                    'timestamp': int(time.time() * 1000)
                }
            }})

        res = iot_client.get_thing_shadow(thingName=device_id)
        iot_client.update_thing_shadow(thingName=device_id, payload=payload)
        payload = json.loads(res['payload'].read())

        state = payload['state']
        state['name'] = g.user.user_db.devices[device_id]

        return state

    @spec('/devices/<string:device_id>/state',
          'Update Current State of Device',
          header_params=[*Swagger.Params.Authorization],
          path_params=[ApiParam('device_id', 'Device ID')],
          body_name="Device States",
          body_params=[
              ApiParam('state',
                       'Device State Info',
                       "object",
                       properties=[
                           ApiParam("state1", type="boolean"),
                           ApiParam("state2", type="number"),
                           ApiParam("state3", type="string")
                       ])
          ],
          responses=[
              ApiResponse(200, 'Register Device Succeed', shadow_example),
              ApiResponse(
                  401, 'Unauthenticated Device',
                  dict(message='This user is not owner of this device.'))
          ])
    @check_device
    def post(self, device_id):
        args = get_args()
        desired = args['state']
        desired['timestamp'] = int(time.time() * 1000)

        if 'name' in desired:
            name = desired.pop('name')
            g.user.user_db.devices[device_id] = name
            g.user.user_db.save()

        payload = json.dumps({'state': {'desired': desired}})
        iot_client.update_thing_shadow(thingName=device_id, payload=payload)
        res = iot_client.get_thing_shadow(thingName=device_id)
        data = json.loads(res['payload'].read())
        state = data['state']
        state['name'] = g.user.user_db.devices[device_id]

        return state