From 6edc5180c7a253bd5c3226db6116e0f91d668b96 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 11 Dec 2017 14:51:45 -0700 Subject: [PATCH] fix roles assigned in the ui for sso (#1017) This commit fixes the ability to assign roles to people in the ui when the user is SSO. The idea is if a role is ever assigned via SSO it becomes a "SSO Role" or a "Third Party" Role. by setting third_party to true on the role object. Once a role is marked as third party it can no longer be controlled through the ui for SSO Users. (for ui users this poses no functional change). It must be controlled via SSO. --- CHANGELOG.rst | 2 ++ lemur/auth/ldap.py | 11 ++++++++-- lemur/auth/views.py | 24 ++++++++++++++++------ lemur/migrations/versions/5bc47fa7cac4_.py | 22 ++++++++++++++++++++ lemur/roles/models.py | 3 ++- lemur/roles/schemas.py | 1 + lemur/roles/service.py | 19 +++++++++++++++-- 7 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 lemur/migrations/versions/5bc47fa7cac4_.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a3358b8b..cf5d8a86 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,8 @@ is 30 days. Every certificate will gain a policy regardless is auto-rotation is Adds per-user API Keys, requires a database migration. +Adds third_party to roles for external authentication roles, requires a database migration. + .. note:: This version is not yet released and is under active development diff --git a/lemur/auth/ldap.py b/lemur/auth/ldap.py index e808a199..e72469a5 100644 --- a/lemur/auth/ldap.py +++ b/lemur/auth/ldap.py @@ -65,7 +65,7 @@ class LdapPrincipal(): else: # we add 'lemur' specific roles, so they do not get marked as removed for ur in user.roles: - if ur.authority_id: + if not ur.third_party: roles.add(ur) # update any changes to the user @@ -97,13 +97,18 @@ class LdapPrincipal(): if self.ldap_default_role: role = role_service.get_by_name(self.ldap_default_role) if role: + if not role.third_party: + role = role.set_third_party(role.id, third_party_status=True) roles.add(role) # update their 'roles' role = role_service.get_by_name(self.ldap_principal) if not role: description = "auto generated role based on owner: {0}".format(self.ldap_principal) - role = role_service.create(self.ldap_principal, description=description) + role = role_service.create(self.ldap_principal, description=description, + third_party=True) + if not role.third_party: + role = role_service.set_third_party(role.id, third_party_status=True) roles.add(role) if not self.ldap_groups_to_roles: return roles @@ -113,6 +118,8 @@ class LdapPrincipal(): if role: if ldap_group_name in self.ldap_groups: current_app.logger.debug("assigning role {0} to ldap user {1}".format(self.ldap_principal, role)) + if not role.third_party: + role = role_service.set_third_party(role.id, third_party_status=True) roles.add(role) return roles diff --git a/lemur/auth/views.py b/lemur/auth/views.py index 47d26d7b..af04d8c5 100644 --- a/lemur/auth/views.py +++ b/lemur/auth/views.py @@ -211,13 +211,17 @@ class Ping(Resource): for group in profile['googleGroups']: role = role_service.get_by_name(group) if not role: - role = role_service.create(group, description='This is a google group based role created by Lemur') + role = role_service.create(group, description='This is a google group based role created by Lemur', third_party=True) + if not role.third_party: + role = role_service.set_third_party(role.id, third_party_status=True) roles.append(role) role = role_service.get_by_name(profile['email']) if not role: - role = role_service.create(profile['email'], description='This is a user specific role') + role = role_service.create(profile['email'], description='This is a user specific role', third_party=True) + if not role.third_party: + role = role_service.set_third_party(role.id, third_party_status=True) roles.append(role) @@ -226,6 +230,8 @@ class Ping(Resource): default = role_service.get_by_name(current_app.config['LEMUR_DEFAULT_ROLE']) if not default: default = role_service.create(current_app.config['LEMUR_DEFAULT_ROLE'], description='This is the default Lemur role.') + if not default.third_party: + role_service.set_third_party(default.id, third_party_status=True) roles.append(default) # if we get an sso user create them an account @@ -242,7 +248,7 @@ class Ping(Resource): else: # we add 'lemur' specific roles, so they do not get marked as removed for ur in user.roles: - if ur.authority_id: + if not ur.third_party: roles.append(ur) # update any changes to the user @@ -352,12 +358,16 @@ class OAuth2(Resource): for group in profile['roles']: role = role_service.get_by_name(group) if not role: - role = role_service.create(group, description='This is a group configured by identity provider') + role = role_service.create(group, description='This is a group configured by identity provider', third_party=True) + if not role.third_party: + role = role_service.set_third_party(role.id, third_party_status=True) roles.append(role) role = role_service.get_by_name(profile['email']) if not role: - role = role_service.create(profile['email'], description='This is a user specific role') + role = role_service.create(profile['email'], description='This is a user specific role', third_party=True) + if not role.third_party: + role = role_service.set_third_party(role.id, third_party_status=True) roles.append(role) # if we get an sso user create them an account @@ -365,6 +375,8 @@ class OAuth2(Resource): # every user is an operator (tied to a default role) if current_app.config.get('LEMUR_DEFAULT_ROLE'): v = role_service.get_by_name(current_app.config.get('LEMUR_DEFAULT_ROLE')) + if not v.third_party: + v = role_service.set_third_party(v.id, third_party_status=True) if v: roles.append(v) @@ -380,7 +392,7 @@ class OAuth2(Resource): else: # we add 'lemur' specific roles, so they do not get marked as removed for ur in user.roles: - if ur.authority_id: + if not ur.third_party: roles.append(ur) # update any changes to the user diff --git a/lemur/migrations/versions/5bc47fa7cac4_.py b/lemur/migrations/versions/5bc47fa7cac4_.py new file mode 100644 index 00000000..f4a145c8 --- /dev/null +++ b/lemur/migrations/versions/5bc47fa7cac4_.py @@ -0,0 +1,22 @@ +"""add third party roles to lemur + +Revision ID: 5bc47fa7cac4 +Revises: c05a8998b371 +Create Date: 2017-12-08 14:19:11.903864 + +""" + +# revision identifiers, used by Alembic. +revision = '5bc47fa7cac4' +down_revision = 'c05a8998b371' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('roles', sa.Column('third_party', sa.Boolean(), nullable=True, default=False)) + + +def downgrade(): + op.drop_column('roles', 'third_party') diff --git a/lemur/roles/models.py b/lemur/roles/models.py index b324360e..3ecd9188 100644 --- a/lemur/roles/models.py +++ b/lemur/roles/models.py @@ -10,7 +10,7 @@ """ from sqlalchemy.orm import relationship -from sqlalchemy import Column, Integer, String, Text, ForeignKey +from sqlalchemy import Boolean, Column, Integer, String, Text, ForeignKey from lemur.database import db from lemur.utils import Vault @@ -27,6 +27,7 @@ class Role(db.Model): authority_id = Column(Integer, ForeignKey('authorities.id')) authorities = relationship("Authority", secondary=roles_authorities, passive_deletes=True, backref="role", cascade='all,delete') user_id = Column(Integer, ForeignKey('users.id')) + third_party = Column(Boolean) users = relationship("User", secondary=roles_users, passive_deletes=True, backref="role") certificates = relationship("Certificate", secondary=roles_certificates, backref="role") diff --git a/lemur/roles/schemas.py b/lemur/roles/schemas.py index 246f5828..bbeb8ef7 100644 --- a/lemur/roles/schemas.py +++ b/lemur/roles/schemas.py @@ -26,6 +26,7 @@ class RoleOutputSchema(LemurOutputSchema): id = fields.Integer() name = fields.String() description = fields.String() + third_party = fields.Boolean() authorities = fields.Nested(AuthorityNestedOutputSchema, many=True) users = fields.Nested(UserNestedOutputSchema, many=True) diff --git a/lemur/roles/service.py b/lemur/roles/service.py index 248e34ca..352ebf1f 100644 --- a/lemur/roles/service.py +++ b/lemur/roles/service.py @@ -32,7 +32,22 @@ def update(role_id, name, description, users): return role -def create(name, password=None, description=None, username=None, users=None): +def set_third_party(role_id, third_party_status=False): + """ + Sets a role to be a third party role. A user should pretty much never + call this directly. + + :param role_id: + :param third_party_status: + :return: + """ + role = get(role_id) + role.third_party = third_party_status + database.update(role) + return role + + +def create(name, password=None, description=None, username=None, users=None, third_party=False): """ Create a new role @@ -43,7 +58,7 @@ def create(name, password=None, description=None, username=None, users=None): :param password: :return: """ - role = Role(name=name, description=description, username=username, password=password) + role = Role(name=name, description=description, username=username, password=password, third_party=third_party) if users: role.users = users