Skip to content

Commit f6f2fff

Browse files
authored
Merge pull request #308 from AlisProject/release/β0.36.1
Release/β0.36.1
2 parents fa265df + 4a15a62 commit f6f2fff

File tree

8 files changed

+412
-20
lines changed

8 files changed

+412
-20
lines changed

api-template.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,10 @@ Resources:
277277
type: string
278278
send_value:
279279
type: string
280+
access_token:
281+
type: string
282+
pin_code:
283+
type: string
280284
MeExternalProviderUserCreate:
281285
type: object
282286
properties:

src/common/lambda_base.py

+25-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import json
33
import logging
44
import traceback
5+
import copy
6+
import settings
57
from jsonschema import ValidationError
68
from record_not_found_error import RecordNotFoundError
79
from not_authorized_error import NotAuthorizedError
@@ -47,31 +49,31 @@ def main(self):
4749
return self.exec_main_proc()
4850
except ValidationError as err:
4951
logger.fatal(err)
50-
logger.info(self.event)
52+
logger.info(self.__filter_event_for_log(self.event))
5153

5254
return {
5355
'statusCode': 400,
5456
'body': json.dumps({'message': "Invalid parameter: {0}".format(err)})
5557
}
5658
except NotVerifiedUserError as err:
5759
logger.fatal(err)
58-
logger.info(self.event)
60+
logger.info(self.__filter_event_for_log(self.event))
5961

6062
return {
6163
'statusCode': 400,
6264
'body': json.dumps({'message': "Bad Request: {0}".format(err)})
6365
}
6466
except NotAuthorizedError as err:
6567
logger.fatal(err)
66-
logger.info(self.event)
68+
logger.info(self.__filter_event_for_log(self.event))
6769

6870
return {
6971
'statusCode': 403,
7072
'body': json.dumps({'message': str(err)})
7173
}
7274
except RecordNotFoundError as err:
7375
logger.fatal(err)
74-
logger.info(self.event)
76+
logger.info(self.__filter_event_for_log(self.event))
7577

7678
return {
7779
'statusCode': 404,
@@ -80,7 +82,7 @@ def main(self):
8082

8183
except Exception as err:
8284
logger.fatal(err)
83-
logger.info(self.event)
85+
logger.info(self.__filter_event_for_log(self.event))
8486
traceback.print_exc()
8587

8688
return {
@@ -120,3 +122,21 @@ def __get_headers(self):
120122
if self.event.get('headers') is not None:
121123
result.update(self.event.get('headers'))
122124
return result
125+
126+
def __filter_event_for_log(self, event):
127+
copied_event = copy.deepcopy(event)
128+
129+
if 'body' not in copied_event:
130+
return copied_event
131+
132+
try:
133+
body = json.loads(copied_event['body'])
134+
except Exception:
135+
return copied_event
136+
137+
for not_logging_param in settings.not_logging_parameters:
138+
if not_logging_param in body:
139+
body[not_logging_param] = 'xxxxx'
140+
copied_event['body'] = json.dumps(body)
141+
142+
return copied_event

src/common/settings.py

+9
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,18 @@
204204
'eth_address': {
205205
'type': 'string',
206206
'pattern': r'^0x[a-fA-F0-9]{40}$'
207+
},
208+
'access_token': {
209+
'type': 'string'
210+
},
211+
'pin_code': {
212+
'type': 'string'
207213
}
208214
}
209215

216+
# ログに出力されてはいけないパラメータ(ログ出力時に値がマスクされる)
217+
not_logging_parameters = {'access_token', 'pin_code'}
218+
210219
article_recent_default_limit = 20
211220
users_articles_public_default_limit = 10
212221
articles_popular_default_limit = 20

src/handlers/cognito_trigger/custommessage/custom_message.py

+18
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from jsonschema import validate, ValidationError
66
from lambda_base import LambdaBase
77
from user_util import UserUtil
8+
from private_chain_util import PrivateChainUtil
89

910

1011
class CustomMessage(LambdaBase):
@@ -36,6 +37,12 @@ def validate_params(self):
3637
for attribute in user['Attributes']:
3738
if attribute['Name'] == 'phone_number_verified' and attribute['Value'] == 'true':
3839
raise ValidationError('This phone_number is already exists')
40+
# セキュリティ観点より、電話番号変更を実行させない。
41+
# これにより XSS が発生したとしても、電話番号認証が必要な処理は回避が可能
42+
if self.event['triggerSource'] == 'CustomMessage_VerifyUserAttribute':
43+
# phone_number_verified が true の場合は電話番号変更を行っていないため当チェックは不要
44+
if params.get('phone_number_verified', '') != 'true':
45+
self.__validate_has_not_token(params)
3946

4047
def exec_main_proc(self):
4148
if self.event['triggerSource'] == 'CustomMessage_ForgotPassword':
@@ -73,3 +80,14 @@ def exec_main_proc(self):
7380
user=self.event['userName']
7481
).replace("\n", "<br />")
7582
return self.event
83+
84+
# トークンを保持していた場合は例外を出力
85+
def __validate_has_not_token(self, params):
86+
address = params.get('custom:private_eth_address')
87+
if address is None:
88+
raise ValidationError('Not exists private_eth_address. user_id: ' + self.event['userName'])
89+
url = 'https://' + os.environ['PRIVATE_CHAIN_EXECUTE_API_HOST'] + '/production/wallet/balance'
90+
payload = {'private_eth_address': address[2:]}
91+
token = PrivateChainUtil.send_transaction(request_url=url, payload_dict=payload)
92+
if token is not None and token != '0x0000000000000000000000000000000000000000000000000000000000000000':
93+
raise ValidationError("Do not allow phone number updates")

