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
7fd2ac35
Commit
7fd2ac35
authored
Jun 19, 2018
by
Lukas Burgey
Browse files
Refactor API towards webpage
parent
56172289
Changes
8
Hide whitespace changes
Inline
Side-by-side
django_backend/backend/auth/v1/models.py
View file @
7fd2ac35
...
...
@@ -96,7 +96,7 @@ class OIDCTokenAuthBackend(object):
LOGGER
.
error
(
"Invalid parameters for get_userinfo"
)
LOGGER
.
debug
(
"Got user info:
\n
%s
\n
"
,
user_info
)
#
LOGGER.debug("Got user info:\n%s\n", user_info)
return
user_info
def
authenticate
(
self
,
request
,
token
=
None
):
...
...
django_backend/backend/auth/v1/views.py
View file @
7fd2ac35
...
...
@@ -13,6 +13,8 @@ from .models import OIDCConfig, default_idp
from
.serializers
import
AuthInfoSerializer
from
.
import
utils
from
...frontend.views
import
state_view_data
LOGGER
=
logging
.
getLogger
(
__name__
)
IDP_COOKIE_NAME
=
'idp_id'
...
...
@@ -141,7 +143,7 @@ class LogoutView(views.APIView):
def
post
(
self
,
request
):
LOGGER
.
debug
(
'logged out %s'
,
request
.
user
)
logout
(
request
)
return
Response
(
{}
)
return
Response
(
state_view_data
(
request
)
)
class
AuthInfo
(
generics
.
RetrieveAPIView
):
...
...
django_backend/backend/clientapi/views.py
View file @
7fd2ac35
...
...
@@ -19,12 +19,7 @@ class DeploymentsView(generics.ListAPIView):
serializer_class
=
DeploymentStateSerializer
def
get_queryset
(
self
):
items
=
self
.
request
.
user
.
site
.
state_items
.
filter
(
state
=
'deployment_pending'
,
)
|
self
.
request
.
user
.
site
.
state_items
.
filter
(
state
=
'removal_pending'
,
)
return
[
item
.
tasks
for
item
in
items
]
return
self
.
request
.
user
.
site
.
pending_tasks
# the client has to fetch the configuration (like services etc.) here
...
...
@@ -56,11 +51,13 @@ class AckView(views.APIView):
item
.
client_deployed
()
else
:
item
.
client_removed
()
return
Response
({
'ok'
:
True
})
return
Response
({})
# this is no critical
LOGGER
.
info
(
'%s executed the obsolete state#%s'
,
request
.
user
,
state_id
)
return
Response
({
'ok'
:
True
})
return
Response
(
data
=
{
'error'
:
'obsolete_state'
},
status
=
500
,
)
class
ResponseView
(
views
.
APIView
):
...
...
django_backend/backend/frontend/serializers.py
View file @
7fd2ac35
...
...
@@ -17,31 +17,39 @@ class ServiceSerializer(serializers.ModelSerializer):
class
Meta
:
model
=
models
.
Service
exclude
=
[]
fields
=
[
'id'
,
'name'
,
'site'
,
'groups'
,
]
class
DeploymentStateItemSerializer
(
serializers
.
ModelSerializer
):
service
=
ServiceSerializer
()
key
=
backend_serializers
.
SSHPublicKeySerializer
B
()
key
=
backend_serializers
.
SSHPublicKey
Ref
Serializer
()
site
=
SiteSerializer
()
questionnaire
=
serializers
.
JSONField
()
credentials
=
serializers
.
JSONField
()
class
Meta
:
model
=
models
.
DeploymentStateItem
fields
=
[
'state_target'
,
'key'
,
'service'
,
'id'
,
'site'
,
'state'
,
'questionnaire'
,
'id'
,
'credentials'
,
'key'
,
'service'
,
]
class
DeploymentStateSerializer
(
serializers
.
ModelSerializer
):
key
=
backend_serializers
.
SSHPublicKeySerializer
B
()
key
=
backend_serializers
.
SSHPublicKey
Ref
Serializer
()
service
=
ServiceSerializer
()
state_items
=
DeploymentStateItemSerializer
(
many
=
True
)
class
Meta
:
model
=
models
.
DeploymentState
fields
=
[
...
...
@@ -49,60 +57,55 @@ class DeploymentStateSerializer(serializers.ModelSerializer):
'key'
,
'service'
,
'id'
,
'state_items'
,
]
class
DeploymentSerializer
(
serializers
.
Serializer
):
class
DeploymentSerializer
(
serializers
.
Model
Serializer
):
service
=
ServiceSerializer
()
ssh_keys
=
backend_serializers
.
SSHPublicKeySerializer
(
many
=
True
)
ssh_keys_to_withdraw
=
backend_serializers
.
SSHPublicKeySerializer
(
many
=
True
)
# deploys = DeploymentStateSerializer(many=True)
# removals = DeploymentStateSerializer(many=True)
class
Meta
:
model
=
models
.
Deployment
exclude
=
[
'user'
]
class
DeploymentSerializerB
(
serializers
.
Serializer
):
service
=
ServiceSerializer
()
states
=
DeploymentStateSerializer
(
many
=
True
)
class
Meta
:
model
=
models
.
Deployment
fields
=
[
'service'
,
'ssh_keys'
,
'ssh_keys_to_withdraw'
,
'states'
,
'id'
,
]
# contains properties which change less often
class
UserSerializer
(
serializers
.
ModelSerializer
):
auth_groups
=
backend_serializers
.
AuthGroupSerializer
(
many
=
True
)
groups
=
backend_serializers
.
GroupSerializer
(
many
=
True
)
ssh_keys
=
backend_serializers
.
SSHPublicKeySerializer
(
many
=
True
)
deployments
=
DeploymentSerializer
(
many
=
True
)
auth_groups
=
backend_serializers
.
AuthGroupSerializer
(
many
=
True
)
deployment_states
=
DeploymentStateSerializer
(
many
=
True
)
deployment_state_items
=
DeploymentStateItemSerializer
(
many
=
True
)
class
Meta
:
model
=
models
.
User
fields
=
[
'
id
'
,
'
auth_groups
'
,
'email'
,
'userinfo'
,
'ssh_keys'
,
'groups'
,
'deployments'
,
'auth_groups'
,
'deployment_states'
,
'deployment_state_items'
,
'id'
,
'ssh_keys'
,
'userinfo'
,
]
class
ClientSerializer
(
serializers
.
HyperlinkedModelSerializer
):
# contains properties which change a lot
class
UserStateSerializer
(
serializers
.
ModelSerializer
):
deployment_state_items
=
DeploymentStateItemSerializer
(
many
=
True
)
deployment_states
=
DeploymentStateSerializer
(
many
=
True
)
deployments
=
DeploymentSerializer
(
many
=
True
)
class
Meta
:
model
=
models
.
User
fields
=
[
'name'
,
'site'
]
class
StateSerializer
(
serializers
.
Serializer
):
services
=
ServiceSerializer
(
many
=
True
)
class
ClientViewSerializer
(
serializers
.
Serializer
):
deployments
=
serializers
.
JSONField
()
fields
=
[
'deployment_state_items'
,
'deployment_states'
,
'deployments'
,
]
django_backend/backend/frontend/test_views.py
View file @
7fd2ac35
...
...
@@ -25,5 +25,7 @@ class ViewTest(TestCase):
response
=
client
.
get
(
'/backend/api/state/'
,
)
self
.
assertTrue
(
response
.
json
()[
'logged_in'
])
self
.
assertTrue
(
'services'
in
response
.
json
())
self
.
assertTrue
(
'user_state'
in
response
.
json
())
self
.
assertTrue
(
'user'
in
response
.
json
())
self
.
assertEqual
(
response
.
json
()[
'user'
][
'userinfo'
][
'email'
],
test_models
.
TEST_EMAIL
)
django_backend/backend/frontend/views.py
View file @
7fd2ac35
...
...
@@ -2,7 +2,7 @@ import logging
from
django.shortcuts
import
get_object_or_404
from
django.core.exceptions
import
ObjectDoesNotExist
from
django.contrib.auth
import
logout
from
rest_framework
import
views
,
viewsets
from
rest_framework
import
views
from
rest_framework.permissions
import
AllowAny
from
rest_framework.response
import
Response
from
rest_framework
import
status
...
...
@@ -22,30 +22,27 @@ def user_services(user):
return
[]
def
_api_error_response
():
return
Response
({
'
ok'
:
False
},
status
=
status
.
HTTP_400_BAD_REQUEST
)
def
_api_error_response
(
error
):
return
Response
({
'
error'
:
error
},
status
=
status
.
HTTP_400_BAD_REQUEST
)
def
user_state_dict
(
user
):
return
serializers
.
UserSerializer
(
user
).
data
def
user_state
(
user
):
return
serializers
.
UserStateSerializer
(
user
,
).
data
def
_api_state_response_data
(
request
):
return
{
'user_state'
:
user_state
(
request
.
user
),
}
# Response for StateView, LogoutView, and all post requests
def
_api_state_response
(
request
):
if
not
request
.
user
.
is_authenticated
:
return
Response
(
{
'logged_in'
:
False
,
}
)
return
Response
({
'user_state'
:
None
,
})
response
=
{
'logged_in'
:
True
,
'user'
:
user_state_dict
(
request
.
user
),
'services'
:
serializers
.
ServiceSerializer
(
user_services
(
request
.
user
),
many
=
True
,
).
data
,
}
response
=
_api_state_response_data
(
request
)
if
'error'
in
request
.
session
:
response
[
'error'
]
=
request
.
session
[
'error'
]
...
...
@@ -54,18 +51,29 @@ def _api_state_response(request):
return
Response
(
response
)
def
state_view_data
(
request
):
data
=
{}
if
request
.
user
.
is_authenticated
:
data
=
_api_state_response_data
(
request
)
data
[
'user'
]
=
serializers
.
UserSerializer
(
request
.
user
,
).
data
data
[
'services'
]
=
serializers
.
ServiceSerializer
(
user_services
(
request
.
user
),
many
=
True
,
).
data
else
:
data
[
'user'
]
=
None
data
[
'user_state'
]
=
None
data
[
'services'
]
=
[]
return
data
class
StateView
(
views
.
APIView
):
permission_classes
=
(
AllowAny
,)
def
get
(
self
,
request
):
return
_api_state_response
(
request
)
# pylint: disable=too-many-ancestors
class
ServiceViewSet
(
viewsets
.
ModelViewSet
):
serializer_class
=
serializers
.
ServiceSerializer
queryset
=
models
.
Service
.
objects
.
all
()
return
Response
(
state_view_data
(
request
))
class
SSHPublicKeyView
(
views
.
APIView
):
...
...
@@ -92,7 +100,7 @@ class SSHPublicKeyView(views.APIView):
return
_api_state_response
(
request
)
LOGGER
.
error
(
'SSHPublicKeyView: malformed request %s'
,
request
)
return
_api_error_response
()
return
_api_error_response
(
"malformed request"
)
class
DeploymentView
(
views
.
APIView
):
...
...
@@ -103,7 +111,9 @@ class DeploymentView(views.APIView):
'service'
not
in
request
.
data
):
LOGGER
.
error
(
'Deployment api got invalid request %s'
,
request
.
data
)
return
_api_error_response
()
return
_api_error_response
(
"request misses fields (should have: 'type', 'key', and 'service')"
)
request_type
=
request
.
data
[
'type'
]
request_service
=
models
.
Service
.
objects
.
get
(
...
...
@@ -123,19 +133,23 @@ class DeploymentView(views.APIView):
elif
request_type
==
'remove'
:
deployment
.
remove_key
(
request_key
)
else
:
return
_api_error_response
()
return
_api_error_response
(
"invalid value of field 'type'"
)
deployment
.
save
()
return
_api_state_response
(
request
)
return
Response
(
serializers
.
DeploymentSerializer
(
deployment
).
data
)
class
QuestionnaireView
(
views
.
APIView
):
def
post
(
self
,
request
):
state_item_id
=
request
.
query_params
.
get
(
'id'
,
''
)
if
state_item_id
!=
''
:
item
=
models
.
DeploymentStateItem
.
objects
.
filter
(
id
=
int
(
state_item_id
))
item
=
models
.
DeploymentStateItem
.
objects
.
filter
(
id
=
int
(
state_item_id
),
)
if
item
.
exists
():
item
.
first
().
questionnaire
_answer
ed
(
item
.
first
().
user
_answer
s
(
answers
=
request
.
data
,
)
else
:
...
...
django_backend/backend/models.py
View file @
7fd2ac35
...
...
@@ -196,9 +196,7 @@ class RabbitMQInstance(SingletonModel):
msg
,
)
def
publish_to_webpage
(
self
,
user
,
msg
):
# noise
# LOGGER.debug('Signalling webpage of user %s', user)
def
publish_to_user
(
self
,
user
,
msg
):
self
.
_publish
(
'update'
,
str
(
user
.
id
),
...
...
@@ -421,6 +419,10 @@ class Site(models.Model):
blank
=
True
,
)
@
property
def
pending_tasks
(
self
):
return
[
item
.
parent
for
item
in
self
.
state_items
.
all
()]
def
__str__
(
self
):
return
self
.
name
...
...
@@ -484,7 +486,7 @@ class SSHPublicKey(models.Model):
# the receiver 'delete_withdrawn_ssh_key' does the actual deletion
def
delete_key
(
self
):
# if this key is not deployed anywhere we delete it now
if
analyze_states
(
self
.
states
)
==
'not_deployed'
:
if
analyze_states
(
self
.
states
.
all
()
)
==
'not_deployed'
:
LOGGER
.
info
(
self
.
msg
(
'Direct deletion of key'
))
self
.
delete
()
return
...
...
@@ -641,7 +643,7 @@ class Deployment(models.Model):
return
'{}:{}'
.
format
(
self
.
service
,
self
.
user
)
def
msg
(
self
,
msg
):
return
'[Depl
oyment
:{}] {}'
.
format
(
self
,
msg
)
return
'[Depl
.m
:{}] {}'
.
format
(
self
,
msg
)
# DeploymentState: knows:
...
...
@@ -728,14 +730,14 @@ class DeploymentState(models.Model):
for
item
in
self
.
state_items
.
all
():
item
.
user_deploy
()
self
.
publish_to_client
()
self
.
publish_to_
user
()
# each state item publishes its state to the
user
def
remove
(
self
):
self
.
_set_target
(
'not_deployed'
)
for
item
in
self
.
state_items
.
all
():
item
.
user_remove
()
self
.
publish_to_client
()
self
.
publish_to_
user
()
# each state item publishes its state to the
user
def
publish_to_client
(
self
):
# mitigating circular dependencies here
...
...
@@ -748,11 +750,11 @@ class DeploymentState(models.Model):
)
# update the state of the remote webpage
def
publish_to_user
(
self
):
from
.frontend.views
import
user_state
_dict
from
.frontend.views
import
user_state
content
=
{
'user_state'
:
user_state
_dict
(
self
.
user
),
'user_state'
:
user_state
(
self
.
user
),
}
RabbitMQInstance
.
load
().
publish_to_
webpage
(
RabbitMQInstance
.
load
().
publish_to_
user
(
self
.
user
,
content
,
)
...
...
@@ -766,8 +768,9 @@ class DeploymentState(models.Model):
self
.
save
()
def
__str__
(
self
):
return
"{}#{}"
.
format
(
return
"{}
:{}
#{}"
.
format
(
self
.
deployment
.
service
,
self
.
key
,
self
.
id
,
)
...
...
@@ -822,26 +825,15 @@ class DeploymentStateItem(models.Model):
def
key
(
self
):
return
self
.
parent
.
key
@
property
def
state_target
(
self
):
if
(
self
.
state
==
'deployment_pending'
or
self
.
state
==
'questionnaire'
):
return
'deploy'
elif
self
.
state
==
'removal_pending'
:
return
'withdraw'
# no state_target is executed
return
'none'
# STATE transitions
# user: deployment requested
def
user_deploy
(
self
):
if
self
.
state
==
'removal_pending'
:
self
.
_set_state
(
'deployed'
)
return
if
self
.
state
==
'deployed'
:
LOGGER
.
info
(
self
.
msg
(
'ignoring invalid state transition user_deploy'
))
return
self
.
_set_state
(
'deployment_pending'
)
...
...
@@ -854,12 +846,17 @@ class DeploymentStateItem(models.Model):
self
.
_set_state
(
'not_deployed'
)
return
if
self
.
state
==
'not_deployed'
:
LOGGER
.
info
(
self
.
msg
(
'ignoring invalid state transition user_remove'
))
return
self
.
_set_state
(
'removal_pending'
)
# user: questionnaire answered
def
user_answers
(
self
,
answers
=
None
):
self
.
questionnaire
=
answers
self
.
_set_state
(
'deployment_pending'
)
self
.
parent
.
publish_to_client
()
# client: deployed
def
client_deployed
(
self
,
credentials
=
None
):
...
...
@@ -875,18 +872,6 @@ class DeploymentStateItem(models.Model):
self
.
questionnaire
=
questionnaire
self
.
_set_state
(
'questionnaire'
)
# only used when we got a questionnaire_answered
def
publish
(
self
):
# mitigating circular dependencies here
from
.clientapi.serializers
import
DeploymentStateSerializer
data
=
DeploymentStateSerializer
(
self
.
state
).
data
data
[
'questionnaire'
]
=
self
.
questionnaire
RabbitMQInstance
.
load
().
publish_by_site
(
self
.
site
,
json
.
dumps
(
data
),
)
def
msg
(
self
,
msg
):
return
'[DSItem:{}] {}'
.
format
(
self
,
msg
)
...
...
@@ -898,8 +883,9 @@ class DeploymentStateItem(models.Model):
def
__str__
(
self
):
return
"{}@{}#{}"
.
format
(
return
"{}
:{}
@{}#{}"
.
format
(
self
.
parent
.
service
,
self
.
parent
.
key
,
self
.
site
,
self
.
id
,
)
...
...
django_backend/backend/serializers.py
View file @
7fd2ac35
...
...
@@ -24,7 +24,7 @@ class SSHPublicKeySerializer(serializers.ModelSerializer):
fields
=
[
'id'
,
'name'
,
'key'
]
class
SSHPublicKeySerializer
B
(
serializers
.
ModelSerializer
):
class
SSHPublicKey
Ref
Serializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
SSHPublicKey
fields
=
[
'id'
,
'name'
]
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