ma.py 11.3 KB
Newer Older
1
2
3
import os
import tempfile
import subprocess
Radomir Klacza's avatar
Radomir Klacza committed
4

5
from myslicelib.api.cbas import Cbas, objects, fields, cbas_id
6
from myslicelib.util.sfa import hrn_to_urn, urn_to_hrn
Radomir Klacza's avatar
Radomir Klacza committed
7
8

import logging
9

Radomir Klacza's avatar
Radomir Klacza committed
10
11
logger = logging.getLogger(__name__)

12
13

class MemberAuthority(Cbas):
Radomir Klacza's avatar
Radomir Klacza committed
14
15
16

    def __init__(self, endpoint=None, authentication=None):

17
        super(MemberAuthority, self).__init__(endpoint, authentication)
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
        self.user_credential = self.get_credential(self.authentication.urn, raw=True)

    def get_credential(self, urn, raw=False):
        _ma = [
            'user',
        ]
        _sa = [
            'slice',
            'project'
        ]
        hrn, entity = urn_to_hrn(urn)
        if entity in _sa:
            return self.sa.get_credential(urn, raw, [self.user_credential])
        elif entity in _ma:
            return super(MemberAuthority, self).get_credential(urn, raw)
        else:
34
            return {'data': [], 'errors': []}
Radomir Klacza's avatar
Radomir Klacza committed
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
    def set_sa(self, sa):
        self.sa = sa

    def get(self, entity, urn=None, filter=None, raw=False):
        try:
            if entity in objects.keys():
                cbas_entity = objects[entity]
            else:
                cbas_entity = entity

            result = super(MemberAuthority, self).get(entity, urn, filter, raw)
            if raw:
                return result
            if entity == "user":
                for i, element in enumerate(result["data"]):
51
                    slices = self.sa._proxy.lookup_for_member('SLICE', element['id'], [self.user_credential], {})
52
                    result['data'][i]['slices'] = [sl['SLICE_URN'] for sl in slices['value']]
53
                    projects = self.sa._proxy.lookup_for_member('PROJECT', element['id'], [self.user_credential], {})
54
55
56
57
58
                    result['data'][i]['projects'] = [sl['PROJECT_URN'] for sl in projects['value']]
        except Exception as e:
            import traceback
            traceback.print_exc()
            self.logs.append({
59
60
61
62
63
64
                'endpoint': self.endpoint.name,
                'url': self.endpoint.url,
                'protocol': self.endpoint.protocol,
                'type': self.endpoint.type,
                'exception': str(e)
            })
65
66
        return result

67
68
69
70
71
72
73
74
    def _key(self, data):
        keys = []
        for d in data:
            k = data[d]
            keys.append(
                k['KEY_PUBLIC']
            )
        return keys
Radomir Klacza's avatar
Radomir Klacza committed
75
76

    def _user(self, data):
Loic Baron's avatar
Loic Baron committed
77
        users = []
Radomir Klacza's avatar
Radomir Klacza committed
78
        for d in data:
Loic Baron's avatar
Loic Baron committed
79
            u = data[d]
80
81
            urn = u.get('MEMBER_URN')
            authority = 'urn:publicid:IDN+' + urn.split("+")[1] + '+authority+ma'
82
83
            credentials = u.get('MEMBER_CREDENTIALS')
            pi_authorities = []
84
85
            # How to check if user has root rights?
            # XXX workaround for now, to be changed
86
87
            if "<privilege><name>GLOBAL_MEMBERS_WILDCARDS</name>" in credentials:
                pi_authorities.append(authority)
88
89
90
            # Get the Key of the user
            k = self.get("key", urn)

Loic Baron's avatar
Loic Baron committed
91
            users.append({
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
                'id': urn,
                'shortname': u.get('MEMBER_USERNAME'),
                'hrn': urn_to_hrn(u.get('MEMBER_URN'))[0],
                'first_name': u.get('MEMBER_FIRSTNAME'),
                'last_name': u.get('MEMBER_LASTNAME'),
                'keys': k.get('data', []),
                'certificate': u.get('MEMBER_CERTIFICATE'),
                'email': u.get('MEMBER_EMAIL'),
                'created': u.get('created'),
                # 'updated': self._datetime(d['last_updated']),
                'authority': authority,
                'pi_authorities': pi_authorities,
                'affiliation': u.get('_ONELAB_MEMBER_AFFILIATION', ''),
                'city': u.get('_ONELAB_MEMBER_CITY', ''),
                'country': u.get('_ONELAB_MEMBER_COUNTRY', ''),
                #                   ],
                # 'projects': [
                #                    hrn_to_urn(pi_auth, 'authority') for pi_auth in filter(lambda x: len(x.split('.')) > 2, u.get('reg-pi-authorities', []))
                #                   ],
                # 'slices': [
                #            hrn_to_urn(sli, 'slice') for sli in u.get('reg-slices', [])
                #        ],
Radomir Klacza's avatar
Radomir Klacza committed
114
            })
