2 # Copyright 2017 RIFT.IO Inc
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
18 Project Manager tasklet is responsible for managing the Projects
19 configurations required for Role Based Access Control feature.
27 gi
.require_version('RwDts', '1.0')
28 gi
.require_version('RwRbacInternalYang', '1.0')
29 from gi
.repository
import (
37 #TODO: Fix once merged to latest platform
38 from rift
.tasklets
.rwproject
.project
import (
45 from rift
.mano
.utils
.project
import (
47 get_add_delete_update_cfgs
,
51 MANO_PROJECT_ROLES
= [
52 'rw-project-mano:catalog-oper',
53 'rw-project-mano:catalog-admin',
57 class ProjectConfigSubscriber(object):
58 """Config subscriber for rw-user config"""
60 def __init__(self
, project
):
61 self
.project_name
= project
.name
62 self
._log
= project
.log
63 self
._dts
= project
.dts
66 self
.pub
= RoleConfigPublisher(project
)
69 return "C,/{}[name='{}']/project-config/user".format(NS_PROJECT
, self
.project_name
)
72 def role_inst(self
, role
, keys
=None):
74 keys
= self
.project_name
81 def delete_user(self
, cfg
):
83 self
._log
.error("Delete user {} for project {}".
84 format(user
.key
, self
.project_name
))
85 if user
.key
in self
.users
:
86 roles
= self
.users
[user
.key
]
87 for role_key
in list(roles
):
88 self
.delete_role(user
, role_key
)
89 self
.users
.pop(user
.key
)
91 def update_user(self
, cfg
):
93 self
._log
.debug("Update user {} for project {}".
94 format(user
.key
, self
.project_name
))
96 for cfg_role
in cfg
.mano_role
:
97 r
= self
.role_inst(cfg_role
)
100 if not user
.key
in self
.users
:
101 self
.users
[user
.key
] = set()
103 #Check if any roles are deleted for the user
104 for role_key
in list(self
.users
[user
.key
]):
105 if role_key
not in cfg_roles
:
106 self
.delete_role(user
, role_key
)
108 for role_key
in cfg_roles
.keys():
109 if role_key
not in self
.users
[user
.key
]:
110 self
.update_role(user
, cfg_roles
[role_key
])
112 def delete_role(self
, user
, role_key
):
113 self
._log
.error("Delete role {} for user {}".
114 format(role_key
, user
.key
))
118 roles
= self
.users
[user_key
]
121 self
.users
[user
.key
] = roles
123 if role_key
in roles
:
124 roles
.remove(role_key
)
125 self
.pub
.delete_role(role_key
, user_key
)
127 def update_role(self
, user
, role
):
128 self
._log
.debug("Update role {} for user {}".
129 format(role
.role
, user
.key
))
133 roles
= self
.users
[user
.key
]
136 self
.users
[user_key
] = roles
140 if not role_key
in roles
:
142 self
.pub
.add_update_role(role_key
, user_key
)
147 def apply_config(dts
, acg
, xact
, action
, scratch
):
148 self
._log
.debug("Got user apply config (xact: %s) (action: %s)",
151 if xact
.xact
is None:
152 if action
== rwdts
.AppconfAction
.INSTALL
:
153 curr_cfg
= self
._reg
.elements
155 self
._log
.debug("Project being re-added after restart.")
158 # When RIFT first comes up, an INSTALL is called with the current config
159 # Since confd doesn't actally persist data this never has any data so
161 self
._log
.debug("No xact handle. Skipping apply config")
165 # TODO: There is user-name and user-domain as keys. Need to fix
167 add_cfgs
, delete_cfgs
, update_cfgs
= get_add_delete_update_cfgs(
168 dts_member_reg
=self
._reg
,
170 key_name
="user_name",
173 self
._log
.debug("Added: {}, Deleted: {}, Modified: {}".
174 format(add_cfgs
, delete_cfgs
, update_cfgs
))
176 for cfg
in delete_cfgs
:
177 self
.delete_user(cfg
)
181 self
.update_user(cfg
)
184 for cfg
in update_cfgs
:
185 self
.update_user(cfg
)
187 return RwTypes
.RwStatus
.SUCCESS
190 def on_prepare(dts
, acg
, xact
, xact_info
, ks_path
, msg
, scratch
):
191 """ Prepare callback from DTS for Project """
193 action
= xact_info
.query_action
195 self
._log
.debug("Project %s on_prepare config received (action: %s): %s",
196 self
.project_name
, xact_info
.query_action
, msg
)
198 user
= User().pb(msg
)
199 if action
in [rwdts
.QueryAction
.CREATE
, rwdts
.QueryAction
.UPDATE
]:
200 if user
.key
in self
.users
:
201 self
._log
.debug("User {} update request".
205 self
._log
.debug("User {}: on_prepare add request".
208 elif action
== rwdts
.QueryAction
.DELETE
:
209 # Check if the user got deleted
210 fref
= ProtobufC
.FieldReference
.alloc()
211 fref
.goto_whole_message(msg
.to_pbcm())
212 if fref
.is_field_deleted():
213 if user
.key
in self
.users
:
214 self
._log
.debug("User {} being deleted".format(user
.key
))
216 self
._log
.warning("Delete on unknown user: {}".
220 self
._log
.error("Action (%s) NOT SUPPORTED", action
)
221 xact_info
.respond_xpath(rwdts
.XactRspCode
.NACK
)
224 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
226 xpath
= self
.get_xpath()
227 self
._log
.debug("Registering for project config using xpath: %s",
231 acg_handler
= rift
.tasklets
.AppConfGroup
.Handler(
232 on_apply
=apply_config
,
235 with self
._dts
.appconf_group_create(acg_handler
) as acg
:
236 self
._reg
= acg
.register(
238 flags
=rwdts
.Flag
.SUBSCRIBER | rwdts
.Flag
.DELTA_READY | rwdts
.Flag
.CACHE
,
239 on_prepare
=on_prepare
,
242 yield from self
.pub
.register()
243 self
.pub
.create_project_roles()
245 def deregister(self
):
246 self
._log
.debug("De-registering DTS handler for project {}".
247 format(self
.project_name
))
250 self
._reg
.deregister()
253 self
.pub
.deregister()
256 class RoleConfigPublisher(rift
.tasklets
.DtsConfigPublisher
):
258 def __init__(self
, project
):
259 super().__init
__(project
._tasklet
)
260 self
.project_name
= project
.name
261 self
.rbac_int
= RwRbacInternalYang
.YangData_RwRbacInternal_RwRbacInternal()
263 self
.proj_roles
= MANO_PROJECT_ROLES
264 self
.proj_roles_published
= False
267 return "D,/rw-rbac-internal:rw-rbac-internal/rw-rbac-internal:role"
269 def role_xpath(self
, role_key
):
270 return "D,/rw-rbac-internal:rw-rbac-internal/rw-rbac-internal:role" + \
271 "[rw-rbac-internal:role='{}']".format(role_key
[0]) + \
272 "[rw-rbac-internal:keys='{}']".format(role_key
[1])
274 def role_user_xpath(self
, role_key
, user_key
):
275 return self
.role_xpath(role_key
) + \
276 "/rw-rbac-internal:user" + \
277 "[rw-rbac-internal:user-name='{}']".format(user_key
[1]) + \
278 "[rw-rbac-internal:user-domain='{}']".format(user_key
[0])
280 def create_project_roles(self
):
281 for name
in self
.proj_roles
:
284 role
.keys
= self
.project_name
285 self
.create_project_role(role
)
287 def create_project_role(self
, role
):
288 self
.log
.error("Create project role for {}: {}".
289 format(self
.project_name
, role
.role
))
290 xpath
= self
.role_xpath(role
.key
)
291 pb_role
= self
.pb_role(role
)
292 self
._regh
.update_element(xpath
, pb_role
)
294 def delete_project_roles(self
):
295 for name
in self
.proj_roles
:
298 role
.keys
= self
.project_name
299 self
.delete_project_role(role
)
301 def delete_project_role(self
, role
):
302 self
.log
.error("Delete project role for {}: {}".
303 format(self
.project_name
, role
.role
))
304 xpath
= self
.role_xpath(role
.key
)
305 self
._regh
.delete_element(xpath
)
307 def create_role(self
, role_key
, user_key
):
308 return RoleKeysUsers(role_key
, user_key
)
310 def pb_role(self
, role
):
312 pbRole
= self
.rbac_int
.create_role()
313 pbRole
.role
= role
.role
314 pbRole
.keys
= role
.keys
315 pbRole
.state_machine
.state
= role
.state
.name
319 def pb_role_user(self
, role
, user
):
321 pbRole
= self
.rbac_int
.create_role()
322 pbRole
.role
= role
.role
323 pbRole
.keys
= role
.keys
325 pbUser
= pbRole
.create_user()
326 pbUser
.user_name
= user
.user_name
327 pbUser
.user_domain
= user
.user_domain
328 pbUser
.state_machine
.state
= user
.state
.name
330 pbRole
.user
.append(pbUser
)
334 def add_update_role(self
, role_key
, user_key
):
337 role
= self
.roles
[role_key
]
339 role
= RoleKeysUsers(role_key
)
340 self
.roles
[role_key
] = role
344 user
= role
.user(user_key
)
346 user
= UserState(user_key
)
351 user
.state
= StateMachine
.new
353 user
.state
= StateMachine
.new
355 xpath
= self
.role_xpath(role_key
)
356 self
.log
.debug("update role: {} user: {} ".format(role_key
, user_key
))
359 pb_role_user
= self
.pb_role_user(role
, user
)
361 self
._regh
.update_element(xpath
, pb_role_user
)
363 def delete_role(self
, role_key
, user_key
):
365 role
= self
.roles
[role_key
]
366 user
= role
.user(user_key
)
370 user
.state
= StateMachine
.delete
371 xpath
= self
.role_xpath(role_key
)
372 self
.log
.error("deleting role: {} user: {} ".format(role_key
, user_key
))
374 pb_role
= self
.pb_role_user(role
, user
)
375 self
._regh
.update_element(xpath
, pb_role
)
377 def do_prepare(self
, xact_info
, action
, ks_path
, msg
):
378 """Handle on_prepare.
380 self
.log
.debug("do_prepare: action: {}, path: {} ks_path, msg: {}".format(action
, ks_path
, msg
))
382 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
384 # TODO: See if we need this as this would be called in the platform also
385 # role_key = tuple([msg.role, msg.keys])
387 # state = msg.state_machine.state
388 # if state == 'init_done':
389 # msg.state_machine.state = 'active'
390 # xpath = self.role_xpath(role_key)
391 # self._regh.update_element(xpath, msg)
393 # for user in msg.users:
394 # user_key = tuple([user.user_domain, user.user_name])
395 # state = user.state_machine.state
396 # if state == 'init_done':
397 # user.state_machine.state = 'active'
398 # xpath = self.role_xpath(role_key)
399 # self._regh.update_element(xpath, msg)
401 def deregister(self
):
403 self
.delete_project_roles()
404 self
._regh
.deregister()