#95 add proxyuser waiving support
Merged 6 years ago by ralph. Opened 6 years ago by mjia.
mjia/waiverdb issue77  into  master

file modified
+46 -1
@@ -9,7 +9,7 @@ 

  

  

  @patch('waiverdb.auth.get_user', return_value=('foo', {}))

- def test_create_waiver(mocked_get_user, client, session, monkeypatch):

+ def test_create_waiver(mocked_get_user, client, session):

      data = {

          'result_id': 123,

          'product_version': 'fool-1',
@@ -39,6 +39,40 @@ 

      assert 'invalid literal for int()' in res_data['message']['result_id']

  

  

+ @patch('waiverdb.auth.get_user', return_value=('foo', {}))

+ def test_non_superuser_cannot_create_waiver_for_other_users(mocked_get_user, client):

+     data = {

+         'result_id': 123,

+         'product_version': 'fool-1',

+         'waived': True,

+         'comment': 'it broke',

+         'proxy_user': 'bar',

+     }

+     r = client.post('/api/v1.0/waivers/', data=json.dumps(data),

+                     content_type='application/json')

+     res_data = json.loads(r.get_data(as_text=True))

+     assert r.status_code == 403

+     assert 'user foo does not have the proxyuser ability' == res_data['message']

+ 

+ 

+ @patch('waiverdb.auth.get_user', return_value=('bodhi', {}))

+ def test_superuser_can_create_waiver_for_other_users(mocked_get_user, client, session):

+     data = {

+         'result_id': 123,

+         'product_version': 'fool-1',

+         'waived': True,

+         'comment': 'it broke',

+         'proxy_user': 'bar',

I would have just made this 'username' so that it matches the same key coming out the other side in the GET request... but anyway too late now. :-)

mjia commented 6 years ago

Yeah, that's a good point.

+     }

+     r = client.post('/api/v1.0/waivers/', data=json.dumps(data),

+                     content_type='application/json')

+     res_data = json.loads(r.get_data(as_text=True))

+     assert r.status_code == 201

+     # a waiver should be created for bar by bodhi

+     assert res_data['username'] == 'bar'

+     assert res_data['proxied_by'] == 'bodhi'

+ 

+ 

  def test_get_waiver(client, session):

      # create a new waiver

      waiver = create_waiver(session, result_id=123, username='foo',
@@ -191,6 +225,17 @@ 

      assert len(res_data['data']) == 0

  

  

+ def test_filtering_waivers_by_proxied_by(client, session):

+     create_waiver(session, result_id=123, username='foo-1', product_version='foo-1',

+                   proxied_by='bodhi')

+     create_waiver(session, result_id=234, username='foo-2', product_version='foo-1')

+     r = client.get('/api/v1.0/waivers/?proxied_by=bodhi')

+     res_data = json.loads(r.get_data(as_text=True))

+     assert r.status_code == 200

+     assert len(res_data['data']) == 1

+     assert res_data['data'][0]['result_id'] == 123

+ 

+ 

  def test_jsonp(client, session):

      waiver = create_waiver(session, result_id=123, username='foo', product_version='foo-1')

      r = client.get('/api/v1.0/waivers/%s?callback=jsonpcallback' % waiver.id)

file modified
+29
@@ -24,6 +24,35 @@ 

              'id': waiver.id,

              'result_id': 1,

              'username': 'jcline',

+             'proxied_by': None,

+             'product_version': 'something',

+             'waived': True,

+             'comment': 'This is a comment',

+             'timestamp': waiver.timestamp.isoformat(),

+         }

+     )

+ 

+ 

+ @mock.patch('waiverdb.events.fedmsg')

+ def test_publish_new_waiver_with_fedmsg_for_proxy_user(mock_fedmsg, session):

+     waiver = Waiver(

+         result_id=1,

+         username='jcline',

+         product_version='something',

+         waived=True,

+         comment='This is a comment',

+         proxied_by='bodhi'

+     )

+     sesh = session()

+     sesh.add(waiver)

+     sesh.commit()

