mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 01:47:35 -02:30
Working on Hosts and Groups
This commit is contained in:
3
TODO.md
3
TODO.md
@@ -27,5 +27,6 @@ TWEAKS/ASSORTED
|
|||||||
* uniqueness checks for playbook paths?
|
* uniqueness checks for playbook paths?
|
||||||
* allow multiple playbook execution types per project, different --tag choices, different --limit choices (maybe just free form in the job for now?)
|
* allow multiple playbook execution types per project, different --tag choices, different --limit choices (maybe just free form in the job for now?)
|
||||||
* permissions infrastructure about who can kick off what kind of jobs
|
* permissions infrastructure about who can kick off what kind of jobs
|
||||||
* it would be nice if POSTs to subcollections used the permissions of the regular collection POST rules and then called the PUT code.
|
* it would be nice if POSTs to subcollections used the permissions of the regular collection POST rules and then called the attach code.
|
||||||
* root API discovery resource at /api and /api/v1
|
* root API discovery resource at /api and /api/v1
|
||||||
|
* audit/test read only fields like creation_date
|
||||||
|
|||||||
@@ -40,11 +40,14 @@ class BaseList(generics.ListCreateAPIView):
|
|||||||
return True
|
return True
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if self.__class__.model in [ User ]:
|
if self.__class__.model in [ User ]:
|
||||||
# Django user gets special handling since it's not our class
|
ok = self.request.user.is_superuser or (self.request.user.admin_of_organizations.count() > 0)
|
||||||
# org admins are allowed to create users
|
if not ok:
|
||||||
return self.request.user.is_superuser or (self.request.user.admin_of_organizations.count() > 0)
|
raise PermissionDenied()
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
return self.__class__.model.can_user_add(request.user, self.request.DATA)
|
if not self.__class__.model.can_user_add(request.user, self.request.DATA):
|
||||||
|
raise PermissionDenied()
|
||||||
|
return True
|
||||||
raise exceptions.NotImplementedError
|
raise exceptions.NotImplementedError
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
@@ -112,7 +115,7 @@ class BaseDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
obj = self.model.objects.get(pk=kwargs['pk'])
|
obj = self.model.objects.get(pk=kwargs['pk'])
|
||||||
if not request.user.is_superuser and not self.delete_permissions_check(request, obj):
|
if not request.user.is_superuser and not self.delete_permissions_check(request, obj):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
if isinstance(obj, CommonModel):
|
if isinstance(obj, PrimordialModel):
|
||||||
obj.name = "_deleted_%s_%s" % (str(datetime.time()), obj.name)
|
obj.name = "_deleted_%s_%s" % (str(datetime.time()), obj.name)
|
||||||
obj.active = False
|
obj.active = False
|
||||||
obj.save()
|
obj.save()
|
||||||
@@ -125,7 +128,7 @@ class BaseDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return HttpResponse(status=204)
|
return HttpResponse(status=204)
|
||||||
|
|
||||||
def delete_permissions_check(self, request, obj):
|
def delete_permissions_check(self, request, obj):
|
||||||
if isinstance(obj, CommonModel):
|
if isinstance(obj, PrimordialModel):
|
||||||
return self.__class__.model.can_user_delete(request.user, obj)
|
return self.__class__.model.can_user_delete(request.user, obj)
|
||||||
elif isinstance(obj, User):
|
elif isinstance(obj, User):
|
||||||
return UserHelper.can_user_delete(request.user, obj)
|
return UserHelper.can_user_delete(request.user, obj)
|
||||||
|
|||||||
270
lib/main/migrations/0009_changes.py
Normal file
270
lib/main/migrations/0009_changes.py
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
# Removing unique constraint on 'Group', fields ['name']
|
||||||
|
db.delete_unique(u'main_group', ['name'])
|
||||||
|
|
||||||
|
# Removing unique constraint on 'Host', fields ['name']
|
||||||
|
db.delete_unique(u'main_host', ['name'])
|
||||||
|
|
||||||
|
|
||||||
|
# Changing field 'Inventory.organization'
|
||||||
|
db.alter_column(u'main_inventory', 'organization_id', self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['main.Organization']))
|
||||||
|
|
||||||
|
# Changing field 'Host.inventory'
|
||||||
|
db.alter_column(u'main_host', 'inventory_id', self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['main.Inventory']))
|
||||||
|
|
||||||
|
# Changing field 'Group.inventory'
|
||||||
|
db.alter_column(u'main_group', 'inventory_id', self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['main.Inventory']))
|
||||||
|
|
||||||
|
# Changing field 'VariableData.group'
|
||||||
|
db.alter_column(u'main_variabledata', 'group_id', self.gf('django.db.models.fields.related.ForeignKey')(on_delete=models.SET_NULL, to=orm['main.Group'], null=True))
|
||||||
|
|
||||||
|
# Changing field 'VariableData.host'
|
||||||
|
db.alter_column(u'main_variabledata', 'host_id', self.gf('django.db.models.fields.related.ForeignKey')(on_delete=models.SET_NULL, to=orm['main.Host'], null=True))
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Changing field 'Inventory.organization'
|
||||||
|
db.alter_column(u'main_inventory', 'organization_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, on_delete=models.SET_NULL, to=orm['main.Organization']))
|
||||||
|
# Adding unique constraint on 'Host', fields ['name']
|
||||||
|
db.create_unique(u'main_host', ['name'])
|
||||||
|
|
||||||
|
|
||||||
|
# Changing field 'Host.inventory'
|
||||||
|
db.alter_column(u'main_host', 'inventory_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, on_delete=models.SET_NULL, to=orm['main.Inventory']))
|
||||||
|
# Adding unique constraint on 'Group', fields ['name']
|
||||||
|
db.create_unique(u'main_group', ['name'])
|
||||||
|
|
||||||
|
|
||||||
|
# Changing field 'Group.inventory'
|
||||||
|
db.alter_column(u'main_group', 'inventory_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, on_delete=models.SET_NULL, to=orm['main.Inventory']))
|
||||||
|
|
||||||
|
# Changing field 'VariableData.group'
|
||||||
|
db.alter_column(u'main_variabledata', 'group_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['main.Group']))
|
||||||
|
|
||||||
|
# Changing field 'VariableData.host'
|
||||||
|
db.alter_column(u'main_variabledata', 'host_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['main.Host']))
|
||||||
|
|
||||||
|
models = {
|
||||||
|
u'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
u'auth.permission': {
|
||||||
|
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
u'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
u'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
'main.audittrail': {
|
||||||
|
'Meta': {'object_name': 'AuditTrail'},
|
||||||
|
'comment': ('django.db.models.fields.TextField', [], {}),
|
||||||
|
'delta': ('django.db.models.fields.TextField', [], {}),
|
||||||
|
'detail': ('django.db.models.fields.TextField', [], {}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||||
|
'resource_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||||
|
'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Tag']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'main.credential': {
|
||||||
|
'Meta': {'object_name': 'Credential'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'credential_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||||
|
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credentials'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['main.Project']", 'blank': 'True', 'null': 'True'}),
|
||||||
|
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
'ssh_key_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4096', 'blank': 'True'}),
|
||||||
|
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||||
|
'ssh_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||||
|
'sudo_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'credential_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'team': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credentials'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Team']", 'blank': 'True', 'null': 'True'}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credentials'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['auth.User']", 'blank': 'True', 'null': 'True'})
|
||||||
|
},
|
||||||
|
'main.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'group_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||||
|
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'group_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"})
|
||||||
|
},
|
||||||
|
'main.host': {
|
||||||
|
'Meta': {'object_name': 'Host'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'host_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'host_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"})
|
||||||
|
},
|
||||||
|
'main.inventory': {
|
||||||
|
'Meta': {'object_name': 'Inventory'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'inventory_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||||
|
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'inventory_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"})
|
||||||
|
},
|
||||||
|
'main.launchjob': {
|
||||||
|
'Meta': {'object_name': 'LaunchJob'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'launchjob_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'launchjob\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'launch_jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'launch_jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Inventory']", 'blank': 'True', 'null': 'True'}),
|
||||||
|
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||||
|
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'launch_jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['main.Project']", 'blank': 'True', 'null': 'True'}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'launchjob_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'launch_jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['auth.User']", 'blank': 'True', 'null': 'True'})
|
||||||
|
},
|
||||||
|
'main.launchjobstatus': {
|
||||||
|
'Meta': {'object_name': 'LaunchJobStatus'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'launchjobstatus_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'launchjobstatus\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'launch_job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'launch_job_statuses'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.LaunchJob']"}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||||
|
'result_data': ('django.db.models.fields.TextField', [], {}),
|
||||||
|
'status': ('django.db.models.fields.IntegerField', [], {}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'launchjobstatus_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"})
|
||||||
|
},
|
||||||
|
'main.organization': {
|
||||||
|
'Meta': {'object_name': 'Organization'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organization_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||||
|
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['main.Project']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organization_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'main.permission': {
|
||||||
|
'Meta': {'object_name': 'Permission'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'permission_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||||
|
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||||
|
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['main.Project']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'permission_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
|
||||||
|
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
|
||||||
|
},
|
||||||
|
u'main.project': {
|
||||||
|
'Meta': {'object_name': 'Project'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'project_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'project\', \'app_label\': u\'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'default_playbook': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'inventories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'projects'", 'blank': 'True', 'to': "orm['main.Inventory']"}),
|
||||||
|
'local_repository': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||||
|
'scm_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'project_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"})
|
||||||
|
},
|
||||||
|
'main.tag': {
|
||||||
|
'Meta': {'object_name': 'Tag'},
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
|
||||||
|
},
|
||||||
|
'main.team': {
|
||||||
|
'Meta': {'object_name': 'Team'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'team_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||||
|
'organizations': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'teams'", 'symmetrical': 'False', 'to': "orm['main.Organization']"}),
|
||||||
|
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['main.Project']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'team_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
|
||||||
|
},
|
||||||
|
'main.variabledata': {
|
||||||
|
'Meta': {'object_name': 'VariableData'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'variabledata_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'variabledata\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||||
|
'creation_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'data': ('django.db.models.fields.TextField', [], {}),
|
||||||
|
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||||
|
'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variable_data'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Group']", 'blank': 'True', 'null': 'True'}),
|
||||||
|
'host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variable_data'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Host']", 'blank': 'True', 'null': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'variabledata_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['main']
|
||||||
@@ -124,15 +124,16 @@ class UserHelper(object):
|
|||||||
return matching_orgs
|
return matching_orgs
|
||||||
|
|
||||||
|
|
||||||
class CommonModel(models.Model):
|
class PrimordialModel(models.Model):
|
||||||
'''
|
'''
|
||||||
common model for all object types that have these standard fields
|
common model for all object types that have these standard fields
|
||||||
|
must use a subclass CommonModel or CommonModelNameNotUnique though
|
||||||
|
as this lacks a name field.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
name = models.CharField(max_length=512, unique=True)
|
|
||||||
description = models.TextField(blank=True, default='')
|
description = models.TextField(blank=True, default='')
|
||||||
created_by = models.ForeignKey('auth.User', on_delete=SET_NULL, null=True, related_name='%s(class)s_created') # not blank=False on purpose for admin!
|
created_by = models.ForeignKey('auth.User', on_delete=SET_NULL, null=True, related_name='%s(class)s_created') # not blank=False on purpose for admin!
|
||||||
creation_date = models.DateField(auto_now_add=True)
|
creation_date = models.DateField(auto_now_add=True)
|
||||||
@@ -173,6 +174,22 @@ class CommonModel(models.Model):
|
|||||||
def can_user_unattach(cls, user, obj, sub_obj, relationship):
|
def can_user_unattach(cls, user, obj, sub_obj, relationship):
|
||||||
return cls.can_user_administrate(user, obj)
|
return cls.can_user_administrate(user, obj)
|
||||||
|
|
||||||
|
class CommonModel(PrimordialModel):
|
||||||
|
''' a base model where the name is unique '''
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
name = models.CharField(max_length=512, unique=True)
|
||||||
|
|
||||||
|
class CommonModelNameNotUnique(PrimordialModel):
|
||||||
|
''' a base model where the name is not unique '''
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
name = models.CharField(max_length=512, unique=False)
|
||||||
|
|
||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
'''
|
'''
|
||||||
any type of object can be given a search tag
|
any type of object can be given a search tag
|
||||||
@@ -268,7 +285,7 @@ class Inventory(CommonModel):
|
|||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
verbose_name_plural = _('inventories')
|
verbose_name_plural = _('inventories')
|
||||||
|
|
||||||
organization = models.ForeignKey(Organization, null=True, on_delete=SET_NULL, related_name='inventories')
|
organization = models.ForeignKey(Organization, null=False, related_name='inventories')
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
import lib.urls
|
import lib.urls
|
||||||
@@ -297,10 +314,34 @@ class Inventory(CommonModel):
|
|||||||
result = (by_org_admin + by_team_permission + by_user_permission)
|
result = (by_org_admin + by_team_permission + by_user_permission)
|
||||||
return result > 0
|
return result > 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _has_any_inventory_permission_types(cls, user, allowed):
|
||||||
|
'''
|
||||||
|
rather than checking for a permission on a specific inventory, return whether we have
|
||||||
|
permissions on any inventory. This is primarily used to decide if the user can create
|
||||||
|
host or group objects
|
||||||
|
'''
|
||||||
|
|
||||||
|
if user.is_superuser:
|
||||||
|
return True
|
||||||
|
by_org_admin = user.organizations.filter(
|
||||||
|
admins__in = [ user ]
|
||||||
|
).count()
|
||||||
|
by_team_permission = Permission.objects.filter(
|
||||||
|
team__in = user.teams.all(),
|
||||||
|
permission_type__in = allowed
|
||||||
|
).count()
|
||||||
|
by_user_permission = user.permissions.filter(
|
||||||
|
permission_type__in = allowed
|
||||||
|
).count()
|
||||||
|
|
||||||
|
result = (by_org_admin + by_team_permission + by_user_permission)
|
||||||
|
return result > 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_add(cls, user, data):
|
def can_user_add(cls, user, data):
|
||||||
if not 'organization' in data:
|
if not 'organization' in data:
|
||||||
return False
|
return True
|
||||||
if user.is_superuser:
|
if user.is_superuser:
|
||||||
return True
|
return True
|
||||||
if not user.is_superuser:
|
if not user.is_superuser:
|
||||||
@@ -322,7 +363,7 @@ class Inventory(CommonModel):
|
|||||||
return cls._has_permission_types(user, obj, PERMISSION_TYPES_ALLOWING_INVENTORY_ADMIN)
|
return cls._has_permission_types(user, obj, PERMISSION_TYPES_ALLOWING_INVENTORY_ADMIN)
|
||||||
|
|
||||||
|
|
||||||
class Host(CommonModel):
|
class Host(CommonModelNameNotUnique):
|
||||||
'''
|
'''
|
||||||
A managed node
|
A managed node
|
||||||
'''
|
'''
|
||||||
@@ -330,12 +371,26 @@ class Host(CommonModel):
|
|||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
inventory = models.ForeignKey('Inventory', null=True, on_delete=SET_NULL, related_name='hosts')
|
inventory = models.ForeignKey('Inventory', null=False, related_name='hosts')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
class Group(CommonModel):
|
@classmethod
|
||||||
|
def can_user_add(cls, user, data):
|
||||||
|
print "DEBUG: can_user_add called for HOST: %s" % data
|
||||||
|
if not 'inventory' in data:
|
||||||
|
print 'DEBUG: missing inventory!'
|
||||||
|
return False
|
||||||
|
inventory = Inventory.objects.get(pk=data['inventory'])
|
||||||
|
return Inventory._has_permission_types(user, inventory, PERMISSION_TYPES_ALLOWING_INVENTORY_WRITE)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
import lib.urls
|
||||||
|
return reverse(lib.urls.views_HostsDetail, args=(self.pk,))
|
||||||
|
|
||||||
|
class Group(CommonModelNameNotUnique):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
A group of managed nodes. May belong to multiple groups
|
A group of managed nodes. May belong to multiple groups
|
||||||
'''
|
'''
|
||||||
@@ -343,13 +398,20 @@ class Group(CommonModel):
|
|||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
|
|
||||||
inventory = models.ForeignKey('Inventory', null=True, on_delete=SET_NULL, related_name='groups')
|
inventory = models.ForeignKey('Inventory', null=False, related_name='groups')
|
||||||
parents = models.ManyToManyField('self', symmetrical=False, related_name='children', blank=True)
|
parents = models.ManyToManyField('self', symmetrical=False, related_name='children', blank=True)
|
||||||
hosts = models.ManyToManyField('Host', related_name='groups', blank=True)
|
hosts = models.ManyToManyField('Host', related_name='groups', blank=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def can_user_add(cls, user, data):
|
||||||
|
if not 'inventory' in data:
|
||||||
|
return False
|
||||||
|
inventory = Inventory.objects.get(pk=data['inventory'])
|
||||||
|
return Inventory._has_permission_types(user, inventory, PERMISSION_TYPES_ALLOWING_INVENTORY_WRITE)
|
||||||
|
|
||||||
# FIXME: audit nullables
|
# FIXME: audit nullables
|
||||||
# FIXME: audit cascades
|
# FIXME: audit cascades
|
||||||
|
|
||||||
@@ -362,8 +424,8 @@ class VariableData(CommonModel):
|
|||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
verbose_name_plural = _('variable data')
|
verbose_name_plural = _('variable data')
|
||||||
|
|
||||||
host = models.ForeignKey('Host', null=True, default=None, blank=True, on_delete=CASCADE, related_name='variable_data')
|
host = models.ForeignKey('Host', null=True, default=None, blank=True, on_delete=SET_NULL, related_name='variable_data')
|
||||||
group = models.ForeignKey('Group', null=True, default=None, blank=True, on_delete=CASCADE, related_name='variable_data')
|
group = models.ForeignKey('Group', null=True, default=None, blank=True, on_delete=SET_NULL, related_name='variable_data')
|
||||||
data = models.TextField() # FIXME: JsonField
|
data = models.TextField() # FIXME: JsonField
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
|
|||||||
@@ -90,9 +90,36 @@ class InventorySerializer(BaseSerializer):
|
|||||||
fields = ('url', 'id', 'name', 'description', 'creation_date', 'organization')
|
fields = ('url', 'id', 'name', 'description', 'creation_date', 'organization')
|
||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
# FIXME: add related resources: inventories
|
# FIXME: add related resources: hosts, groups
|
||||||
return dict()
|
return dict()
|
||||||
|
|
||||||
|
class HostSerializer(BaseSerializer):
|
||||||
|
|
||||||
|
# add the URL and related resources
|
||||||
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
related = serializers.SerializerMethodField('get_related')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Host
|
||||||
|
fields = ('url', 'id', 'name', 'description', 'creation_date', 'inventory')
|
||||||
|
|
||||||
|
def get_related(self, obj):
|
||||||
|
# FIXME: add related resources
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
class GroupSerializer(BaseSerializer):
|
||||||
|
|
||||||
|
# add the URL and related resources
|
||||||
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
related = serializers.SerializerMethodField('get_related')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Host
|
||||||
|
fields = ('url', 'id', 'name', 'description', 'creation_date', 'inventory')
|
||||||
|
|
||||||
|
def get_related(self, obj):
|
||||||
|
# FIXME: add related resources
|
||||||
|
return dict()
|
||||||
|
|
||||||
class TeamSerializer(BaseSerializer):
|
class TeamSerializer(BaseSerializer):
|
||||||
|
|
||||||
@@ -126,7 +153,6 @@ class UserSerializer(BaseSerializer):
|
|||||||
admin_of_organizations = reverse(lib.urls.views_UsersAdminOrganizationsList, args=(obj.pk,)),
|
admin_of_organizations = reverse(lib.urls.views_UsersAdminOrganizationsList, args=(obj.pk,)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_absolute_url_override(self, obj):
|
def get_absolute_url_override(self, obj):
|
||||||
import lib.urls
|
import lib.urls
|
||||||
return reverse(lib.urls.views_UsersDetail, args=(obj.pk,))
|
return reverse(lib.urls.views_UsersDetail, args=(obj.pk,))
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class InventoryTest(BaseTest):
|
|||||||
# an org admin of any org can create inventory, if it is one of his organizations
|
# an org admin of any org can create inventory, if it is one of his organizations
|
||||||
# the organization parameter is required!
|
# the organization parameter is required!
|
||||||
new_inv_incomplete = dict(name='inventory-d', description='baz')
|
new_inv_incomplete = dict(name='inventory-d', description='baz')
|
||||||
data = self.post(inventories, data=new_inv_incomplete, expect=403, auth=self.get_normal_credentials())
|
data = self.post(inventories, data=new_inv_incomplete, expect=400, auth=self.get_normal_credentials())
|
||||||
new_inv_not_my_org = dict(name='inventory-d', description='baz', organization=3)
|
new_inv_not_my_org = dict(name='inventory-d', description='baz', organization=3)
|
||||||
|
|
||||||
data = self.post(inventories, data=new_inv_not_my_org, expect=403, auth=self.get_normal_credentials())
|
data = self.post(inventories, data=new_inv_not_my_org, expect=403, auth=self.get_normal_credentials())
|
||||||
@@ -119,13 +119,38 @@ class InventoryTest(BaseTest):
|
|||||||
new_inv_denied = dict(name='inventory-e', description='glorp', organization=1)
|
new_inv_denied = dict(name='inventory-e', description='glorp', organization=1)
|
||||||
data = self.post(inventories, data=new_inv_denied, expect=403, auth=self.get_other_credentials())
|
data = self.post(inventories, data=new_inv_denied, expect=403, auth=self.get_other_credentials())
|
||||||
|
|
||||||
# a super user can add hosts
|
# a super user can add hosts (but inventory ID is required)
|
||||||
|
inv = Inventory.objects.create(
|
||||||
|
name = 'test inventory',
|
||||||
|
organization = self.organizations[0]
|
||||||
|
)
|
||||||
|
invalid = dict(name='asdf0.example.com')
|
||||||
|
new_host_a = dict(name='asdf0.example.com', inventory=inv.pk)
|
||||||
|
new_host_b = dict(name='asdf1.example.com', inventory=inv.pk)
|
||||||
|
new_host_c = dict(name='asdf2.example.com', inventory=inv.pk)
|
||||||
|
new_host_d = dict(name='asdf3.example.com', inventory=inv.pk)
|
||||||
|
# FIXME: should raise 400 not 201, look into required fields in rest_framework
|
||||||
|
print hosts
|
||||||
|
data0 = self.post(hosts, data=invalid, expect=400, auth=self.get_super_credentials())
|
||||||
|
data0 = self.post(hosts, data=new_host_a, expect=201, auth=self.get_super_credentials())
|
||||||
|
|
||||||
# an org admin can add groups
|
# an org admin can add hosts
|
||||||
|
data1 = self.post(hosts, data=new_host_a, expect=201, auth=self.get_normal_credentials())
|
||||||
|
|
||||||
# a normal user cannot add hosts
|
# a normal user cannot add hosts
|
||||||
|
data2 = self.post(hosts, data=new_host_b, expect=403, auth=self.get_nobody_credentials())
|
||||||
|
|
||||||
# a normal user with inventory edit permissions can create hosts
|
# a normal user with inventory edit permissions (on any inventory) can create hosts
|
||||||
|
edit_perm = Permission.objects.create(
|
||||||
|
user = self.other_django_user,
|
||||||
|
inventory = Inventory.objects.get(pk=1),
|
||||||
|
permission_type = PERM_INVENTORY_EDIT
|
||||||
|
)
|
||||||
|
data3 = self.post(hosts, data=new_host_c, expect=201, auth=self.get_other_credentials())
|
||||||
|
|
||||||
|
# hostnames must be unique -- posting a duplicate just returns the previous
|
||||||
|
data4 = self.post(hosts, data=new_host_c, expect=200, auth=self.get_other_credentials())
|
||||||
|
self.assertEqual(data1['id'], data4['id'])
|
||||||
|
|
||||||
# a super user can add groups
|
# a super user can add groups
|
||||||
|
|
||||||
@@ -135,6 +160,8 @@ class InventoryTest(BaseTest):
|
|||||||
|
|
||||||
# a normal user with inventory edit permissions can create groups
|
# a normal user with inventory edit permissions can create groups
|
||||||
|
|
||||||
|
# group names must be unique for each inventory record
|
||||||
|
|
||||||
# a super user can associate hosts with inventories
|
# a super user can associate hosts with inventories
|
||||||
|
|
||||||
# an org admin can associate hosts with inventories
|
# an org admin can associate hosts with inventories
|
||||||
|
|||||||
@@ -282,3 +282,37 @@ class InventoryDetail(BaseDetail):
|
|||||||
serializer_class = InventorySerializer
|
serializer_class = InventorySerializer
|
||||||
permission_classes = (CustomRbac,)
|
permission_classes = (CustomRbac,)
|
||||||
|
|
||||||
|
class HostsList(BaseList):
|
||||||
|
|
||||||
|
model = Host
|
||||||
|
serializer_class = HostSerializer
|
||||||
|
permission_classes = (CustomRbac,)
|
||||||
|
|
||||||
|
def _get_queryset(self):
|
||||||
|
'''
|
||||||
|
I can see hosts when:
|
||||||
|
I'm a superuser,
|
||||||
|
or an organization admin of an inventory they are in
|
||||||
|
or when I have allowing read permissions via a user or team on an inventory they are in
|
||||||
|
'''
|
||||||
|
base = Host.objects
|
||||||
|
if self.request.user.is_superuser:
|
||||||
|
return base.all()
|
||||||
|
admin_of = base.filter(inventory__organization__admins__in = [ self.request.user ]).distinct()
|
||||||
|
has_user_perms = base.filter(
|
||||||
|
inventory__permissions__user__in = [ self.request.user ],
|
||||||
|
inventory__permissions__permission_type__in = PERMISSION_TYPES_ALLOWING_INVENTORY_READ,
|
||||||
|
).distinct()
|
||||||
|
has_team_perms = base.filter(
|
||||||
|
inventory__permissions__team__in = self.request.user.teams.all(),
|
||||||
|
inventory__permissions__permission_type__in = PERMISSION_TYPES_ALLOWING_INVENTORY_READ,
|
||||||
|
).distinct()
|
||||||
|
return admin_of | has_user_perms | has_team_perms
|
||||||
|
|
||||||
|
class HostsDetail(BaseDetail):
|
||||||
|
|
||||||
|
model = Host
|
||||||
|
serializer_class = HostSerializer
|
||||||
|
permission_classes = (CustomRbac,)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ views_InventoryDetail = views.InventoryDetail.as_view()
|
|||||||
# group service
|
# group service
|
||||||
|
|
||||||
# host service
|
# host service
|
||||||
|
views_HostsList = views.HostsList.as_view()
|
||||||
|
views_HostsDetail = views.HostsDetail.as_view()
|
||||||
|
|
||||||
# inventory variable service
|
# inventory variable service
|
||||||
|
|
||||||
@@ -92,9 +94,11 @@ urlpatterns = patterns('',
|
|||||||
url(r'^api/v1/inventories/$', views_InventoryList),
|
url(r'^api/v1/inventories/$', views_InventoryList),
|
||||||
url(r'^api/v1/inventories/(?P<pk>[0-9]+)/$', views_InventoryDetail),
|
url(r'^api/v1/inventories/(?P<pk>[0-9]+)/$', views_InventoryDetail),
|
||||||
|
|
||||||
# group service
|
|
||||||
|
|
||||||
# host service
|
# host service
|
||||||
|
url(r'^api/v1/hosts/$', views_HostsList),
|
||||||
|
url(r'^api/v1/hosts/(?P<pk>[0-9]+)/$', views_HostsDetail),
|
||||||
|
|
||||||
|
# group service
|
||||||
|
|
||||||
# inventory variable service
|
# inventory variable service
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user