This file is indexed.

/usr/lib/python2.7/dist-packages/redmine/managers.py is in python-redmine 1.4.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
import datetime

from distutils.version import LooseVersion

from .resultsets import ResourceSet
from .utilities import MemorizeFormatter
from .exceptions import (
    ResourceError,
    ResourceBadMethodError,
    ResourceFilterError,
    ResourceNoFiltersProvidedError,
    ResourceNoFieldsProvidedError,
    ResourceVersionMismatchError,
    ResourceNotFoundError,
    ValidationError,
    ResourceRequirementsError
)


class ResourceManager(object):
    """Manages the behaviour of Redmine resources"""
    url = ''
    params = {}
    container = None

    def __init__(self, redmine, resource_name):
        """Accepts redmine instance object and tries to import the needed resource by resource name"""
        resource_class = None
        resource_name = ''.join(word[0].upper() + word[1:] for word in resource_name.split('_'))
        resource_paths = tuple((redmine.custom_resource_paths or ())) + ('redmine.resources',)

        for path in resource_paths:
            try:
                resource_class = getattr(__import__(path, fromlist=[resource_name]), resource_name)
                break
            except (ImportError, AttributeError):
                continue

        if resource_class is None:
            raise ResourceError

        if redmine.ver is not None and LooseVersion(str(redmine.ver)) < LooseVersion(resource_class.redmine_version):
            raise ResourceVersionMismatchError

        self.redmine = redmine
        self.resource_class = resource_class

    def retrieve(self, **params):
        """A proxy for Redmine object request which does some extra work for resource retrieval"""
        self.params.update(**params)

        results = []
        total_count = 0
        limit = self.params.get('limit', 0)
        offset = self.params.get('offset', 0)

        if limit == 0:
            limit = 100

        while True:
            try:
                response = self.redmine.request('get', self.url, params=dict(self.params, limit=limit, offset=offset))
            except ResourceNotFoundError:
                # This is the only place we're checking for ResourceRequirementsError
                # because for some POST/PUT/DELETE requests Redmine may also return 404
                # status code instead of 405 which can lead us to improper decisions
                if self.resource_class.requirements:
                    raise ResourceRequirementsError(self.resource_class.requirements)

                raise ResourceNotFoundError

            # A single resource was requested via get()
            if isinstance(response[self.container], dict):
                results = response[self.container]
                total_count = 1
                break

            # Resource supports limit/offset on Redmine level
            if all(response.get(param) is not None for param in ('total_count', 'limit', 'offset')):
                total_count = response['total_count']
                results.extend(response[self.container])

                # We want to get all resources
                if self.params.get('limit', 0) == 0:
                    offset += limit

                    if total_count <= offset:
                        break
                # We want to get only some resources
                else:
                    limit -= 100
                    offset += 100

                    if limit <= 0:
                        break
            # We have to mimic limit/offset if a resource
            # doesn't support this feature on Redmine level
            else:
                total_count = len(response[self.container])
                results = response[self.container][offset:None if self.params.get('limit', 0) == 0 else limit + offset]
                break

        return results, total_count

    def to_resource(self, resource):
        """Converts a single resource dict from Redmine result set to resource object"""
        return self.resource_class(self, resource)

    def to_resource_set(self, resources):
        """Converts an iterable with resource dicts from Redmine result set to ResourceSet object"""
        return ResourceSet(self, resources)

    def new(self):
        """Returns new empty resource"""
        return self.to_resource({})

    def get(self, resource_id, **params):
        """Returns a Resource object directly by resource id (can be either integer id or string identifier)"""
        if self.resource_class.query_one is None or self.resource_class.container_one is None:
            raise ResourceBadMethodError

        try:
            self.url = '{0}{1}'.format(self.redmine.url, self.resource_class.query_one.format(resource_id, **params))
        except KeyError as exception:
            raise ValidationError('{0} argument is required'.format(exception))

        self.params = self.prepare_params(params)
        self.container = self.resource_class.container_one
        return self.resource_class(self, self.retrieve()[0])

    def all(self, **params):
        """Returns a ResourceSet object with all Resource objects"""
        if self.resource_class.query_all is None or self.resource_class.container_all is None:
            raise ResourceBadMethodError

        self.url = '{0}{1}'.format(self.redmine.url, self.resource_class.query_all)
        self.params = self.prepare_params(params)
        self.container = self.resource_class.container_all
        return ResourceSet(self)

    def filter(self, **filters):
        """Returns a ResourceSet object with Resource objects filtered by a dict of filters"""
        if self.resource_class.query_filter is None or self.resource_class.container_filter is None:
            raise ResourceBadMethodError

        if not filters:
            raise ResourceNoFiltersProvidedError

        try:
            self.url = '{0}{1}'.format(self.redmine.url, self.resource_class.query_filter.format(**filters))
            self.container = self.resource_class.container_filter.format(**filters)
        except KeyError:
            raise ResourceFilterError

        self.params = self.prepare_params(filters)
        return ResourceSet(self)

    def create(self, **fields):
        """Creates a new resource in Redmine database and returns resource object on success"""
        if self.resource_class.query_create is None or self.resource_class.container_create is None:
            raise ResourceBadMethodError

        if not fields:
            raise ResourceNoFieldsProvidedError

        for index, upload in enumerate(fields.get('uploads', [])):
            fields['uploads'][index]['token'] = self.redmine.upload(upload.get('path', ''))

        formatter = MemorizeFormatter()

        try:
            url = '{0}{1}'.format(self.redmine.url, formatter.format(self.resource_class.query_create, **fields))
        except KeyError as exception:
            raise ValidationError('{0} field is required'.format(exception))

        self.container = self.resource_class.container_one
        data = {self.resource_class.container_create: self.prepare_params(formatter.unused_kwargs)}

        # Almost all resources are created via POST method, but some
        # resources are created via PUT, so we should check for this
        try:
            response = self.redmine.request('post', url, data=data)
        except ResourceNotFoundError:
            response = self.redmine.request('put', url, data=data)

        try:
            resource = self.to_resource(response[self.container])
        except TypeError:
            raise ValidationError('Resource already exists')  # fix for repeated PUT requests

        self.params = formatter.used_kwargs
        self.url = '{0}{1}'.format(
            self.redmine.url,
            self.resource_class.query_one.format(resource.internal_id, **fields)
        )
        return resource

    def update(self, resource_id, **fields):
        """Updates a Resource object by resource id (can be either integer id or string identifier)"""
        if self.resource_class.query_update is None or self.resource_class.container_update is None:
            raise ResourceBadMethodError

        if not fields:
            raise ResourceNoFieldsProvidedError

        for index, upload in enumerate(fields.get('uploads', [])):
            fields['uploads'][index]['token'] = self.redmine.upload(upload.get('path', ''))

        formatter = MemorizeFormatter()

        try:
            query_update = formatter.format(self.resource_class.query_update, resource_id, **fields)
        except KeyError as exception:
            param = str(exception).replace("'", "")

            if param in self.params:
                fields[param] = self.params[param]
                query_update = formatter.format(self.resource_class.query_update, resource_id, **fields)
            else:
                raise ValidationError('{0} argument is required'.format(exception))

        url = '{0}{1}'.format(self.redmine.url, query_update)
        data = {self.resource_class.container_update: self.prepare_params(formatter.unused_kwargs)}
        return self.redmine.request('put', url, data=data)

    def delete(self, resource_id, **params):
        """Deletes a Resource object by resource id (can be either integer id or string identifier)"""
        if self.resource_class.query_delete is None:
            raise ResourceBadMethodError

        try:
            url = '{0}{1}'.format(self.redmine.url, self.resource_class.query_delete.format(resource_id, **params))
        except KeyError as exception:
            raise ValidationError('{0} argument is required'.format(exception))

        return self.redmine.request('delete', url, params=self.prepare_params(params))

    def prepare_params(self, params):
        """Prepares params so Redmine could understand them correctly"""
        for name, value in params.items():
            type_ = type(value)

            if type_ is datetime.date:
                params[name] = value.strftime(self.redmine.date_format)
            elif type_ is datetime.datetime:
                params[name] = value.strftime(self.redmine.datetime_format)

        return self.resource_class.translate_params(params)