Skip to content
This repository has been archived by the owner on Apr 28, 2020. It is now read-only.

Commit

Permalink
Remove obsolete ClientTeamAccess (resolves #76)
Browse files Browse the repository at this point in the history
  • Loading branch information
jace committed Mar 19, 2020
1 parent 2c583ee commit 4a907cc
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 219 deletions.
41 changes: 0 additions & 41 deletions lastuser_core/models/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@
__all__ = [
'AuthCode',
'AuthToken',
'CLIENT_TEAM_ACCESS',
'Client',
'ClientCredential',
'ClientTeamAccess',
'NoticeType',
'Permission',
'TeamClientPermissions',
Expand Down Expand Up @@ -98,8 +96,6 @@ class Client(ScopeMixin, BaseMixin, db.Model):
active = db.Column(db.Boolean, nullable=False, default=True)
#: Allow anyone to login to this app?
allow_any_login = db.Column(db.Boolean, nullable=False, default=True)
#: Team access flag
team_access = db.Column(db.Boolean, nullable=False, default=False)
#: OAuth client key/id
key = db.Column(db.String(22), nullable=False, unique=True, default=buid)
#: Trusted flag: trusted clients are authorized to access user data
Expand Down Expand Up @@ -170,16 +166,6 @@ def owner_is(self, user):
self.org and self.org in user.organizations_owned()
)

def orgs_with_team_access(self):
"""
Return a list of organizations that this client has access to the teams of.
"""
return [
cta.org
for cta in self.org_team_access
if cta.access_level == CLIENT_TEAM_ACCESS.ALL
]

def permissions(self, user, inherited=None):
perms = super(Client, self).permissions(user, inherited)
perms.add('view')
Expand Down Expand Up @@ -650,33 +636,6 @@ def buid(self):
return self.team.buid


class CLIENT_TEAM_ACCESS: # NOQA: N801
NONE = 0 # The default if there's no connecting object
ALL = 1 # All teams can be seen
PARTIAL = 2 # TODO: Not supported yet


class ClientTeamAccess(BaseMixin, db.Model):
__tablename__ = 'clientteamaccess'
#: Organization whose teams are exposed to the client app
org_id = db.Column(db.Integer, db.ForeignKey('organization.id'), nullable=True)
org = db.relationship(
Organization,
primaryjoin=org_id == Organization.id,
backref=db.backref('client_team_access', cascade='all, delete-orphan'),
)
#: Client app they are exposed to
client_id = db.Column(db.Integer, db.ForeignKey('client.id'), nullable=False)
client = db.relationship(
Client,
primaryjoin=client_id == Client.id,
backref=db.backref('org_team_access', cascade='all, delete-orphan'),
)
access_level = db.Column(
db.Integer, default=CLIENT_TEAM_ACCESS.NONE, nullable=False
)


class NoticeType(BaseMixin, db.Model):
__tablename__ = 'noticetype'
#: User who created this notice type
Expand Down
12 changes: 0 additions & 12 deletions lastuser_core/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -735,18 +735,6 @@ def pickername(self):
else:
return self.title

def clients_with_team_access(self):
"""
Return a list of clients with access to the organization's teams.
"""
from lastuser_core.models.client import CLIENT_TEAM_ACCESS

return [
cta.client
for cta in self.client_team_access
if cta.access_level == CLIENT_TEAM_ACCESS.ALL
]

def permissions(self, user, inherited=None):
perms = super(Organization, self).permissions(user, inherited)
if user and user in self.owners.users:
Expand Down
9 changes: 0 additions & 9 deletions lastuser_oauth/views/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,6 @@ def notify_org_data_changed(org, user, changes, team=None):
all other owners of this org to find apps that need to be notified.
"""
client_users = {}
if team is not None:
team_access = set(org.clients_with_team_access()) | (
set(user.clients_with_team_access()) if user else set()
)
else:
team_access = []
for token in AuthToken.all(users=org.owners.users):
if (
{'*', 'organizations', 'organizations/*'}.intersection(
Expand All @@ -114,9 +108,6 @@ def notify_org_data_changed(org, user, changes, team=None):
and token.client.notification_uri
and token.is_valid()
):
if team is not None:
if token.client not in team_access:
continue
client_users.setdefault(token.client, []).append(token.user)
# Now we have a list of clients to notify and a list of users to notify them with
for client, users in client_users.items():
Expand Down
39 changes: 0 additions & 39 deletions lastuser_oauth/views/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,45 +472,6 @@ def user_autocomplete():
return api_result('ok', users=result, _jsonp=True)


# This is org/* instead of organizations/* because it's a client resource. TODO: Reconsider
# DEPRECATED, to be removed soon
@lastuser_oauth.route('/api/1/org/get_teams', methods=['GET', 'POST'])
@requires_client_login
def org_team_get():
"""
Returns a list of teams in the given organization.
"""
if not current_auth.client.team_access:
return api_result('error', error='no_team_access')
org_buids = request.values.getlist('org')
if not org_buids:
return api_result('error', error='no_org_provided')
organizations = Organization.all(buids=org_buids)
if not organizations:
return api_result('error', error='no_such_organization')
orgteams = {}
for org in organizations:
# If client has access to team information, make a list of teams.
# XXX: Should trusted clients have access anyway? Will this be an abuse
# of the trusted flag? It was originally meant to only bypass user authorization
# on login to HasGeek websites as that would have been very confusing to users.
# XXX: Return user list here?
if current_auth.client in org.clients_with_team_access():
orgteams[org.buid] = [
{
'userid': team.buid,
'buid': team.buid,
'uuid': team.uuid,
'org': org.buid,
'org_uuid': org.uuid,
'title': team.title,
'owners': team == org.owners,
}
for team in org.teams
]
return api_result('ok', org_teams=orgteams)


# --- Public endpoints --------------------------------------------------------


Expand Down
17 changes: 0 additions & 17 deletions lastuser_ui/forms/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

__all__ = [
'ClientCredentialForm',
'ClientTeamAccessForm',
'ConfirmDeleteForm',
'PermissionEditForm',
'PermissionForm',
Expand Down Expand Up @@ -118,14 +117,6 @@ class RegisterClientForm(forms.Form):
"and only users who have been assigned a permission to the app will be able to login"
),
)
team_access = forms.BooleanField(
__("Requires access to teams"),
default=False,
description=__(
"If your application is capable of assigning access permissions to teams, check this. "
"Organization owners will then able to grant access to teams in their organizations"
),
)

def validate_client_owner(self, field):
if field.data == self.edit_user.buid:
Expand Down Expand Up @@ -328,11 +319,3 @@ class PermissionEditForm(forms.Form):
perms = forms.SelectMultipleField(
__("Permissions"), validators=[forms.validators.DataRequired()]
)


class ClientTeamAccessForm(forms.Form):
"""
Select organizations that the client has access to the teams of
"""

organizations = forms.SelectMultipleField(__("Organizations"))
23 changes: 0 additions & 23 deletions lastuser_ui/templates/client_info.html.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
<li><a href="{{ url_for('.client_cred_new', key=client.key) }}" title="New access key">New access key</a></li>
<li><a href="{{ url_for('.permission_new') }}">Define a new permission</a></li>
<li><a href="{{ url_for('.permission_user_new', key=client.key) }}">Assign permissions to another {% if client.user %}user{% else %}team{% endif %}</a></li>
{% if current_auth.is_authenticated and client.team_access and current_auth.user.organizations_owned() %}
<li><a href="{{ url_for('.client_team_access', key=client.key) }}" title="Change access permissions">Change access permissions</a></li>
{% endif %}
</ol>
</div>
</div>
Expand Down Expand Up @@ -128,26 +125,6 @@
</div>
{%- endif %}

{% if current_auth.is_authenticated and client.team_access and current_auth.user.organizations_owned() %}
<div class="grid__col--bleed-y">
<h2>Team Access</h2>
<p>This application can manage access permissions per team. Select the organizations whose teams it has access to.</p>
</div>
<div class="grid grid--align-baseline">
{% for org in current_auth.user.organizations_owned() %}
<div class="grid__col-auto">
<div class="card card--limited">
<div class="card__header">
<h3 class="mui--text-title">{{ org.title }}</h3>
</div>
<div class="card__body card--text mui--text-subhead">
<p><b>Access</b>: {% if client in org.clients_with_team_access() %}Yes{% else %}No{% endif %}</p>
</div>
</div>
</div>
{% endfor %}
</div>
{%- endif %}
</div>
<div>
{% endblock %}
53 changes: 0 additions & 53 deletions lastuser_ui/views/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@
from coaster.auth import current_auth
from coaster.views import load_model, load_models
from lastuser_core.models import (
CLIENT_TEAM_ACCESS,
Client,
ClientCredential,
ClientTeamAccess,
Organization,
Permission,
Team,
TeamClientPermissions,
Expand All @@ -24,7 +21,6 @@
from .. import lastuser_ui
from ..forms import (
ClientCredentialForm,
ClientTeamAccessForm,
PermissionEditForm,
PermissionForm,
RegisterClientForm,
Expand Down Expand Up @@ -143,10 +139,6 @@ def client_edit(client):
form.populate_obj(client)
client.user = form.user
client.org = form.org
if not client.team_access:
# This client does not have access to teams in organizations. Remove all existing assignments
for cta in ClientTeamAccess.query.filter_by(client=client).all():
db.session.delete(cta)
db.session.commit()
return render_redirect(url_for('.client_info', key=client.key), code=303)

Expand Down Expand Up @@ -527,48 +519,3 @@ def permission_user_delete(client, kwargs):
),
next=url_for('.client_info', key=client.key),
)


# --- Routes: client team access ----------------------------------------------


@lastuser_ui.route('/apps/<key>/teams', methods=['GET', 'POST'])
@requires_login
@load_model(Client, {'key': 'key'}, 'client')
def client_team_access(client):
form = ClientTeamAccessForm()
user_orgs = current_auth.user.organizations_owned()
form.organizations.choices = [(org.buid, org.title) for org in user_orgs]
org_selected = [
org.buid for org in user_orgs if client in org.clients_with_team_access()
]
if request.method == 'GET':
form.organizations.data = org_selected
if form.validate_on_submit():
org_del = Organization.query.filter(
Organization.buid.in_(set(org_selected) - set(form.organizations.data))
).all()
org_add = Organization.query.filter(
Organization.buid.in_(set(form.organizations.data) - set(org_selected))
).all()
cta_del = (
ClientTeamAccess.query.filter_by(client=client)
.filter(ClientTeamAccess.org_id.in_([org.id for org in org_del]))
.all()
)
for cta in cta_del:
db.session.delete(cta)
for org in org_add:
cta = ClientTeamAccess(
org=org, client=client, access_level=CLIENT_TEAM_ACCESS.ALL
)
db.session.add(cta)
db.session.commit()
flash(
_("You have assigned access to teams in your organizations for this app"),
'success',
)
return render_redirect(url_for('.client_info', key=client.key), code=303)
return render_form(
form=form, title=_("Select organizations"), submit=_("Save"), ajax=True
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
"""Remove obsolete ClientTeamAccess
Revision ID: f65c00c0cfc3
Revises: 4b6d120b1612
Create Date: 2020-03-20 04:42:28.526187
"""
from alembic import op
from sqlalchemy.dialects import postgresql
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = 'f65c00c0cfc3'
down_revision = '4b6d120b1612'
branch_labels = None
depends_on = None


def upgrade():
op.drop_table('clientteamaccess')
op.drop_column('client', 'team_access')


def downgrade():
op.add_column(
'client',
sa.Column('team_access', sa.BOOLEAN(), autoincrement=False, nullable=False),
)
op.create_table(
'clientteamaccess',
sa.Column('org_id', sa.INTEGER(), autoincrement=False, nullable=True),
sa.Column('client_id', sa.INTEGER(), autoincrement=False, nullable=False),
sa.Column('access_level', sa.INTEGER(), autoincrement=False, nullable=False),
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column(
'created_at',
postgresql.TIMESTAMP(timezone=True),
autoincrement=False,
nullable=False,
),
sa.Column(
'updated_at',
postgresql.TIMESTAMP(timezone=True),
autoincrement=False,
nullable=False,
),
sa.ForeignKeyConstraint(
['client_id'], ['client.id'], name='clientteamaccess_client_id_fkey'
),
sa.ForeignKeyConstraint(
['org_id'], ['organization.id'], name='clientteamaccess_org_id_fkey'
),
sa.PrimaryKeyConstraint('id', name='clientteamaccess_pkey'),
)
7 changes: 0 additions & 7 deletions tests/unit/lastuser_core/fixtures.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# -*- coding: utf-8 -*-

from lastuser_core.models import (
CLIENT_TEAM_ACCESS,
Client,
ClientTeamAccess,
Organization,
Permission,
SMSMessage,
Expand Down Expand Up @@ -75,11 +73,6 @@ def make_fixtures(self):
self.team_client_permission = team_client_permission
db.session.add(team_client_permission)

client_team_access = ClientTeamAccess(
org=batdog, client=client, access_level=CLIENT_TEAM_ACCESS.ALL
)
db.session.add(client_team_access)

bdfl = Permission(name="bdfl", title="BDFL", user=crusoe)
db.session.add(bdfl)
self.bdfl = bdfl
Expand Down
Loading

0 comments on commit 4a907cc

Please sign in to comment.