Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
646673ba
Commit
646673ba
authored
Sep 16, 2013
by
David Ormsbee
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #994 from edx/ormsbee/ss_msg_signing2
Software Secure message signing pt. 2
parents
89a4737d
99841be6
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
79 additions
and
18 deletions
+79
-18
lms/djangoapps/verify_student/models.py
+22
-2
lms/djangoapps/verify_student/ssencrypt.py
+29
-10
lms/djangoapps/verify_student/views.py
+28
-6
No files found.
lms/djangoapps/verify_student/models.py
View file @
646673ba
...
...
@@ -356,6 +356,26 @@ class PhotoVerification(StatusModel):
self
.
status
=
"denied"
self
.
save
()
@status_before_must_be
(
"must_retry"
,
"submitted"
,
"approved"
,
"denied"
)
def
system_error
(
self
,
error_msg
,
error_code
=
""
,
reviewing_user
=
None
,
reviewing_service
=
""
):
"""
Mark that this attempt could not be completed because of a system error.
Status should be moved to `must_retry`.
"""
if
self
.
status
in
[
"approved"
,
"denied"
]:
return
# If we were already approved or denied, just leave it.
self
.
error_msg
=
error_msg
self
.
error_code
=
error_code
self
.
reviewing_user
=
reviewing_user
self
.
reviewing_service
=
reviewing_service
self
.
status
=
"must_retry"
self
.
save
()
class
SoftwareSecurePhotoVerification
(
PhotoVerification
):
"""
...
...
@@ -500,7 +520,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
header_txt
=
"
\n
"
.
join
(
"{}: {}"
.
format
(
h
,
v
)
for
h
,
v
in
sorted
(
headers
.
items
())
)
body_txt
=
json
.
dumps
(
body
,
indent
=
2
,
sort_keys
=
True
,
ensure_ascii
=
False
)
body_txt
=
json
.
dumps
(
body
,
indent
=
2
,
sort_keys
=
True
,
ensure_ascii
=
False
)
.
encode
(
'utf-8'
)
return
header_txt
+
"
\n\n
"
+
body_txt
...
...
@@ -509,7 +529,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
response
=
requests
.
post
(
settings
.
VERIFY_STUDENT
[
"SOFTWARE_SECURE"
][
"API_URL"
],
headers
=
headers
,
data
=
json
.
dumps
(
body
,
indent
=
2
,
sort_keys
=
True
,
ensure_ascii
=
False
)
data
=
json
.
dumps
(
body
,
indent
=
2
,
sort_keys
=
True
,
ensure_ascii
=
False
)
.
encode
(
'utf-8'
)
)
log
.
debug
(
"Sent request to Software Secure for {}"
.
format
(
self
.
receipt_id
))
log
.
debug
(
"Headers:
\n
{}
\n\n
"
.
format
(
headers
))
...
...
lms/djangoapps/verify_student/ssencrypt.py
View file @
646673ba
...
...
@@ -127,9 +127,7 @@ def generate_signed_message(method, headers_dict, body_dict, access_key, secret_
"""
Returns a (message, signature) pair.
"""
headers_str
=
"{}
\n\n
{}"
.
format
(
method
,
header_string
(
headers_dict
))
body_str
=
body_string
(
body_dict
)
message
=
headers_str
+
body_str
message
=
signing_format_message
(
method
,
headers_dict
,
body_dict
)
# hmac needs a byte string for it's starting key, can't be unicode.
hashed
=
hmac
.
new
(
secret_key
.
encode
(
'utf-8'
),
message
,
sha256
)
...
...
@@ -139,6 +137,18 @@ def generate_signed_message(method, headers_dict, body_dict, access_key, secret_
message
+=
'
\n
'
return
message
,
signature
,
authorization_header
def
signing_format_message
(
method
,
headers_dict
,
body_dict
):
"""
Given a dictionary of headers and a dictionary of the JSON for the body,
will return a str that represents the normalized version of this messsage
that will be used to generate a signature.
"""
headers_str
=
"{}
\n\n
{}"
.
format
(
method
,
header_string
(
headers_dict
))
body_str
=
body_string
(
body_dict
)
message
=
headers_str
+
body_str
return
message
def
header_string
(
headers_dict
):
"""Given a dictionary of headers, return a canonical string representation."""
header_list
=
[]
...
...
@@ -152,17 +162,26 @@ def header_string(headers_dict):
return
""
.
join
(
header_list
)
# Note that trailing \n's are important
def
body_string
(
body_dict
):
def
body_string
(
body_dict
,
prefix
=
""
):
"""
This version actually doesn't support nested lists and dicts. The code f
or
that was a little gnarly and we don't use that functionality, so there's no
real test for correctness
.
Return a canonical string representation of the body of a JSON request
or
response. This canonical representation will be used as an input to the
hashing used to generate a signature
.
"""
body_list
=
[]
for
key
,
value
in
sorted
(
body_dict
.
items
()):
if
value
is
None
:
value
=
"null"
body_list
.
append
(
u"{}:{}
\n
"
.
format
(
key
,
value
)
.
encode
(
'utf-8'
))
if
isinstance
(
value
,
(
list
,
tuple
)):
for
i
,
arr
in
enumerate
(
value
):
if
isinstance
(
arr
,
dict
):
body_list
.
append
(
body_string
(
arr
,
u"{}.{}."
.
format
(
key
,
i
)))
else
:
body_list
.
append
(
u"{}.{}:{}
\n
"
.
format
(
key
,
i
,
arr
)
.
encode
(
'utf-8'
))
elif
isinstance
(
value
,
dict
):
body_list
.
append
(
body_string
(
value
,
key
+
":"
))
else
:
if
value
is
None
:
value
=
"null"
body_list
.
append
(
u"{}{}:{}
\n
"
.
format
(
prefix
,
key
,
value
)
.
encode
(
'utf-8'
))
return
""
.
join
(
body_list
)
# Note that trailing \n's are important
lms/djangoapps/verify_student/views.py
View file @
646673ba
...
...
@@ -180,21 +180,43 @@ def results_callback(request):
settings
.
VERIFY_STUDENT
[
"SOFTWARE_SECURE"
][
"API_SECRET_KEY"
]
)
if
not
sig_valid
:
return
HttpResponseBadRequest
(
_
(
"Signature is invalid"
))
_
,
access_key_and_sig
=
headers
[
"Authorization"
]
.
split
(
" "
)
access_key
=
access_key_and_sig
.
split
(
":"
)[
0
]
# This is what we should be doing...
#if not sig_valid:
# return HttpResponseBadRequest("Signature is invalid")
# This is what we're doing until we can figure out why we disagree on sigs
if
access_key
!=
settings
.
VERIFY_STUDENT
[
"SOFTWARE_SECURE"
][
"API_ACCESS_KEY"
]:
return
HttpResponseBadRequest
(
"Access key invalid"
)
receipt_id
=
body_dict
.
get
(
"EdX-ID"
)
result
=
body_dict
.
get
(
"Result"
)
reason
=
body_dict
.
get
(
"Reason"
,
""
)
error_code
=
body_dict
.
get
(
"MessageType"
,
""
)
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
get
(
receipt_id
=
receipt_id
)
if
result
==
"PASSED"
:
try
:
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
get
(
receipt_id
=
receipt_id
)
except
SoftwareSecurePhotoVerification
.
DoesNotExist
:
log
.
error
(
"Software Secure posted back for receipt_id {}, but not found"
.
format
(
receipt_id
))
return
HttpResponseBadRequest
(
"edX ID {} not found"
.
format
(
receipt_id
))
if
result
==
"PASS"
:
log
.
debug
(
"Approving verification for {}"
.
format
(
receipt_id
))
attempt
.
approve
()
elif
result
==
"FAILED"
:
attempt
.
deny
(
reason
,
error_code
=
error_code
)
elif
result
==
"FAIL"
:
log
.
debug
(
"Denying verification for {}"
.
format
(
receipt_id
))
attempt
.
deny
(
json
.
dumps
(
reason
),
error_code
=
error_code
)
elif
result
==
"SYSTEM FAIL"
:
log
.
debug
(
"System failure for {} -- resetting to must_retry"
.
format
(
receipt_id
))
attempt
.
system_error
(
json
.
dumps
(
reason
),
error_code
=
error_code
)
log
.
error
(
"Software Secure callback attempt for
%
s failed:
%
s"
,
receipt_id
,
reason
)
else
:
log
.
error
(
"Software Secure returned unknown result {}"
.
format
(
result
))
return
HttpResponseBadRequest
(
"Result {} not understood. Known results: PASS, FAIL, SYSTEM FAIL"
.
format
(
result
)
)
return
HttpResponse
(
"OK!"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment