mirror of
https://github.com/ansible/awx.git
synced 2024-10-30 05:25:29 +03:00
Working on Hosts and Groups
This commit is contained in:
parent
a4ae1012bc
commit
5e91e0a43c
3
TODO.md
3
TODO.md
@ -27,5 +27,6 @@ TWEAKS/ASSORTED
|
||||
* 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?)
|
||||
* 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
|
||||
* audit/test read only fields like creation_date
|
||||
|
@ -40,11 +40,14 @@ class BaseList(generics.ListCreateAPIView):
|
||||
return True
|
||||
if request.method == 'POST':
|
||||
if self.__class__.model in [ User ]:
|
||||
# Django user gets special handling since it's not our class
|
||||
# org admins are allowed to create users
|
||||
return self.request.user.is_superuser or (self.request.user.admin_of_organizations.count() > 0)
|
||||
ok = self.request.user.is_superuser or (self.request.user.admin_of_organizations.count() > 0)
|
||||
if not ok:
|
||||
raise PermissionDenied()
|
||||
return True
|
||||
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
|
||||
|
||||
def get_queryset(self):
|
||||
@ -112,7 +115,7 @@ class BaseDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
obj = self.model.objects.get(pk=kwargs['pk'])
|
||||
if not request.user.is_superuser and not self.delete_permissions_check(request, obj):
|
||||
raise PermissionDenied()
|
||||
if isinstance(obj, CommonModel):
|
||||
if isinstance(obj, PrimordialModel):
|
||||
obj.name = "_deleted_%s_%s" % (str(datetime.time()), obj.name)
|
||||
obj.active = False
|
||||
obj.save()
|
||||
@ -125,7 +128,7 @@ class BaseDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
return HttpResponse(status=204)
|
||||
|
||||
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)
|
||||
elif isinstance(obj, User):
|
||||
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
|
||||
|
||||
|
||||
class CommonModel(models.Model):
|
||||
class PrimordialModel(models.Model):
|
||||
'''
|
||||
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:
|
||||
abstract = True
|
||||
|
||||
name = models.CharField(max_length=512, unique=True)
|
||||
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!
|
||||
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):
|
||||
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):
|
||||
'''
|
||||
any type of object can be given a search tag
|
||||
@ -268,7 +285,7 @@ class Inventory(CommonModel):
|
||||
app_label = 'main'
|
||||
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):
|
||||
import lib.urls
|
||||
@ -297,10 +314,34 @@ class Inventory(CommonModel):
|
||||
result = (by_org_admin + by_team_permission + by_user_permission)
|
||||
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
|
||||
def can_user_add(cls, user, data):
|
||||
if not 'organization' in data:
|
||||
return False
|
||||
return True
|
||||
if user.is_superuser:
|
||||
return True
|
||||
if not user.is_superuser:
|
||||
@ -322,7 +363,7 @@ class Inventory(CommonModel):
|
||||
return cls._has_permission_types(user, obj, PERMISSION_TYPES_ALLOWING_INVENTORY_ADMIN)
|
||||
|
||||
|
||||
class Host(CommonModel):
|
||||
class Host(CommonModelNameNotUnique):
|
||||
'''
|
||||
A managed node
|
||||
'''
|
||||
@ -330,12 +371,26 @@ class Host(CommonModel):
|
||||
class Meta:
|
||||
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):
|
||||
return self.name
|
||||
|
||||
@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):
|
||||
|
||||
class Group(CommonModel):
|
||||
'''
|
||||
A group of managed nodes. May belong to multiple groups
|
||||
'''
|
||||
@ -343,13 +398,20 @@ class Group(CommonModel):
|
||||
class Meta:
|
||||
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)
|
||||
hosts = models.ManyToManyField('Host', related_name='groups', blank=True)
|
||||
|
||||
def __unicode__(self):
|
||||
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 cascades
|
||||
|
||||
@ -362,8 +424,8 @@ class VariableData(CommonModel):
|
||||
app_label = 'main'
|
||||
verbose_name_plural = _('variable data')
|
||||
|
||||
host = models.ForeignKey('Host', 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=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=SET_NULL, related_name='variable_data')
|
||||
data = models.TextField() # FIXME: JsonField
|
||||
|
||||
def __unicode__(self):
|
||||
|
@ -90,9 +90,36 @@ class InventorySerializer(BaseSerializer):
|
||||
fields = ('url', 'id', 'name', 'description', 'creation_date', 'organization')
|
||||
|
||||
def get_related(self, obj):
|
||||
# FIXME: add related resources: inventories
|
||||
# FIXME: add related resources: hosts, groups
|
||||
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):
|
||||
|
||||
@ -126,7 +153,6 @@ class UserSerializer(BaseSerializer):
|
||||
admin_of_organizations = reverse(lib.urls.views_UsersAdminOrganizationsList, args=(obj.pk,)),
|
||||
)
|
||||
|
||||
|
||||
def get_absolute_url_override(self, obj):
|
||||
import lib.urls
|
||||
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
|
||||
# the organization parameter is required!
|
||||
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)
|
||||
|
||||
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)
|
||||
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
|
||||
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
|
||||
|
||||
@ -134,6 +159,8 @@ class InventoryTest(BaseTest):
|
||||
# a normal user cannot 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
|
||||
|
||||
|
@ -282,3 +282,37 @@ class InventoryDetail(BaseDetail):
|
||||
serializer_class = InventorySerializer
|
||||
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
|
||||
|
||||
# host service
|
||||
views_HostsList = views.HostsList.as_view()
|
||||
views_HostsDetail = views.HostsDetail.as_view()
|
||||
|
||||
# inventory variable service
|
||||
|
||||
@ -92,9 +94,11 @@ urlpatterns = patterns('',
|
||||
url(r'^api/v1/inventories/$', views_InventoryList),
|
||||
url(r'^api/v1/inventories/(?P<pk>[0-9]+)/$', views_InventoryDetail),
|
||||
|
||||
# group 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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user