Loic Baron's avatar
Loic Baron committed
115
        return users
Radomir Klacza's avatar
Radomir Klacza committed
116

117
118
    def _user_mappings(self, record_dict):
        mapped_dict = {
119
            # 'MEMBER_URN': record_dict.get('id'),
120
121
122
123
            'MEMBER_USERNAME': record_dict.get('shortname'),
            'MEMBER_EMAIL': record_dict.get('email'),
            'MEMBER_FIRSTNAME': record_dict.get('first_name', ''),
            'MEMBER_LASTNAME': record_dict.get('last_name', ''),
124
            # 'MEMBER_CERTIFICATE': record_dict.get('certificate',''),
125
126
127
            '_ONELAB_MEMBER_AFFILIATION': record_dict.get('affiliation', ''),
            '_ONELAB_MEMBER_CITY': record_dict.get('city', ''),
            '_ONELAB_MEMBER_COUNTRY': record_dict.get('country', ''),
128
129
130
131
            # 'type': 'member',
            # 'keys' : record_dict.get('keys', '')

            'KEY_PUBLIC': self.convert_pkcs8(next(iter(record_dict.get('keys', []) or []), None))
132
        }
133

134
135
136
137
138
        # filter key have empty value
        mapped_dict = {k: v for k, v in mapped_dict.items() if v}
        return mapped_dict

    def _key_mappings(self, record_dict):
139

140
141
142
143
144
145
146
147
148
149
150
151
        mapped_dict = {
            'KEY_PUBLIC': next(iter(record_dict.get('keys', []) or []), None),
            'KEY_TYPE': "rsa-ssh",
            'KEY_MEMBER': record_dict.get('id')
        }
        # filter key have empty value
        mapped_dict = {k: v for k, v in mapped_dict.items() if v}
        return mapped_dict

    def create(self, entity, urn, record_dict):
        result = []
        try:
152
153
            if entity in objects.keys():
                cbas_entity = objects[entity]
154
155
156
            else:
                cbas_entity = entity

157
158
159
            mapped_dict = getattr(self, '_' + entity + '_mappings')(record_dict)

            create_result = self._proxy.create(cbas_entity, [self.user_credential], {'fields': mapped_dict})
Loic Baron's avatar
Loic Baron committed
160
            self.save_key(urn, record_dict)
161
162
163
164
            if 'code' not in create_result or create_result['code'] != 0:
                if 'output' in create_result:
                    raise Exception(create_result['output'])
                raise Exception(create_result)
165
            result = create_result
166
167
168
            res = self.get(entity, urn)
            result = res['data']
            self.logs += res['errors']
169
170
171
172
        except Exception as e:
            import traceback
            traceback.print_exc()
            self.logs.append({
173
174
175
176
177
178
179
                'endpoint': self.endpoint.name,
                'url': self.endpoint.url,
                'protocol': self.endpoint.protocol,
                'type': self.endpoint.type,
                'exception': str(e)
            })
        return {'data': result, 'errors': self.logs}
180

Loic Baron's avatar
Loic Baron committed
181
182
183
184
    def save_key(self, urn, record_dict):
        """
        save_key function creates or update a public key for a user
        """
Loic Baron's avatar
Loic Baron committed
185
        try:
186
            self.delete_keys(record_dict.get('id'))
Loic Baron's avatar
Loic Baron committed
187
            if record_dict.get("keys") is not None:
188
189
                mapped_dict = getattr(self, '_key' + '_mappings')(record_dict)
                create_key_result = self._proxy.create("KEY", [self.user_credential], {'fields': mapped_dict})
Loic Baron's avatar
Loic Baron committed
190
191
192
                # DUPLICATE ERROR => update KEY
                if 'code' in create_key_result and create_key_result['code'] == 5:
                    k = self.get("key", urn, raw=True)
193
                    key_id = next(iter(k.keys()))
