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 from rift
.tasklets
.rwproject
.project
import (
44 from rift
.mano
.utils
.project
import (
46 get_add_delete_update_cfgs
,
50 from .projectmano
import MANO_PROJECT_ROLES
53 class ProjectConfigSubscriber(object):
54 """Config subscriber for rw-user config"""
56 def __init__(self
, project
):
57 self
.project_name
= project
.name
58 self
._log
= project
.log
59 self
._dts
= project
.dts
62 self
.pub
= RoleConfigPublisher(project
)
65 return "C,/{}[name='{}']/project-config/user".format(NS_PROJECT
, self
.project_name
)
68 def role_inst(self
, role
, keys
=None):
70 keys
= self
.project_name
77 def delete_user(self
, cfg
):
79 self
._log
.error("Delete user {} for project {}".
80 format(user
.key
, self
.project_name
))
81 if user
.key
in self
.users
:
82 roles
= self
.users
[user
.key
]
83 for role_key
in list(roles
):
84 self
.delete_role(user
, role_key
)
85 self
.users
.pop(user
.key
)
87 def update_user(self
, cfg
):
89 self
._log
.debug("Update user {} for project {}".
90 format(user
.key
, self
.project_name
))
92 for cfg_role
in cfg
.mano_role
:
93 r
= self
.role_inst(cfg_role
)
96 if not user
.key
in self
.users
:
97 self
.users
[user
.key
] = set()
99 #Check if any roles are deleted for the user
100 for role_key
in list(self
.users
[user
.key
]):
101 if role_key
not in cfg_roles
:
102 self
.delete_role(user
, role_key
)
104 for role_key
in cfg_roles
.keys():
105 if role_key
not in self
.users
[user
.key
]:
106 self
.update_role(user
, cfg_roles
[role_key
])
108 def delete_role(self
, user
, role_key
):
109 self
._log
.error("Delete role {} for user {}".
110 format(role_key
, user
.key
))
114 roles
= self
.users
[user_key
]
117 self
.users
[user
.key
] = roles
119 if role_key
in roles
:
120 roles
.remove(role_key
)
121 self
.pub
.delete_role(role_key
, user_key
)
123 def update_role(self
, user
, role
):
124 self
._log
.debug("Update role {} for user {}".
125 format(role
.role
, user
.key
))
129 roles
= self
.users
[user
.key
]
132 self
.users
[user_key
] = roles
136 if not role_key
in roles
:
138 self
.pub
.add_update_role(role_key
, user_key
)
143 def apply_config(dts
, acg
, xact
, action
, scratch
):
144 self
._log
.debug("Got user apply config (xact: %s) (action: %s)",
147 if xact
.xact
is None:
148 if action
== rwdts
.AppconfAction
.INSTALL
:
149 curr_cfg
= self
._reg
.elements
151 self
._log
.debug("Project being re-added after restart.")
154 # When RIFT first comes up, an INSTALL is called with the current config
155 # Since confd doesn't actally persist data this never has any data so
157 self
._log
.debug("No xact handle. Skipping apply config")
161 # TODO: There is user-name and user-domain as keys. Need to fix
163 add_cfgs
, delete_cfgs
, update_cfgs
= get_add_delete_update_cfgs(
164 dts_member_reg
=self
._reg
,
166 key_name
="user_name",
169 self
._log
.debug("Added: {}, Deleted: {}, Modified: {}".
170 format(add_cfgs
, delete_cfgs
, update_cfgs
))
172 for cfg
in delete_cfgs
:
173 self
.delete_user(cfg
)
177 self
.update_user(cfg
)
180 for cfg
in update_cfgs
:
181 self
.update_user(cfg
)
183 return RwTypes
.RwStatus
.SUCCESS
186 def on_prepare(dts
, acg
, xact
, xact_info
, ks_path
, msg
, scratch
):
187 """ Prepare callback from DTS for Project """
189 action
= xact_info
.query_action
191 self
._log
.debug("Project %s on_prepare config received (action: %s): %s",
192 self
.project_name
, xact_info
.query_action
, msg
)
194 user
= User().pb(msg
)
195 if action
in [rwdts
.QueryAction
.CREATE
, rwdts
.QueryAction
.UPDATE
]:
196 if user
.key
in self
.users
:
197 self
._log
.debug("User {} update request".
201 self
._log
.debug("User {}: on_prepare add request".
204 elif action
== rwdts
.QueryAction
.DELETE
:
205 # Check if the user got deleted
206 fref
= ProtobufC
.FieldReference
.alloc()
207 fref
.goto_whole_message(msg
.to_pbcm())
208 if fref
.is_field_deleted():
209 if user
.key
in self
.users
:
210 self
._log
.debug("User {} being deleted".format(user
.key
))
212 self
._log
.warning("Delete on unknown user: {}".
216 self
._log
.error("Action (%s) NOT SUPPORTED", action
)
217 xact_info
.respond_xpath(rwdts
.XactRspCode
.NACK
)
220 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
222 xpath
= self
.get_xpath()
223 self
._log
.debug("Registering for project config using xpath: %s",
227 acg_handler
= rift
.tasklets
.AppConfGroup
.Handler(
228 on_apply
=apply_config
,
231 with self
._dts
.appconf_group_create(acg_handler
) as acg
:
232 self
._reg
= acg
.register(
234 flags
=rwdts
.Flag
.SUBSCRIBER | rwdts
.Flag
.DELTA_READY | rwdts
.Flag
.CACHE
,
235 on_prepare
=on_prepare
,
238 yield from self
.pub
.register()
239 self
.pub
.create_project_roles()
241 def deregister(self
):
242 self
._log
.debug("De-registering DTS handler for project {}".
243 format(self
.project_name
))
246 self
._reg
.deregister()
249 self
.pub
.deregister()
252 class RoleConfigPublisher(rift
.tasklets
.DtsConfigPublisher
):
254 def __init__(self
, project
):
255 super().__init
__(project
._tasklet
)
256 self
.project_name
= project
.name
257 self
.rbac_int
= RwRbacInternalYang
.YangData_RwRbacInternal_RwRbacInternal()
259 self
.proj_roles
= [role
['mano-role'] for role
in MANO_PROJECT_ROLES
]
260 self
.proj_roles_published
= False
263 return "D,/rw-rbac-internal:rw-rbac-internal/rw-rbac-internal:role"
265 def role_xpath(self
, role_key
):
266 return "D,/rw-rbac-internal:rw-rbac-internal/rw-rbac-internal:role" + \
267 "[rw-rbac-internal:role='{}']".format(role_key
[0]) + \
268 "[rw-rbac-internal:keys='{}']".format(role_key
[1])
270 def role_user_xpath(self
, role_key
, user_key
):
271 return self
.role_xpath(role_key
) + \
272 "/rw-rbac-internal:user" + \
273 "[rw-rbac-internal:user-name='{}']".format(user_key
[1]) + \
274 "[rw-rbac-internal:user-domain='{}']".format(user_key
[0])
276 def create_project_roles(self
):
277 for name
in self
.proj_roles
:
280 role
.keys
= self
.project_name
281 self
.create_project_role(role
)
283 def create_project_role(self
, role
):
284 self
.log
.error("Create project role for {}: {}".
285 format(self
.project_name
, role
.role
))
286 xpath
= self
.role_xpath(role
.key
)
287 pb_role
= self
.pb_role(role
)
288 self
._regh
.update_element(xpath
, pb_role
)
290 def delete_project_roles(self
):
291 for name
in self
.proj_roles
:
294 role
.keys
= self
.project_name
295 self
.delete_project_role(role
)
297 def delete_project_role(self
, role
):
298 self
.log
.error("Delete project role for {}: {}".
299 format(self
.project_name
, role
.role
))
300 xpath
= self
.role_xpath(role
.key
)
301 self
._regh
.delete_element(xpath
)
303 def create_role(self
, role_key
, user_key
):
304 return RoleKeysUsers(role_key
, user_key
)
306 def pb_role(self
, role
):
308 pbRole
= self
.rbac_int
.create_role()
309 pbRole
.role
= role
.role
310 pbRole
.keys
= role
.keys
311 pbRole
.state_machine
.state
= role
.state
.name
315 def pb_role_user(self
, role
, user
):
317 pbRole
= self
.rbac_int
.create_role()
318 pbRole
.role
= role
.role
319 pbRole
.keys
= role
.keys
321 pbUser
= pbRole
.create_user()
322 pbUser
.user_name
= user
.user_name
323 pbUser
.user_domain
= user
.user_domain
324 pbUser
.state_machine
.state
= user
.state
.name
326 pbRole
.user
.append(pbUser
)
330 def add_update_role(self
, role_key
, user_key
):
333 role
= self
.roles
[role_key
]
335 role
= RoleKeysUsers(role_key
)
336 self
.roles
[role_key
] = role
340 user
= role
.user(user_key
)
342 user
= UserState(user_key
)
347 user
.state
= StateMachine
.new
349 user
.state
= StateMachine
.new
351 xpath
= self
.role_xpath(role_key
)
352 self
.log
.debug("update role: {} user: {} ".format(role_key
, user_key
))
355 pb_role_user
= self
.pb_role_user(role
, user
)
357 self
._regh
.update_element(xpath
, pb_role_user
)
359 def delete_role(self
, role_key
, user_key
):
361 role
= self
.roles
[role_key
]
362 user
= role
.user(user_key
)
366 user
.state
= StateMachine
.delete
367 xpath
= self
.role_xpath(role_key
)
368 self
.log
.error("deleting role: {} user: {} ".format(role_key
, user_key
))
370 pb_role
= self
.pb_role_user(role
, user
)
371 self
._regh
.update_element(xpath
, pb_role
)
373 def do_prepare(self
, xact_info
, action
, ks_path
, msg
):
374 """Handle on_prepare.
376 self
.log
.debug("do_prepare: action: {}, path: {} ks_path, msg: {}".format(action
, ks_path
, msg
))
378 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
380 # TODO: See if we need this as this would be called in the platform also
381 # role_key = tuple([msg.role, msg.keys])
383 # state = msg.state_machine.state
384 # if state == 'init_done':
385 # msg.state_machine.state = 'active'
386 # xpath = self.role_xpath(role_key)
387 # self._regh.update_element(xpath, msg)
389 # for user in msg.users:
390 # user_key = tuple([user.user_domain, user.user_name])
391 # state = user.state_machine.state
392 # if state == 'init_done':
393 # user.state_machine.state = 'active'
394 # xpath = self.role_xpath(role_key)
395 # self._regh.update_element(xpath, msg)
397 def deregister(self
):
399 self
.delete_project_roles()
400 self
._regh
.deregister()