src/handlers/me/wallet/token/send/me_wallet_token_send.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from decimal import Decimal
77
from db_util import DBUtil
88
from boto3.dynamodb.conditions import Key
9+
from botocore.exceptions import ClientError
910
from private_chain_util import PrivateChainUtil
1011
from time_util import TimeUtil
1112
from jsonschema import validate
@@ -23,8 +24,10 @@ def get_schema(self):
2324
'properties': {
2425
'recipient_eth_address': settings.parameters['eth_address'],
2526
'send_value': settings.parameters['token_send_value'],
27+
'access_token': settings.parameters['access_token'],
28+
'pin_code': settings.parameters['pin_code']
2629
},
27-
'required': ['recipient_eth_address', 'send_value']
30+
'required': ['recipient_eth_address', 'send_value', 'access_token', 'pin_code']
2831
}
2932

3033
def validate_params(self):
@@ -35,6 +38,10 @@ def validate_params(self):
3538
self.params['send_value'] = int(self.params['send_value'])
3639
except ValueError:
3740
raise ValidationError('send_value must be numeric')
41+
42+
# pinコードを検証
43+
self.__validate_pin_code(self.params['access_token'], self.params['pin_code'])
44+
3845
validate(self.params, self.get_schema())
3946

4047
def exec_main_proc(self):
@@ -197,3 +204,26 @@ def __update_send_info_with_send_status(self, sort_key, user_id, send_status):
197204
':send_status': send_status,
198205
}
199206
)
207+
208+
def __validate_pin_code(self, access_token, pin_code):
209+
try:
210+
self.__verify_user_attribute(access_token, pin_code)
211+
except ClientError as client_error:
212+
code = client_error.response['Error']['Code']
213+
if code == 'NotAuthorizedException':
214+
raise ValidationError('Access token is invalid')
215+
elif code == 'CodeMismatchException':
216+
raise ValidationError('Pin code is invalid')
217+
elif code == 'ExpiredCodeException':
218+
raise ValidationError('Pin code is expired')
219+
elif code == 'LimitExceededException':
220+
raise ValidationError('Verification limit is exceeded')
221+
else:
222+
raise client_error
223+
224+
def __verify_user_attribute(self, access_token, pin_code):
225+
self.cognito.verify_user_attribute(
226+
AccessToken=access_token,
227+
AttributeName='phone_number',
228+
Code=pin_code
229+
)

tests/common/test_lambda_base.py

+49-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json
22
from tests_util import TestsUtil
33
from unittest import TestCase
4-
from unittest.mock import MagicMock
4+
from unittest.mock import patch, MagicMock
55
from jsonschema import ValidationError
66
from lambda_base import LambdaBase
77
from record_not_found_error import RecordNotFoundError
@@ -116,3 +116,51 @@ def test_get_headers_ok(self):
116116
'test_key2': 'test2'
117117
}
118118
self.assertEqual(expected_headers, lambda_impl.headers)
119+
120+
def test_filter_event_for_log_ok(self):
121+
with patch('settings.not_logging_parameters', {"not_logging_param_1", "not_logging_param_2"}), \
122+
patch('logging.Logger.info') as mock_logger_info:
123+
124+
event = {
125+
'body': '{"logging_param": "aaaaa", "not_logging_param_1": "bbbbb", "not_logging_param_2": "ccccc"}',
126+
'other_part': {}
127+
}
128+
lambda_impl = self.TestLambdaImpl(event, {})
129+
lambda_impl.exec_main_proc = MagicMock(side_effect=Exception())
130+
131+
lambda_impl.main()
132+
133+
mock_logger_info.assert_called_with({
134+
'body': '{"logging_param": "aaaaa", "not_logging_param_1": "xxxxx", "not_logging_param_2": "xxxxx"}',
135+
'other_part': {}
136+
})
137+
138+
def test_filter_event_for_log_ok_with_no_body(self):
139+
with patch('logging.Logger.info') as mock_logger_info:
140+
141+
event = {
142+
'other_part': {}
143+
}
144+
lambda_impl = self.TestLambdaImpl(event, {})
145+
lambda_impl.exec_main_proc = MagicMock(side_effect=Exception())
146+
147+
lambda_impl.main()
148+
149+
mock_logger_info.assert_called_with({
150+
'other_part': {}
151+
})
152+
153+
def test_filter_event_for_log_ok_with_invalid_body(self):
154+
with patch('logging.Logger.info') as mock_logger_info:
155+
156+
event = {
157+
'body': 'invalid body'
158+
}
159+
lambda_impl = self.TestLambdaImpl(event, {})
160+
lambda_impl.exec_main_proc = MagicMock(side_effect=Exception())
161+
162+
lambda_impl.main()
163+
164+
mock_logger_info.assert_called_with({
165+
'body': 'invalid body'
166+
})

0 commit comments

Comments
 (0)