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:mano-oper',
53 'rw-project-mano:mano-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 if user
.key
in self
.users
:
84 roles
= self
.users
[user
.key
]
85 for role_key
in list(roles
):
86 self
.delete_role(user
, role_key
)
87 self
.users
.pop(user
.key
)
89 def update_user(self
, cfg
):
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
):
112 roles
= self
.users
[user_key
]
115 self
.users
[user
.key
] = roles
117 if role_key
in roles
:
118 roles
.remove(role_key
)
119 self
.pub
.delete_role(role_key
, user_key
)
121 def update_role(self
, user
, role
):
125 roles
= self
.users
[user
.key
]
128 self
.users
[user_key
] = roles
132 if not role_key
in roles
:
134 self
.pub
.add_update_role(role_key
, user_key
)
139 def apply_config(dts
, acg
, xact
, action
, scratch
):
140 self
._log
.debug("Got user apply config (xact: %s) (action: %s)",
143 if xact
.xact
is None:
144 if action
== rwdts
.AppconfAction
.INSTALL
:
145 curr_cfg
= self
._reg
.elements
147 self
._log
.debug("Project being re-added after restart.")
150 # When RIFT first comes up, an INSTALL is called with the current config
151 # Since confd doesn't actally persist data this never has any data so
153 self
._log
.debug("No xact handle. Skipping apply config")
157 # TODO: There is user-name and user-domain as keys. Need to fix
159 add_cfgs
, delete_cfgs
, update_cfgs
= get_add_delete_update_cfgs(
160 dts_member_reg
=self
._reg
,
162 key_name
="user_name",
165 self
._log
.debug("Added: {}, Deleted: {}, Modified: {}".
166 format(add_cfgs
, delete_cfgs
, update_cfgs
))
168 for cfg
in delete_cfgs
:
169 self
.delete_user(cfg
)
173 self
.update_user(cfg
)
176 for cfg
in update_cfgs
:
177 self
.update_user(cfg
)
179 return RwTypes
.RwStatus
.SUCCESS
182 def on_prepare(dts
, acg
, xact
, xact_info
, ks_path
, msg
, scratch
):
183 """ Prepare callback from DTS for Project """
185 action
= xact_info
.query_action
187 self
._log
.debug("Project %s on_prepare config received (action: %s): %s",
188 self
.project_name
, xact_info
.query_action
, msg
)
190 user
= User().pb(msg
)
191 if action
in [rwdts
.QueryAction
.CREATE
, rwdts
.QueryAction
.UPDATE
]:
192 if user
.key
in self
.users
:
193 self
._log
.debug("User {} update request".
197 self
._log
.debug("User {}: on_prepare add request".
200 elif action
== rwdts
.QueryAction
.DELETE
:
201 # Check if the user got deleted
202 fref
= ProtobufC
.FieldReference
.alloc()
203 fref
.goto_whole_message(msg
.to_pbcm())
204 if fref
.is_field_deleted():
205 if user
.key
in self
.users
:
206 self
._log
.debug("User {} being deleted".format(user
.key
))
208 self
._log
.warning("Delete on unknown user: {}".
212 self
._log
.error("Action (%s) NOT SUPPORTED", action
)
213 xact_info
.respond_xpath(rwdts
.XactRspCode
.NACK
)
216 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
218 xpath
= self
.get_xpath()
219 self
._log
.debug("Registering for project config using xpath: %s",
223 acg_handler
= rift
.tasklets
.AppConfGroup
.Handler(
224 on_apply
=apply_config
,
227 with self
._dts
.appconf_group_create(acg_handler
) as acg
:
228 self
._reg
= acg
.register(
230 flags
=rwdts
.Flag
.SUBSCRIBER | rwdts
.Flag
.DELTA_READY | rwdts
.Flag
.CACHE
,
231 on_prepare
=on_prepare
,
234 yield from self
.pub
.register()
235 self
.pub
.create_project_roles()
237 def deregister(self
):
238 self
._log
.debug("De-registering DTS handler for project {}".
239 format(self
.project_name
))
241 self
._reg
.deregister()
244 self
.pub
.deregister()
247 class RoleConfigPublisher(rift
.tasklets
.DtsConfigPublisher
):
249 def __init__(self
, project
):
250 super().__init
__(project
._tasklet
)
251 self
.project_name
= project
.name
252 self
.rbac_int
= RwRbacInternalYang
.YangData_RwRbacInternal_RwRbacInternal()
254 self
.proj_roles
= MANO_PROJECT_ROLES
255 self
.proj_roles_published
= False
258 return "D,/rw-rbac-internal:rw-rbac-internal/rw-rbac-internal:role"
260 def role_xpath(self
, role_key
):
261 return "D,/rw-rbac-internal:rw-rbac-internal/rw-rbac-internal:role" + \
262 "[rw-rbac-internal:role='{}']".format(role_key
[0]) + \
263 "[rw-rbac-internal:keys='{}']".format(role_key
[1])
265 def role_user_xpath(self
, role_key
, user_key
):
266 return self
.role_xpath(role_key
) + \
267 "/rw-rbac-internal:user" + \
268 "[rw-rbac-internal:user-name='{}']".format(user_key
[1]) + \
269 "[rw-rbac-internal:user-domain='{}']".format(user_key
[0])
271 def create_project_roles(self
):
272 for name
in self
.proj_roles
:
275 role
.keys
= self
.project_name
276 self
.create_project_role(role
)
278 def create_project_role(self
, role
):
279 xpath
= self
.role_xpath(role
.key
)
280 pb_role
= self
.pb_role(role
)
281 self
._regh
.update_element(xpath
, pb_role
)
283 def create_role(self
, role_key
, user_key
):
284 return RoleKeysUsers(role_key
, user_key
)
286 def pb_role(self
, role
):
288 pbRole
= self
.rbac_int
.create_role()
289 pbRole
.role
= role
.role
290 pbRole
.keys
= role
.keys
294 def pb_role_user(self
, role
, user
):
296 pbRole
= self
.rbac_int
.create_role()
297 pbRole
.role
= role
.role
298 pbRole
.keys
= role
.keys
300 pbUser
= pbRole
.create_user()
301 pbUser
.user_name
= user
.user_name
302 pbUser
.user_domain
= user
.user_domain
303 pbUser
.state_machine
.state
= user
.state
.name
305 pbRole
.user
.append(pbUser
)
309 def add_update_role(self
, role_key
, user_key
):
312 role
= self
.roles
[role_key
]
314 role
= RoleKeysUsers(role_key
)
315 self
.roles
[role_key
] = role
319 user
= role
.user(user_key
)
321 user
= UserState(user_key
)
325 user
.state
= StateMachine
.new
327 xpath
= self
.role_xpath(role_key
)
329 pb_role_user
= self
.pb_role_user(role
, user
)
330 self
.log
.debug("add_update_role: xpath:{} pb_role:{}".format(xpath
, pb_role_user
))
332 self
._regh
.update_element(xpath
, pb_role_user
)
334 def delete_role(self
, role_key
, user_key
):
336 role
= self
.roles
[role_key
]
337 user
= role
.user(user_key
)
341 user
.state
= StateMachine
.delete
342 xpath
= self
.role_xpath(role_key
)
343 self
.log
.debug("deleting role: {} user: {} ".format(role_key
, user_key
))
345 pb_role
= self
.pb_role_user(role
, user
)
346 self
._regh
.update_element(xpath
, pb_role
)
348 def do_prepare(self
, xact_info
, action
, ks_path
, msg
):
349 """Handle on_prepare. To be overridden by Concreate Publisher Handler
351 self
.log
.debug("do_prepare: action: {}, path: {} ks_path, msg: {}".format(action
, ks_path
, msg
))
353 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
355 # TODO: See if we need this as this would be called in the platform also
356 # role_key = tuple([msg.role, msg.keys])
358 # state = msg.state_machine.state
359 # if state == 'init_done':
360 # msg.state_machine.state = 'active'
361 # xpath = self.role_xpath(role_key)
362 # self._regh.update_element(xpath, msg)
364 # for user in msg.users:
365 # user_key = tuple([user.user_domain, user.user_name])
366 # state = user.state_machine.state
367 # if state == 'init_done':
368 # user.state_machine.state = 'active'
369 # xpath = self.role_xpath(role_key)
370 # self._regh.update_element(xpath, msg)
372 def deregister(self
):
374 self
._regh
.deregister()