Loic Baron's avatar
Loic Baron committed
194
195
196
                    # UPDATE method do not support these fields
                    del mapped_dict["KEY_TYPE"]
                    del mapped_dict["KEY_MEMBER"]
197
198
                    update_key_result = self._proxy.update("KEY", key_id, [self.user_credential],
                                                           {'fields': mapped_dict})
Loic Baron's avatar
Loic Baron committed
199
200
201
202
203
                return True

        except Exception as e:
            logger.exception(e)
            pass
Loic Baron's avatar
Loic Baron committed
204
205
206

        return False

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
    def update_sa(self, urn, _type, current, record_dict):
        """
        Update the slices and projects of a user
        this function calls the SliceAuthority
        sa needs to be set by self.set_sa called in api/__init__.py

        :param urn: User.id
        :param _type: slice | project
        :param current: current record of the user before update
        :param record_dict: dict of the record with new values
        """
        rem = list(set(current[_type + 's']) - set(record_dict[_type + 's']))
        add = list(set(record_dict[_type + 's']) - set(current[_type + 's']))
        mod = rem + add
        for item in add:
            obj = self.sa.get(_type, item)['data'][0]
            obj['users'].append(urn)
            self.sa.update(_type, item, obj)
        for item in rem:
            obj = self.sa.get(_type, item)['data'][0]
            obj['users'].remove(urn)
            self.sa.update(_type, item, obj)

Loic Baron's avatar
Loic Baron committed
230
    def update(self, entity, urn, record_dict):
231
232
233
        """
        Update a user
        """
Loic Baron's avatar
Loic Baron committed
234
235
        result = []
        try:
236
237
            if entity in objects.keys():
                cbas_entity = objects[entity]
Loic Baron's avatar
Loic Baron committed
238
239
240
            else:
                cbas_entity = entity

241
            mapped_dict = getattr(self, '_' + entity + '_mappings')(record_dict)
Loic Baron's avatar
Loic Baron committed
242
243
            # UPDATE method do not support these fields
            del mapped_dict["MEMBER_USERNAME"]
244

245
246
247
            current = self.get(entity, urn)['data'][0]
            self.update_sa(urn, 'slice', current, record_dict)
            self.update_sa(urn, 'project', current, record_dict)
248
249

            update_result = self._proxy.update(cbas_entity, urn, [self.user_credential], {'fields': mapped_dict})
Loic Baron's avatar
Loic Baron committed
250
251
252
253
254
255
256
257
            self.save_key(urn, record_dict)
            res = self.get(entity, urn)
            result = res['data']
            self.logs += res['errors']
        except Exception as e:
            import traceback
            traceback.print_exc()
            self.logs.append({
258
259
260
261
262
263
264
265
                'endpoint': self.endpoint.name,
                'url': self.endpoint.url,
                'protocol': self.endpoint.protocol,
                'type': self.endpoint.type,
                'exception': str(e)
            })

        return {'data': result, 'errors': self.logs}
Loic Baron's avatar
Loic Baron committed
266

267
    def delete_keys(self, urn):
Loic Baron's avatar
Loic Baron committed
268
        """
269
        delete_keys function removes all public keys of a user
Loic Baron's avatar
Loic Baron committed
270
271
        """
        try:
272
273
            keys = self.get("key", urn, raw=True)
            for k in keys:
274
                delete_key_result = self._proxy.delete("KEY", k, [self.user_credential], {})
Loic Baron's avatar
Loic Baron committed
275
276
277
278
279
280
281
282
            return True

        except Exception as e:
            logger.exception(e)
            pass

        return False

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
    def convert_pkcs8(self, key):

        try:
            # Write the key in a temporary file
            tempf, fpath = tempfile.mkstemp()
            os.write(tempf, bytes(key, 'utf-8'))
            os.close(tempf)

            # Convert to PKCS8 (---BEGIN PUBLIC KEY---)
            command = "ssh-keygen -e -m pkcs8 -f".split(' ')
            command.append(fpath)

            data = subprocess.run(command, stdout=subprocess.PIPE)
            # Remove temporary file
            os.remove(fpath)

        except Exception as e:
            logger.debug(e)
            return
        else:
            return data.stdout.decode("utf-8")

Loic Baron's avatar
Loic Baron committed
305

306
class MaError(Exception):
Radomir Klacza's avatar
Radomir Klacza committed
307
308
309
310
311
312
313
314

    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)