Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
CIRCLE
/
cloud
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
94
Merge Requests
10
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
A prog2-höz tartozó friss repo anyagok itt elérhetőek:
https://git.iit.bme.hu/
Commit
e4141022
authored
Jun 06, 2014
by
Őry Máté
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/feature-new-saml-group'
parents
a499689c
94e3de4b
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
215 additions
and
54 deletions
+215
-54
circle/circle/settings/local.py
+5
-0
circle/dashboard/forms.py
+69
-40
circle/dashboard/models.py
+10
-3
circle/dashboard/templates/dashboard/group-create.html
+1
-1
circle/dashboard/templates/dashboard/group-detail.html
+10
-0
circle/dashboard/templates/dashboard/groupprofile_form.html
+21
-0
circle/dashboard/urls.py
+3
-1
circle/dashboard/views.py
+96
-9
No files found.
circle/circle/settings/local.py
View file @
e4141022
...
...
@@ -96,3 +96,8 @@ for i in LOCAL_APPS:
LOGGING
[
'loggers'
][
i
]
=
{
'handlers'
:
[
'console'
],
'level'
:
'DEBUG'
}
CRISPY_FAIL_SILENTLY
=
not
DEBUG
# propagate exceptions from signals
if
DEBUG
:
from
django.dispatch
import
Signal
Signal
.
send_robust
=
Signal
.
send
circle/dashboard/forms.py
View file @
e4141022
...
...
@@ -33,7 +33,7 @@ from crispy_forms.layout import (
from
crispy_forms.utils
import
render_field
from
django
import
forms
from
django.contrib.auth.forms
import
UserCreationForm
as
OrgUserCreationForm
from
django.forms.widgets
import
TextInput
from
django.forms.widgets
import
TextInput
,
HiddenInput
from
django.template
import
Context
from
django.template.loader
import
render_to_string
from
django.utils.translation
import
ugettext
as
_
...
...
@@ -44,7 +44,7 @@ from storage.models import Disk, DataStore
from
vm.models
import
(
InstanceTemplate
,
Lease
,
InterfaceTemplate
,
Node
,
Trait
,
Instance
)
from
.models
import
Profile
from
.models
import
Profile
,
GroupProfile
class
VmCustomizeForm
(
forms
.
Form
):
...
...
@@ -315,51 +315,80 @@ class VmCustomizeForm(forms.Form):
class
GroupCreateForm
(
forms
.
ModelForm
):
description
=
forms
.
CharField
(
label
=
_
(
"Description"
),
required
=
False
,
widget
=
forms
.
Textarea
(
attrs
=
{
'rows'
:
3
}))
def
__init__
(
self
,
*
args
,
**
kwargs
):
new_groups
=
kwargs
.
pop
(
'new_groups'
,
None
)
super
(
GroupCreateForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
helper
=
FormHelper
(
self
)
self
.
helper
.
form_show_labels
=
False
self
.
helper
.
layout
=
Layout
(
Div
(
Div
(
AnyTag
(
'h4'
,
HTML
(
_
(
"Name"
)),
),
css_class
=
"col-sm-10"
,
),
css_class
=
"row"
,
),
Div
(
Div
(
Field
(
'name'
,
id
=
"group-create-name"
),
css_class
=
"col-sm-10"
,
),
css_class
=
"row"
,
),
choices
=
[(
''
,
'--'
)]
if
new_groups
:
choices
+=
[(
g
,
g
)
for
g
in
new_groups
if
len
(
g
)
<=
64
]
self
.
fields
[
'org_id'
]
=
forms
.
ChoiceField
(
# TRANSLATORS: directory like in LDAP
choices
=
choices
,
required
=
False
,
label
=
_
(
'Directory identifier'
))
if
not
new_groups
:
self
.
fields
[
'org_id'
]
.
widget
=
HiddenInput
()
Div
(
# buttons
Div
(
AnyTag
(
# tip: don't try to use Button class
"button"
,
AnyTag
(
"i"
,
css_class
=
"icon-play"
),
HTML
(
" Create"
),
css_id
=
"vm-create-submit"
,
css_class
=
"btn btn-success"
,
def
save
(
self
,
commit
=
True
):
if
not
commit
:
raise
AttributeError
(
'Committing is mandatory.'
)
group
=
super
(
GroupCreateForm
,
self
)
.
save
()
),
css_class
=
"col-sm-5"
,
),
css_class
=
"row"
,
),
)
profile
=
group
.
profile
# multiple blanks were not be unique unlike NULLs are
profile
.
org_id
=
self
.
cleaned_data
[
'org_id'
]
or
None
profile
.
description
=
self
.
cleaned_data
[
'description'
]
profile
.
save
()
return
group
@property
def
helper
(
self
):
helper
=
FormHelper
(
self
)
helper
.
add_input
(
Submit
(
"submit"
,
_
(
"Create"
)))
helper
.
form_tag
=
False
return
helper
class
Meta
:
model
=
Group
fields
=
[
'name'
,
]
fields
=
(
'name'
,
)
class
GroupProfileUpdateForm
(
forms
.
ModelForm
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
new_groups
=
kwargs
.
pop
(
'new_groups'
,
None
)
superuser
=
kwargs
.
pop
(
'superuser'
,
False
)
super
(
GroupProfileUpdateForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
if
not
superuser
:
choices
=
[(
''
,
'--'
)]
if
new_groups
:
choices
+=
[(
g
,
g
)
for
g
in
new_groups
if
len
(
g
)
<=
64
]
self
.
fields
[
'org_id'
]
=
forms
.
ChoiceField
(
choices
=
choices
,
required
=
False
,
label
=
_
(
'Directory identifier'
))
if
not
new_groups
:
self
.
fields
[
'org_id'
]
.
widget
=
HiddenInput
()
self
.
fields
[
'description'
]
.
widget
=
forms
.
Textarea
(
attrs
=
{
'rows'
:
3
})
@property
def
helper
(
self
):
helper
=
FormHelper
(
self
)
helper
.
add_input
(
Submit
(
"submit"
,
_
(
"Save"
)))
helper
.
form_tag
=
False
return
helper
def
save
(
self
,
commit
=
True
):
profile
=
super
(
GroupProfileUpdateForm
,
self
)
.
save
(
commit
=
False
)
profile
.
org_id
=
self
.
cleaned_data
[
'org_id'
]
or
None
if
commit
:
profile
.
save
()
return
profile
class
Meta
:
model
=
GroupProfile
fields
=
(
'description'
,
'org_id'
)
class
HostForm
(
forms
.
ModelForm
):
...
...
circle/dashboard/models.py
View file @
e4141022
...
...
@@ -104,7 +104,12 @@ class GroupProfile(AclBase):
org_id
=
CharField
(
unique
=
True
,
blank
=
True
,
null
=
True
,
max_length
=
64
,
help_text
=
_
(
'Unique identifier of the group at the organization.'
))
description
=
TextField
()
description
=
TextField
(
blank
=
True
)
def
save
(
self
,
*
args
,
**
kwargs
):
if
not
self
.
org_id
:
self
.
org_id
=
None
super
(
GroupProfile
,
self
)
.
save
(
*
args
,
**
kwargs
)
@classmethod
def
search
(
cls
,
name
):
...
...
@@ -162,7 +167,8 @@ if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'):
logger
.
debug
(
"org_id of
%
s already added to user
%
s's profile"
,
value
,
sender
.
username
)
memberatrs
=
getattr
(
settings
,
'SAML_GROUP_ATTRIBUTES'
,
[])
for
group
in
chain
(
*
[
attributes
[
i
]
for
i
in
memberatrs
]):
for
group
in
chain
(
*
[
attributes
[
i
]
for
i
in
memberatrs
if
i
in
attributes
]):
try
:
g
=
GroupProfile
.
search
(
group
)
except
Group
.
DoesNotExist
:
...
...
@@ -173,7 +179,8 @@ if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'):
g
.
user_set
.
add
(
sender
)
owneratrs
=
getattr
(
settings
,
'SAML_GROUP_OWNER_ATTRIBUTES'
,
[])
for
group
in
chain
(
*
[
attributes
[
i
]
for
i
in
owneratrs
]):
for
group
in
chain
(
*
[
attributes
[
i
]
for
i
in
owneratrs
if
i
in
attributes
]):
try
:
g
=
GroupProfile
.
search
(
group
)
except
Group
.
DoesNotExist
:
...
...
circle/dashboard/templates/dashboard/group-create.html
View file @
e4141022
...
...
@@ -5,7 +5,7 @@
}
</style>
<form
method=
"POST"
action=
"
/dashboard/group/create/
"
>
<form
method=
"POST"
action=
"
{% url "
dashboard
.
views
.
group-create
"
%}
"
>
{% csrf_token %}
{% crispy form %}
</form>
circle/dashboard/templates/dashboard/group-detail.html
View file @
e4141022
{% extends "dashboard/base.html" %}
{% load crispy_forms_tags %}
{% load i18n %}
{% block content %}
...
...
@@ -19,6 +20,9 @@
</div>
<div
id=
"group-details-h1-name"
>
{{ group.name }}
{% if group.groupprofile.org_id %}
<small>
{{group.groupprofile.org_id}}
</small>
{% endif %}
</div>
</h1>
<div
class=
"group-details-help js-hidden"
>
...
...
@@ -38,6 +42,12 @@
<div
class=
"col-md-12"
id=
"group-detail-pane"
>
<div
class=
"panel panel-default"
id=
"group-detail-panel"
>
<div
class=
"tab-content panel-body"
id=
"group-form-body"
>
<form
method=
"POST"
action=
"{% url "
dashboard
.
views
.
group-update
"
pk=
group.pk
%}"
>
{% csrf_token %}
{% crispy group_profile_form %}
</form>
<h3>
{% trans "User list"|capfirst %}
</h3>
<form
action=
""
method=
"post"
>
{% csrf_token %}
<table
class=
"table table-striped table-with-form-fields table-bordered"
id=
"group-detail-user-table"
>
...
...
circle/dashboard/templates/dashboard/groupprofile_form.html
0 → 100644
View file @
e4141022
{% extends "dashboard/base.html" %}
{% load crispy_forms_tags %}
{% load i18n %}
{% block content %}
<div
class=
"body-content"
>
<div
class=
"panel panel-default"
style=
"margin-top: 60px;"
>
<div
class=
"panel-heading"
>
<h3
class=
"no-margin"
>
{% trans "Update group" %}
</h3>
</div>
<div
class=
"panel-body"
>
<form
method=
"POST"
action=
""
>
{% csrf_token %}
{% crispy form %}
</form>
</div>
</div>
</div>
{% endblock %}
circle/dashboard/urls.py
View file @
e4141022
...
...
@@ -31,7 +31,7 @@ from .views import (
VmDetailVncTokenView
,
VmGraphView
,
VmList
,
VmMassDelete
,
VmMigrateView
,
VmRenewView
,
DiskRemoveView
,
get_disk_download_status
,
InterfaceDeleteView
,
GroupRemoveAclUserView
,
GroupRemoveAclGroupView
,
GroupRemoveUserView
,
GroupCreate
,
GroupCreate
,
GroupProfileUpdate
,
TemplateChoose
,
UserCreationView
,
get_vm_screenshot
,
...
...
@@ -121,6 +121,8 @@ urlpatterns = patterns(
name
=
'dashboard.views.node-graph'
),
url
(
r'^group/(?P<pk>\d+)/$'
,
GroupDetailView
.
as_view
(),
name
=
'dashboard.views.group-detail'
),
url
(
r'^group/(?P<pk>\d+)/update/$'
,
GroupProfileUpdate
.
as_view
(),
name
=
'dashboard.views.group-update'
),
url
(
r'^group/(?P<pk>\d+)/acl/$'
,
GroupAclUpdateView
.
as_view
(),
name
=
'dashboard.views.group-acl'
),
url
(
r'^notifications/$'
,
NotificationView
.
as_view
(),
...
...
circle/dashboard/views.py
View file @
e4141022
...
...
@@ -17,6 +17,7 @@
from
__future__
import
unicode_literals
,
absolute_import
from
itertools
import
chain
from
os
import
getenv
import
json
import
logging
...
...
@@ -58,7 +59,7 @@ from braces.views._access import AccessMixin
from
.forms
import
(
CircleAuthenticationForm
,
DiskAddForm
,
HostForm
,
LeaseForm
,
MyProfileForm
,
NodeForm
,
TemplateForm
,
TraitForm
,
VmCustomizeForm
,
GroupCreateForm
,
UserCreationForm
,
UserCreationForm
,
GroupProfileUpdateForm
,
CirclePasswordChangeForm
)
...
...
@@ -75,6 +76,7 @@ from firewall.models import Vlan, Host, Rule
from
.models
import
Favourite
,
Profile
,
GroupProfile
logger
=
logging
.
getLogger
(
__name__
)
saml_available
=
hasattr
(
settings
,
"SAML_CONFIG"
)
def
search_user
(
keyword
):
...
...
@@ -87,6 +89,39 @@ def search_user(keyword):
return
User
.
objects
.
get
(
email
=
keyword
)
class
GroupCodeMixin
(
object
):
@classmethod
def
get_available_group_codes
(
cls
,
request
):
newgroups
=
[]
if
saml_available
:
from
djangosaml2.cache
import
StateCache
,
IdentityCache
from
djangosaml2.conf
import
get_config
from
djangosaml2.views
import
_get_subject_id
from
saml2.client
import
Saml2Client
state
=
StateCache
(
request
.
session
)
conf
=
get_config
(
None
,
request
)
client
=
Saml2Client
(
conf
,
state_cache
=
state
,
identity_cache
=
IdentityCache
(
request
.
session
),
logger
=
logger
)
subject_id
=
_get_subject_id
(
request
.
session
)
identity
=
client
.
users
.
get_identity
(
subject_id
,
check_not_on_or_after
=
False
)
if
identity
:
attributes
=
identity
[
0
]
owneratrs
=
getattr
(
settings
,
'SAML_GROUP_OWNER_ATTRIBUTES'
,
[])
for
group
in
chain
(
*
[
attributes
[
i
]
for
i
in
owneratrs
if
i
in
attributes
]):
try
:
GroupProfile
.
search
(
group
)
except
Group
.
DoesNotExist
:
newgroups
.
append
(
group
)
return
newgroups
class
FilterMixin
(
object
):
def
get_queryset_filters
(
self
):
...
...
@@ -692,6 +727,8 @@ class GroupDetailView(CheckedDetailView):
context
[
'group'
]
=
self
.
object
context
[
'users'
]
=
self
.
object
.
user_set
.
all
()
context
[
'acl'
]
=
get_group_acl_data
(
self
.
object
)
context
[
'group_profile_form'
]
=
GroupProfileUpdate
.
get_form_object
(
self
.
request
,
self
.
object
.
profile
)
return
context
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
...
...
@@ -1565,10 +1602,9 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
return
redirect
(
path
)
class
GroupCreate
(
LoginRequiredMixin
,
TemplateView
):
class
GroupCreate
(
GroupCodeMixin
,
LoginRequiredMixin
,
TemplateView
):
form_class
=
GroupCreateForm
form
=
None
def
get_template_names
(
self
):
if
self
.
request
.
is_ajax
():
...
...
@@ -1580,7 +1616,8 @@ class GroupCreate(LoginRequiredMixin, TemplateView):
if
not
request
.
user
.
has_module_perms
(
'auth'
):
raise
PermissionDenied
()
if
form
is
None
:
form
=
self
.
form_class
()
form
=
self
.
form_class
(
new_groups
=
self
.
get_available_group_codes
(
request
))
context
=
self
.
get_context_data
(
**
kwargs
)
context
.
update
({
'template'
:
'dashboard/group-create.html'
,
...
...
@@ -1593,7 +1630,8 @@ class GroupCreate(LoginRequiredMixin, TemplateView):
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
if
not
request
.
user
.
has_module_perms
(
'auth'
):
raise
PermissionDenied
()
form
=
self
.
form_class
(
request
.
POST
)
form
=
self
.
form_class
(
request
.
POST
,
new_groups
=
self
.
get_available_group_codes
(
request
))
if
not
form
.
is_valid
():
return
self
.
get
(
request
,
form
,
*
args
,
**
kwargs
)
form
.
cleaned_data
...
...
@@ -1608,6 +1646,55 @@ class GroupCreate(LoginRequiredMixin, TemplateView):
return
redirect
(
savedform
.
profile
.
get_absolute_url
())
class
GroupProfileUpdate
(
SuccessMessageMixin
,
GroupCodeMixin
,
LoginRequiredMixin
,
UpdateView
):
form_class
=
GroupProfileUpdateForm
model
=
Group
success_message
=
_
(
'Group is successfully updated.'
)
@classmethod
def
get_available_group_codes
(
cls
,
request
,
extra
=
None
):
result
=
super
(
GroupProfileUpdate
,
cls
)
.
get_available_group_codes
(
request
)
if
extra
and
extra
not
in
result
:
result
+=
[
extra
]
return
result
def
get_object
(
self
):
group
=
super
(
GroupProfileUpdate
,
self
)
.
get_object
()
profile
=
group
.
profile
if
not
profile
.
has_level
(
self
.
request
.
user
,
'owner'
):
raise
PermissionDenied
else
:
return
profile
@classmethod
def
get_form_object
(
cls
,
request
,
instance
,
*
args
,
**
kwargs
):
kwargs
[
'instance'
]
=
instance
kwargs
[
'new_groups'
]
=
cls
.
get_available_group_codes
(
request
,
instance
.
org_id
)
kwargs
[
'superuser'
]
=
request
.
user
.
is_superuser
return
cls
.
form_class
(
*
args
,
**
kwargs
)
def
get
(
self
,
request
,
form
=
None
,
*
args
,
**
kwargs
):
self
.
object
=
self
.
get_object
()
if
form
is
None
:
form
=
self
.
get_form_object
(
request
,
self
.
object
)
return
super
(
GroupProfileUpdate
,
self
)
.
get
(
request
,
form
,
*
args
,
**
kwargs
)
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
if
not
request
.
user
.
has_module_perms
(
'auth'
):
raise
PermissionDenied
()
self
.
object
=
self
.
get_object
()
form
=
self
.
get_form_object
(
request
,
self
.
object
,
self
.
request
.
POST
)
if
not
form
.
is_valid
():
return
self
.
form_invalid
(
form
)
form
.
save
()
return
self
.
form_valid
(
form
)
class
VmDelete
(
LoginRequiredMixin
,
DeleteView
):
model
=
Instance
template_name
=
"dashboard/confirm/base-delete.html"
...
...
@@ -1880,9 +1967,9 @@ class VmMassDelete(LoginRequiredMixin, View):
logger
.
info
(
'Tried to delete instance #
%
d without owner '
'permission by
%
s.'
,
i
.
pk
,
unicode
(
request
.
user
))
raise
PermissionDenied
()
# no need for rollback or proper
# error message, this can't
# normally happen.
# no need for rollback or proper error message, this can't
# normally happen:
raise
PermissionDenied
()
try
:
i
.
destroy
.
async
(
user
=
request
.
user
)
names
.
append
(
i
.
name
)
...
...
@@ -2416,7 +2503,7 @@ class NotificationView(LoginRequiredMixin, TemplateView):
def
circle_login
(
request
):
authentication_form
=
CircleAuthenticationForm
extra_context
=
{
'saml2'
:
hasattr
(
settings
,
"SAML_CONFIG"
)
'saml2'
:
saml_available
,
}
response
=
login
(
request
,
authentication_form
=
authentication_form
,
extra_context
=
extra_context
)
...
...
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