1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
##############################################################################
# Copyright 2017 Parker Berberian and Others #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. #
# You may obtain a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
##############################################################################
import ldap
import os
import random
from base64 import b64encode
from st2actions.runners.pythonrunner import Action
names = [
'frodo_baggins', 'samwise_gamgee', 'peregrin_took', 'meriadoc_brandybuck',
'bilbo_baggins', 'gandalf_grey', 'aragorn_dunadan', 'arwen_evenstar',
'saruman_white', 'pippin_took', 'merry _randybuck', 'legolas_greenleaf',
'gimli_gloin', 'anakin_skywalker', 'padme_amidala', 'han_solo',
'jabba_hut', 'mace_windu', 'count_dooku', 'qui-gon_jinn',
'admiral_ackbar', 'emperor_palpatine'
]
class VPNAction(Action):
"""
This class communicates with the ldap server to manage vpn users.
This class extends the above ABC, and implements the makeNewUser,
removeOldUser, and __init__ abstract functions you must override to
extend the VPN_BaseClass
"""
def __init__(self, config=None):
"""
init takes the parsed vpn config file as an arguement.
automatically connects and authenticates on the ldap server
based on the configuration file
"""
self.config = config['vpn']
server = self.config['server']
self.uri = "ldap://"+server
self.conn = None
user = self.config['authentication']['user']
pswd = self.config['authentication']['pass']
if os.path.isfile(pswd):
pswd = open(pswd).read()
self.connect(user, pswd)
def connect(self, root_dn, root_pass):
"""
Opens a connection to the server in the config file
and authenticates as the given user
"""
self.conn = ldap.initialize(self.uri)
self.conn.simple_bind_s(root_dn, root_pass)
def addUser(self, full_name, passwd):
"""
Adds a user to the ldap server. Creates the new user with the classes
and in the directory given in the config file.
full_name should be two tokens seperated by a space. The first token
will become the username
private helper function for the makeNewUser()
"""
full_name = str(full_name)
passwd = str(passwd) # avoids unicode bug
first = full_name.split('_')[0]
last = full_name.split('_')[1]
user_dir = self.config['directory']['user']
user_dir += ','+self.config['directory']['root']
user_dir = str(user_dir)
dn = "uid=" + first + ',' + user_dir
record = [
('objectclass', ['top', 'inetOrgPerson']),
('uid', first),
('cn', full_name),
('sn', last),
('userpassword', passwd),
('ou', str(self.config['directory']['user'].split('=')[1]))
]
self.conn.add_s(dn, record)
return first, dn
def makeNewUser(self, name=None, passwd=None):
"""
creates a new user in the ldap database, with the given name
if supplied. If no name is given, we will try to select from the
pre-written list above, and will resort to generating a random string
as a username if the preconfigured names are all taken.
Returns the username and password the user needs to authenticate, and
the dn that we can use to manage the user.
"""
if name is None:
i = 0
while not self.checkName(name):
i += 1
if i == 20:
name = self.randoString(8)
name += ' '+self.randoString(8)
break # generates a random name to prevent infinite loop
name = self.genUserName()
if passwd is None:
passwd = self.randoString(15)
username, dn = self.addUser(name, passwd)
return username, passwd, dn
def checkName(self, name):
"""
returns true if the name is available
"""
if name is None:
return False
uid = name.split('_')[0]
base = self.config['directory']['user'] + ','
base += self.config['directory']['root']
filtr = '(uid=' + uid + ')'
timeout = 5
ans = self.conn.search_st(
base,
ldap.SCOPE_SUBTREE,
filtr,
timeout=timeout
)
return len(ans) < 1
@staticmethod
def randoString(n):
"""
uses /dev/urandom to generate a random string of length n
"""
n = int(n)
# defines valid characters
alpha = 'abcdefghijklmnopqrstuvwxyz'
alpha_num = alpha
alpha_num += alpha.upper()
alpha_num += "0123456789"
# generates random string from /dev/urandom
rnd = b64encode(os.urandom(3*n)).decode('utf-8')
random_string = ''
for char in rnd:
if char in alpha_num:
random_string += char
return str(random_string[:n])
def genUserName(self):
"""
grabs a random name from the list above
"""
i = random.randint(0, len(names) - 1)
return names[i]
def deleteUser(self, dn):
dn = str(dn) # avoids unicode bug
self.conn.delete(dn)
def getAllUsers(self):
"""
returns all the user dn's in the ldap database in a list
"""
base = self.config['directory']['user'] + ','
base += self.config['directory']['root']
filtr = '(objectclass='+self.config['user']['objects'][-1]+')'
timeout = 10
ans = self.conn.search_st(
base,
ldap.SCOPE_SUBTREE,
filtr,
timeout=timeout
)
users = []
for user in ans:
users.append(user[0]) # adds the dn of each user
return users
|