+     mock_fedmsg.publish.assert_called_once_with(

+         topic='waiver.new',

+         msg={

+             'id': waiver.id,

+             'result_id': 1,

+             'username': 'jcline',

+             'proxied_by': 'bodhi',

              'product_version': 'something',

              'waived': True,

              'comment': 'This is a comment',

file modified
+2 -2
@@ -4,8 +4,8 @@ 

  

  

  def create_waiver(session, result_id, username, product_version, waived=True,

-                   comment=None):

-     waiver = Waiver(result_id, username, product_version, waived, comment)

+                   comment=None, proxied_by=None):

+     waiver = Waiver(result_id, username, product_version, waived, comment, proxied_by)

      session.add(waiver)

      session.flush()

      return waiver

file modified
+25 -5
@@ -2,7 +2,7 @@ 

  

  from flask import Blueprint, request, current_app

  from flask_restful import Resource, Api, reqparse, marshal_with, marshal

- from werkzeug.exceptions import BadRequest, NotFound, UnsupportedMediaType

+ from werkzeug.exceptions import BadRequest, NotFound, UnsupportedMediaType, Forbidden

  from sqlalchemy.sql.expression import func

  

  from waiverdb import __version__
@@ -23,6 +23,7 @@ 

  RP['create_waiver'].add_argument('waived', type=bool, required=True, location='json')

  RP['create_waiver'].add_argument('product_version', type=str, required=True, location='json')

  RP['create_waiver'].add_argument('comment', type=str, default=None, location='json')

+ RP['create_waiver'].add_argument('proxy_user', type=str, default=None, location='json')

  

  RP['get_waivers'] = reqparse.RequestParser()

  RP['get_waivers'].add_argument('result_id', location='args')
@@ -34,6 +35,7 @@ 

  RP['get_waivers'].add_argument('since', location='args')

  RP['get_waivers'].add_argument('page', default=1, type=int, location='args')

  RP['get_waivers'].add_argument('limit', default=10, type=int, location='args')

+ RP['get_waivers'].add_argument('proxied_by', location='args')

  

  

  class WaiversResource(Resource):
@@ -76,6 +78,8 @@ 

              more result IDs separated by commas.

          :query string product_version: Filter the waivers by product version.

          :query string username: Filter the waivers by username.

+         :query string proxied_by: Filter the waivers by the users who are

+             allowed to create waivers on behalf of other users.

          :query string since: An ISO 8601 formatted datetime (e.g. 2017-03-16T13:40:05+00:00)

              to filter results by. Optionally provide a second ISO 8601 datetime separated

              by a comma to retrieve a range (e.g. 2017-03-16T13:40:05+00:00,
@@ -93,6 +97,8 @@ 

              query = query.filter(Waiver.product_version == args['product_version'])

          if args['username']:

              query = query.filter(Waiver.username == args['username'])

+         if args['proxied_by']:

+             query = query.filter(Waiver.proxied_by == args['proxied_by'])

          if args['since']:

              try:

                  since_start, since_end = reqparse_since(args['since'])
@@ -152,19 +158,27 @@ 

                 "result_id": 1,

                 "timestamp": "2017-03-16T17:42:04.209638",

                 "username": "jcline",

-                "waived": false

+                "waived": false,

+                "proxied_by": null

             }

  

          :json int result_id: The result ID for the waiver.

          :json boolean waived: Whether or not the result is waived.

          :json string product_version: The product version string.

          :json string comment: A comment explaining the waiver.

+         :json string proxy_user: Username on whose behalf the caller is proxying.

          :statuscode 201: The waiver was successfully created.

          """

          user, headers = waiverdb.auth.get_user(request)

          args = RP['create_waiver'].parse_args()

+         proxied_by = None

+         if args.get('proxy_user'):

+             if user not in current_app.config['SUPERUSERS']:

+                 raise Forbidden('user %s does not have the proxyuser ability' % user)

+             proxied_by = user

+             user = args['proxy_user']

          waiver = Waiver(args['result_id'], user, args['product_version'], args['waived'],

-                         args['comment'])

+                         args['comment'], proxied_by)

          db.session.add(waiver)

          db.session.commit()

          return waiver, 201, headers
@@ -233,7 +247,8 @@ 

                          "result_id": 2,

                          "timestamp": "2017-09-21T04:55:53.343368",

                          "username": "dummy",

-                         "waived": true

+                         "waived": true,

+                         "proxied_by": null

                      },

                      {

                          "comment": "It's dead!",
@@ -242,7 +257,8 @@ 

                          "result_id": 1,

                          "timestamp": "2017-09-21T04:55:51.936658",

                          "username": "dummy",

-                         "waived": true

+                         "waived": true,

+                         "proxied_by": null

                      }

                  ]

              }
@@ -250,6 +266,8 @@ 

          :jsonparam array result_ids: Filter the waivers by a list of result IDs.

          :jsonparam string product_version: Filter the waivers by product version.

          :jsonparam string username: Filter the waivers by username.

+         :jsonparam string proxied_by: Filter the waivers by the users who are

+             allowed to create waivers on behalf of other users.

          :jsonparam string since: An ISO 8601 formatted datetime (e.g. 2017-03-16T13:40:05+00:00)

              to filter results by. Optionally provide a second ISO 8601 datetime separated

              by a comma to retrieve a range (e.g. 2017-03-16T13:40:05+00:00,
@@ -268,6 +286,8 @@ 

              query = query.filter(Waiver.product_version == data['product_version'])

          if 'username' in data:

              query = query.filter(Waiver.username == data['username'])

+         if 'proxied_by' in data:

+             query = query.filter(Waiver.proxied_by == data['proxied_by'])

          if 'since' in data:

              try:

                  since_start, since_end = reqparse_since(data['since'])

file modified
+3
@@ -30,6 +30,8 @@ 

      # Specify fedmsg or stomp for publishing messages

      MESSAGE_PUBLISHER = 'fedmsg'

      SQLALCHEMY_TRACK_MODIFICATIONS = True

+     # A list of users are allowed to create waivers on behalf of other users.

+     SUPERUSERS = []

  

  

  class ProductionConfig(Config):
@@ -61,3 +63,4 @@ 

      )

      OIDC_REQUIRED_SCOPE = 'waiverdb_scope'

      OIDC_RESOURCE_SERVER_ONLY = True

+     SUPERUSERS = ['bodhi']

file modified
+1
@@ -49,6 +49,7 @@ 

              "timestamp": "2017-03-16T17:42:04.209638",

              "product_version": "Satellite 6.3",

              "result_id": 1,

+             "proxied_by": null,

              "id": 15

            }

          }

file modified
+1
@@ -6,6 +6,7 @@ 

      'id': fields.Integer,

      'result_id': fields.Integer,

      'username': fields.String,

+     'proxied_by': fields.String,

      'product_version': fields.String,

      'waived': fields.Boolean,

      'comment': fields.String,

@@ -0,0 +1,26 @@ 

+ """add proxyuser waiving support

+ 

+ Revision ID: 0a74cdab732a

+ Revises: 0a27a8ad723a

+ Create Date: 2017-11-22 13:31:50.213681

+ 

+ """

+ 

+ # revision identifiers, used by Alembic.

+ revision = '0a74cdab732a'

+ down_revision = '0a27a8ad723a'

+ 

+ from alembic import op

+ import sqlalchemy as sa

+ 

+ 

+ def upgrade():

+     # ### commands auto generated by Alembic - please adjust! ###

+     op.add_column('waiver', sa.Column('proxied_by', sa.String(length=255), nullable=True))

+     # ### end Alembic commands ###

+ 

+ 

+ def downgrade():

+     # ### commands auto generated by Alembic - please adjust! ###

+     op.drop_column('waiver', 'proxied_by')

+     # ### end Alembic commands ###

file modified
+4 -1
@@ -8,17 +8,20 @@ 

      id = db.Column(db.Integer, primary_key=True)

      result_id = db.Column(db.Integer, nullable=False)

      username = db.Column(db.String(255), nullable=False)

+     proxied_by = db.Column(db.String(255))

      product_version = db.Column(db.String(200), nullable=False)

      waived = db.Column(db.Boolean, nullable=False, default=False)

      comment = db.Column(db.Text)

      timestamp = db.Column(db.DateTime, default=datetime.datetime.utcnow)

  

-     def __init__(self, result_id, username, product_version, waived=False, comment=None):

+     def __init__(self, result_id, username, product_version, waived=False, comment=None,

+                  proxied_by=None):

          self.result_id = result_id

          self.username = username

          self.product_version = product_version

          self.waived = waived

          self.comment = comment

+         self.proxied_by = proxied_by

  

      def __repr__(self):

          return '%s(result_id=%r, username=%r, product_version=%r, waived=%r)' % \