Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
feudal
feudalBackend
Commits
c7cd871a
Commit
c7cd871a
authored
Jan 23, 2018
by
Lukas Burgey
Browse files
Rework error reporting of authentication process
parent
c3cd1719
Changes
5
Hide whitespace changes
Inline
Side-by-side
django_backend/backend/auth/v1/models.py
View file @
c7cd871a
from
...
import
models
from
django.db.utils
import
OperationalError
from
django.core.exceptions
import
ObjectDoesNotExist
from
django.db
import
models
as
db_models
from
oic.oic
import
Client
from
oic.utils.authn.client
import
CLIENT_AUTHN_METHOD
from
oic.oic.message
import
RegistrationResponse
from
oic.utils.authn.client
import
CLIENT_AUTHN_METHOD
from
urllib.request
import
Request
,
urlopen
from
...
import
models
from
django.core.exceptions
import
ObjectDoesNotExist
from
django.conf
import
settings
import
json
import
logging
logger
=
logging
.
getLogger
(
__name__
)
oidc_client
=
{}
...
...
@@ -60,15 +63,20 @@ class OIDCConfig(db_models.Model):
return
self
.
name
def
get_oidc_client
():
# TODO dubious
oidc_config
=
OIDCConfig
.
objects
.
all
()[
settings
.
OIDC_CONFIG_INDEX
]
return
oidc_config
.
oidc_client
def
default_idp
():
return
OIDCConfig
.
objects
.
filter
(
enabled
=
True
).
first
()
class
OIDCTokenAuthBackend
(
object
):
def
get_user_info
(
self
,
access_token
,
token_type
=
'Bearer'
):
q
=
Request
(
get_oidc_client
().
provider_info
[
'userinfo_endpoint'
])
def
get_user_info
(
self
,
request
,
access_token
,
token_type
=
'Bearer'
):
try
:
idp_id
=
request
.
session
[
'idp_id'
]
except
OperationalError
:
logger
.
error
(
'OperationalError: Unable to get from session'
)
raise
oidc_config
=
OIDCConfig
.
objects
.
get
(
id
=
idp_id
)
q
=
Request
(
oidc_config
.
oidc_client
.
provider_info
[
'userinfo_endpoint'
])
auth
=
(
token_type
+
' '
+
access_token
)
q
.
add_header
(
'Authorization'
,
auth
)
...
...
@@ -79,7 +87,7 @@ class OIDCTokenAuthBackend(object):
if
token
is
None
:
return
None
user_info
=
self
.
get_user_info
(
token
)
user_info
=
self
.
get_user_info
(
request
,
token
)
try
:
user
=
models
.
User
.
objects
.
get
(
sub
=
user_info
[
'sub'
])
...
...
django_backend/backend/auth/v1/urls.py
View file @
c7cd871a
from
django.conf.urls
import
url
from
.
import
views
from
...frontend
import
views
as
frontend_views
urlpatterns
=
[
url
(
r
'^info/'
,
views
.
AuthInfo
.
as_view
()),
url
(
r
'^request/'
,
views
.
Auth
.
as_view
()),
url
(
r
'^callback/'
,
views
.
AuthCallback
.
as_view
()),
url
(
r
'^logout/'
,
frontend_
views
.
LogoutView
.
as_view
()),
url
(
r
'^logout/'
,
views
.
LogoutView
.
as_view
()),
]
django_backend/backend/auth/v1/views.py
View file @
c7cd871a
from
django.contrib.auth
import
authenticate
,
login
from
django.contrib.auth
import
authenticate
,
login
,
logout
from
django.http
import
HttpResponse
,
HttpResponseServerError
from
django.db.utils
import
OperationalError
from
django.shortcuts
import
redirect
,
render
from
django.shortcuts
import
redirect
from
django.views
import
View
from
oic
import
rndstr
from
oic.oic.message
import
AuthorizationResponse
from
rest_framework.permissions
import
AllowAny
from
rest_framework
import
generics
from
rest_framework
import
generics
,
views
from
rest_framework.response
import
Response
import
json
from
.models
import
OIDCConfig
from
.models
import
OIDCConfig
,
default_idp
from
.serializers
import
AuthInfoSerializer
import
logging
logger
=
logging
.
getLogger
(
__name__
)
def
oidc_config_for_request
(
request
):
idp_id
=
request
.
GET
.
get
(
'idp'
,
None
)
if
idp_id
is
not
None
:
try
:
return
OIDCConfig
.
objects
.
get
(
id
=
idp_id
)
except
Exception
:
print
(
'No OIDCConfig with id {}'
.
format
(
idp_id
))
raise
def
idp_id_from_request
(
request
):
id
=
request
.
COOKIES
.
get
(
'idp_id'
,
None
)
if
id
is
not
None
:
return
id
else
:
return
OIDCConfig
.
objects
.
first
()
return
default_idp
().
id
class
AuthInfo
(
generics
.
RetrieveAPIView
):
permission_classes
=
(
AllowAny
,)
serializer_class
=
AuthInfoSerializer
def
get_session
(
request
,
key
,
default
):
try
:
value
=
request
.
session
.
get
(
key
,
None
)
except
OperationalError
:
logger
.
error
(
'Operational: Error getting from session'
)
raise
if
value
is
not
None
:
return
value
else
:
return
default
def
get_object
(
self
):
idps
=
OIDCConfig
.
objects
.
filter
(
enabled
=
True
)
return
{
'idps'
:
idps
,
'default'
:
idps
.
first
().
id
,
}
def
set_session
(
request
,
key
,
value
):
try
:
value
=
request
.
session
[
key
]
=
value
except
OperationalError
:
logger
.
error
(
'Operational: Error setting in session'
)
raise
class
Auth
(
View
):
def
get
(
self
,
request
,
**
kwargs
):
state
=
rndstr
()
try
:
request
.
session
[
'state'
]
=
state
# request.session['nonce'] = rndstr()
except
OperationalError
:
return
redirect
(
'/'
)
oidc_config
=
oidc_config_for_request
(
request
)
try
:
return
redirect
(
oidc_config
.
get_auth_request
(
oidc_config
.
oidc_client
,
state
))
except
Exception
:
print
(
'Malformed oidc config: {}'
.
format
(
oidc_config
))
state
=
rndstr
()
idp_id
=
idp_id_from_request
(
request
)
oidc_config
=
OIDCConfig
.
objects
.
get
(
id
=
idp_id
)
set_session
(
request
,
'state'
,
state
)
set_session
(
request
,
'idp_id'
,
idp_id
)
try
:
response
=
redirect
(
oidc_config
.
get_auth_request
(
oidc_config
.
oidc_client
,
state
))
return
response
except
Exception
:
logger
.
error
(
'Malformed oidc config: {}'
.
format
(
oidc_config
))
raise
except
Exception
as
e
:
logger
.
error
(
'request: {}'
.
format
(
e
))
# the error is deleted from the session when the state is delivered
request
.
session
[
'error'
]
=
'Server Error'
return
redirect
(
'/'
)
class
AuthCallback
(
View
):
def
get
(
self
,
request
,
**
kwargs
):
try
:
state
=
request
.
session
[
'state'
]
except
OperationalError
:
state
=
get_session
(
request
,
'state'
,
None
)
idp_id
=
get_session
(
request
,
'idp_id'
,
default_idp
().
id
)
oidc_config
=
OIDCConfig
.
objects
.
get
(
id
=
idp_id
)
aresp
=
oidc_config
.
oidc_client
.
parse_response
(
AuthorizationResponse
,
info
=
json
.
dumps
(
request
.
GET
))
if
not
state
==
aresp
[
'state'
]:
logger
.
error
(
'states do not match'
)
raise
Exception
(
'States do not match'
)
ac_token_response
=
(
oidc_config
.
oidc_client
.
do_access_token_request
(
state
=
aresp
[
'state'
],
request_args
=
{
'code'
:
aresp
[
'code'
]
},
)
)
# does fail with 'invalid_token'
# user_info = OIDC_CLIENT.do_user_info_request(
# statearesp['state'])
# user_info = self.get_user_info(ac_token_response['access_token'])
# try:
# u = models.User.objects.get(sub=user_info['sub'])
# except:
# u = models.user_from_user_info(user_info)
# u.save()
# response = render(request, 'backend/index.html', context)
user
=
authenticate
(
request
,
token
=
ac_token_response
[
'access_token'
])
if
user
is
None
:
# authentication failed -> 401
logger
.
error
(
'Login for a user failed'
)
response
=
HttpResponse
(
'Unauthorized'
,
status
=
401
)
else
:
# redirect back to the frontend
login
(
request
,
user
)
response
=
redirect
(
'/'
)
response
.
set_cookie
(
'sessionid'
,
request
.
COOKIES
[
'sessionid'
])
return
response
except
Exception
as
e
:
logger
.
error
(
'request: {}'
.
format
(
e
))
# the error is deleted from the session when the state is delivered
request
.
session
[
'error'
]
=
'Server Error'
return
redirect
(
'/'
)
oidc_config
=
OIDCConfig
.
objects
.
all
()[
0
]
oidc_client
=
oidc_config
.
oidc_client
aresp
=
oidc_client
.
parse_response
(
AuthorizationResponse
,
info
=
json
.
dumps
(
request
.
GET
))
assert
state
==
aresp
[
'state'
]
code
=
aresp
[
'code'
]
args
=
{
'code'
:
code
,
}
ac_token_response
=
oidc_client
.
do_access_token_request
(
state
=
aresp
[
'state'
],
request_args
=
args
)
# does fail with 'invalid_token'
# user_info = OIDC_CLIENT.do_user_info_request(
# statearesp['state'])
# user_info = self.get_user_info(ac_token_response['access_token'])
# try:
# u = models.User.objects.get(sub=user_info['sub'])
# except:
# u = models.user_from_user_info(user_info)
# u.save()
# response = render(request, 'backend/index.html', context)
user
=
authenticate
(
request
,
token
=
ac_token_response
[
'access_token'
])
if
user
is
None
:
# authentication failed -> 401
response
=
render
(
request
,
'backend/index.html'
,
status
=
401
)
else
:
# redirect back to the frontend
login
(
request
,
user
)
response
=
redirect
(
'/'
)
response
.
set_cookie
(
'sessionid'
,
request
.
COOKIES
[
'sessionid'
])
return
response
class
LogoutView
(
views
.
APIView
):
def
post
(
self
,
request
,
format
=
None
):
logout
(
request
)
return
Response
({})
class
AuthInfo
(
generics
.
RetrieveAPIView
):
permission_classes
=
(
AllowAny
,)
serializer_class
=
AuthInfoSerializer
def
get_object
(
self
):
idps
=
OIDCConfig
.
objects
.
filter
(
enabled
=
True
)
return
{
'idps'
:
idps
,
'default'
:
idps
.
first
().
id
,
}
django_backend/backend/frontend/views.py
View file @
c7cd871a
from
django.contrib.auth
import
logout
from
django.db
import
connections
from
django.db.utils
import
OperationalError
from
django.shortcuts
import
get_object_or_404
from
rest_framework
import
views
,
viewsets
from
rest_framework.permissions
import
AllowAny
from
rest_framework.authentication
import
TokenAuthentication
from
rest_framework.response
import
Response
from
rest_framework
import
status
...
...
@@ -23,6 +21,11 @@ def state_response(request):
response
[
'state'
]
=
serializers
.
StateSerializer
(
frontend_models
.
State
(
request
.
user
)).
data
if
'error'
in
request
.
session
:
response
[
'error'
]
=
request
.
session
[
'error'
]
# we display errors only once
del
request
.
session
[
'error'
]
return
Response
(
response
)
...
...
@@ -44,12 +47,6 @@ class OperationalView(views.APIView):
return
Response
(
response
)
class
LogoutView
(
views
.
APIView
):
def
post
(
self
,
request
,
format
=
None
):
logout
(
request
)
return
state_response
(
request
)
class
StateView
(
views
.
APIView
):
permission_classes
=
(
AllowAny
,)
...
...
django_backend/settings.py
View file @
c7cd871a
...
...
@@ -21,10 +21,6 @@ SESSION_COOKIE_AGE = 3600
CSRF_HEADER_NAME
=
'HTTP_X_CSRFTOKEN'
# index in the django_backend.backend.auth.models.OIDCConfig table
OIDC_CONFIG_INDEX
=
0
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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