1 # -*- coding: utf-8 -*-
3 # Copyright 2020 TATA ELXSI
5 # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 # not use this file except in compliance with the License. You may obtain
7 # a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 # License for the specific language governing permissions and limitations
17 # For those usages not covered by the Apache License, Version 2.0 please
18 # contact: saikiran.k@tataelxsi.co.in
23 AuthconnTacacs implements implements the connector for TACACS.
24 Leverages AuthconnInternal for token lifecycle management and the RBAC model.
26 When NBI bootstraps, it tries to create admin user with admin role associated to admin project.
27 Hence, the TACACS server should contain admin user.
30 __author__
= "K Sai Kiran <saikiran.k@tataelxsi.co.in>"
31 __date__
= "$11-Nov-2020 11:04:00$"
34 from osm_nbi
.authconn
import Authconn
, AuthException
35 from osm_nbi
.authconn_internal
import AuthconnInternal
36 from osm_nbi
.base_topic
import BaseTopic
40 from http
import HTTPStatus
43 from tacacs_plus
.client
import TACACSClient
46 class AuthconnTacacs(AuthconnInternal
):
51 tacacs_def_timeout
= 10
52 users_collection
= "users_tacacs"
53 roles_collection
= "roles_tacacs"
54 projects_collection
= "projects_tacacs"
55 tokens_collection
= "tokens_tacacs"
57 def __init__(self
, config
, db
, role_permissions
):
59 Constructor to initialize db and TACACS server attributes to members.
61 Authconn
.__init
__(self
, config
, db
, role_permissions
)
62 self
.logger
= logging
.getLogger("nbi.authenticator.tacacs")
64 self
.tacacs_host
= config
["tacacs_host"]
65 self
.tacacs_secret
= config
["tacacs_secret"]
67 config
["tacacs_port"] if config
.get("tacacs_port") else self
.tacacs_def_port
69 self
.tacacs_timeout
= (
70 config
["tacacs_timeout"]
71 if config
.get("tacacs_timeout")
72 else self
.tacacs_def_timeout
74 self
.tacacs_cli
= TACACSClient(
75 self
.tacacs_host
, self
.tacacs_port
, self
.tacacs_secret
, self
.tacacs_timeout
78 def validate_user(self
, user
, password
):
82 tacacs_authen
= self
.tacacs_cli
.authenticate(user
, password
)
83 except Exception as e
:
85 "TACACS server error: {}".format(e
), http_code
=HTTPStatus
.UNAUTHORIZED
88 user_rows
= self
.db
.get_list(
89 self
.users_collection
, {BaseTopic
.id_field("users", user
): user
}
91 if not tacacs_authen
.valid
:
93 # To remove TACACS stale user from system.
94 self
.delete_user(user_rows
[0][BaseTopic
.id_field("users", user
)])
97 user_content
= user_rows
[0]
101 "password": password
,
102 "_admin": {"created": now
, "modified": now
},
103 "project_role_mappings": [],
105 user_content
= self
.create_user(new_user
)
108 def create_user(self
, user_info
):
110 Validates user credentials in TACACS and add user.
112 :param user_info: Full user information in dict.
113 :return: returns username and id if credentails are valid. Otherwise, raise exception
115 BaseTopic
.format_on_new(user_info
, make_public
=False)
117 authen
= self
.tacacs_cli
.authenticate(
118 user_info
["username"], user_info
["password"]
121 user_info
.pop("password")
122 self
.db
.create(self
.users_collection
, user_info
)
125 "TACACS server error: Invalid credentials",
126 http_code
=HTTPStatus
.FORBIDDEN
,
128 except Exception as e
:
130 "TACACS server error: {}".format(e
), http_code
=HTTPStatus
.BAD_REQUEST
132 return {"username": user_info
["username"], "_id": user_info
["_id"]}
134 def update_user(self
, user_info
):
136 Updates user information, in particular for add/remove of project and role mappings.
137 Does not allow change of username or password.
139 :param user_info: Full user information in dict.
140 :return: returns None for successful add/remove of project and role map.
142 if user_info
.get("username"):
144 "Can not update username of this user", http_code
=HTTPStatus
.FORBIDDEN
146 if user_info
.get("password"):
148 "Can not update password of this user", http_code
=HTTPStatus
.FORBIDDEN
150 super(AuthconnTacacs
, self
).update_user(user_info
)