Skip to content

Commit

Permalink
user-credential categories
Browse files Browse the repository at this point in the history
  • Loading branch information
ansibleguy committed May 12, 2024
1 parent e2c845a commit 34e64cc
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

* Enhanced graceful stop
* Alerting
* E-Mail
* Plugin System
* Credential categories
* Fix for SSH-RSA key usage (*error in libcrypto*)

----

Expand Down
6 changes: 5 additions & 1 deletion docs/source/usage/credentials.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ User Credentials

User credential can only be used and accessed by the user that created them.

Jobs that are executed by an user will use the first user-credentials found as a fallback in case no other credentials were provided/configured (*but the job is set to need credentials*)!
Jobs that are executed by an user will use: (*if the job is set to need credentials*)

* the user-credentials matching the jobs :code:`credential category`

* or the first user-credentials found as a fallback in case no other credentials were provided/configured

----

Expand Down
1 change: 1 addition & 0 deletions src/ansibleguy-webui/aw/api_endpoints/permission.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class Meta:
repositories = serializers.MultipleChoiceField(allow_blank=True, choices=[])
users = serializers.MultipleChoiceField(allow_blank=True, choices=[])
groups = serializers.MultipleChoiceField(allow_blank=True, choices=[])
name = serializers.CharField(validators=[]) # uc on update

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down
2 changes: 2 additions & 0 deletions src/ansibleguy-webui/aw/api_endpoints/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class Meta:
model = Repository
fields = Repository.api_fields_write

name = serializers.CharField(validators=[]) # uc on update


class RepositoryReadResponse(RepositoryWriteRequest):
class Meta:
Expand Down
5 changes: 5 additions & 0 deletions src/ansibleguy-webui/aw/config/form_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'tags_skip': 'Skip Tags',
'credentials_needed': 'Needs Credentials',
'credentials_default': 'Default Job Credentials',
'credentials_category': 'Credentials Category',
'form': 'Execution Form',
},
'credentials': {
Expand All @@ -23,6 +24,7 @@
'vault_pass': 'Vault Password',
'vault_id': 'Vault ID',
'ssh_key': 'SSH Private Key',
'category': 'Category',
},
'repository': {
'rtype': 'Repository Type',
Expand Down Expand Up @@ -133,6 +135,8 @@
'(either as default or at execution-time; '
'fallback are the user-credentials of the executing user)',
'credentials_default': 'Specify job-level default credentials to use',
'credentials_category': 'The credential category can be used for dynamic matching of '
'user credentials at execution time',
'enabled': 'En- or disable the schedule. Can be ignored if no schedule was set',
'form': 'Select a Job-Execution form to display on ad-hoc executions',
},
Expand All @@ -143,6 +147,7 @@
'vault_managing_passwords.html">'
'Ansible Docs - Managing Passwords</a>',
'ssh_key': 'Provide an unencrypted SSH private key',
'category': 'The category of user credentials. Used for dynamic matching at execution time',
},
'repository': {
'static_path': 'Path to the local static repository/playbook-base-directory',
Expand Down
20 changes: 14 additions & 6 deletions src/ansibleguy-webui/aw/execute/play_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,20 @@ def get_credentials_to_use(job: Job, execution: JobExecution) -> (BaseJobCredent
credentials = job.credentials_default

elif job.credentials_needed and is_set(execution.user):
# try to get default user-credentials as a last-resort if the job needs some credentials
try:
credentials = JobUserCredentials.objects.filter(user=execution.user).first()

except ObjectDoesNotExist:
pass
# get user credentials that match the job credential-category
if is_set(job.credentials_category):
for user_creds in JobUserCredentials.objects.filter(user=execution.user):
if user_creds.category == job.credentials_category:
credentials = user_creds
break

if credentials is None:
# try to get default user-credentials as a last-resort if the job needs some credentials
try:
credentials = JobUserCredentials.objects.filter(user=execution.user).first()

except ObjectDoesNotExist:
pass

if job.credentials_needed and credentials is None:
config_error(
Expand Down
3 changes: 2 additions & 1 deletion src/ansibleguy-webui/aw/model/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class Job(BaseJob):
CHANGE_FIELDS = [
'name', 'playbook_file', 'inventory_file', 'repository', 'schedule', 'enabled', 'limit', 'verbosity',
'mode_diff', 'mode_check', 'tags', 'tags_skip', 'verbosity', 'comment', 'environment_vars', 'cmd_args',
'credentials_default', 'credentials_needed',
'credentials_default', 'credentials_needed', 'credentials_category',
]
form_fields_primary = ['name', 'playbook_file', 'inventory_file', 'repository']
form_fields = CHANGE_FIELDS
Expand All @@ -101,6 +101,7 @@ class Job(BaseJob):
credentials_default = models.ForeignKey(
JobGlobalCredentials, on_delete=models.SET_NULL, related_name='job_fk_creddflt', null=True, blank=True,
)
credentials_category = models.CharField(max_length=100, **DEFAULT_NONE)
repository = models.ForeignKey(Repository, on_delete=models.SET_NULL, related_name='job_fk_repo', **DEFAULT_NONE)

def __str__(self) -> str:
Expand Down
13 changes: 5 additions & 8 deletions src/ansibleguy-webui/aw/model/job_credential.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class Meta:

class JobGlobalCredentials(BaseJobCredentials):
api_fields_read = [
'id', 'name', 'become_user', 'connect_user', 'vault_file', 'vault_id'
'id', 'name', 'become_user', 'connect_user', 'vault_file', 'vault_id',
]
api_fields_write = api_fields_read.copy()
api_fields_write.extend(BaseJobCredentials.SECRET_ATTRS)
Expand All @@ -142,17 +142,14 @@ class Meta:

class JobUserCredentials(BaseJobCredentials):
api_fields_read = JobGlobalCredentials.api_fields_read.copy()
api_fields_read.append('user')
api_fields_read.extend(['user', 'category'])
api_fields_write = JobGlobalCredentials.api_fields_write.copy()
api_fields_write.append('user')
api_fields_write.extend(['user', 'category'])
form_fields = JobGlobalCredentials.api_fields_write.copy()
form_fields.append('category')

user = models.ForeignKey(USERS, on_delete=models.CASCADE)
category = models.CharField(max_length=100, **DEFAULT_NONE)

def __str__(self) -> str:
return f"Credentials '{self.name}' of user '{self.user.username}'{self._get_set_creds_str()}"

class Meta:
constraints = [
models.UniqueConstraint(fields=['user', 'name'], name='jobusercreds_user_name')
]

0 comments on commit 34e64cc

Please sign in to comment.