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