1313# limitations under the License.
1414#
1515import yaml
16+ import json
1617import os
18+ import argparse
19+ import datetime
1720import strongdm
1821from okta .framework .ApiClient import ApiClient
1922from okta .framework .Utils import Utils
@@ -37,6 +40,14 @@ def __init__(self, login, first_name, last_name, groups):
3740 def __repr__ (self ):
3841 return "%s %s" % (self .login , self .groups )
3942
43+ def to_dict (self ):
44+ return {
45+ 'login' : self .login ,
46+ 'first_name' : self .first_name ,
47+ 'last_name' : self .last_name ,
48+ 'groups' : self .groups ,
49+ }
50+
4051
4152def load_okta_users ():
4253 ret = []
@@ -62,13 +73,23 @@ def load_okta_users():
6273
6374
6475def main ():
65- try :
66- okta_sync ()
67- except Exception as ex :
68- print ("okta sync failed:" + str (ex ))
69-
70-
71- def okta_sync ():
76+ parser = argparse .ArgumentParser (
77+ description = 'Update StrongDM permissions according to Okta groups' )
78+ parser .add_argument ('-p' ,
79+ '--plan' ,
80+ action = 'store_const' ,
81+ const = True ,
82+ help = 'calculate changes but do not apply them' )
83+ parser .add_argument ('-v' ,
84+ '--verbose' ,
85+ action = 'store_const' ,
86+ const = True ,
87+ help = 'print detailed report' )
88+ args = parser .parse_args ()
89+ okta_sync (args .plan , args .verbose )
90+
91+
92+ def okta_sync (plan , verbose ):
7293 SDM_API_ACCESS_KEY = os .getenv ('SDM_API_ACCESS_KEY' )
7394 SDM_API_SECRET_KEY = os .getenv ('SDM_API_SECRET_KEY' )
7495 OKTA_CLIENT_TOKEN = os .getenv ('OKTA_CLIENT_TOKEN' )
@@ -81,13 +102,36 @@ def okta_sync():
81102 )
82103 return
83104
105+ report = {
106+ 'start' : str (datetime .datetime .now ()),
107+ 'oktaUsersCount' : 0 ,
108+ 'oktaUsers' : [],
109+ 'sdmUsersCount' : 0 ,
110+ 'sdmUsers' : [],
111+ 'bothUsersCount' : 0 ,
112+ 'sdmResourcesCount' : 0 ,
113+ 'sdmResources' : {},
114+ 'permissionsGranted' : 0 ,
115+ 'permissionsRevoked' : 0 ,
116+ 'grants' : [],
117+ 'revocations' : [],
118+ 'matchers' : {},
119+ }
120+
84121 matchers = load_matchers ()
85122 okta_users = load_okta_users ()
86123
124+ report ['matchers' ] = matchers
125+ report ['oktaUsers' ] = [x .to_dict () for x in okta_users ]
126+ report ['oktaUsersCount' ] = len (okta_users )
127+
87128 client = strongdm .Client (SDM_API_ACCESS_KEY , SDM_API_SECRET_KEY )
88129
89- accounts = {o .email : o .id for o in client .accounts .list ("" )}
90- permissions = [v for v in client .account_grants .list ("" )]
130+ accounts = {o .email : o for o in client .accounts .list ('type:user' )}
131+ permissions = [v for v in client .account_grants .list ('' )]
132+
133+ report ['sdmUsers' ] = {k : v .to_dict () for (k , v ) in accounts .items ()}
134+ report ['sdmUsersCount' ] = len (accounts )
91135
92136 # define current state
93137 current = {}
@@ -102,24 +146,33 @@ def okta_sync():
102146 for group in matchers ["groups" ]:
103147 for resourceQuery in group ["resources" ]:
104148 for res in client .resources .list (resourceQuery ):
149+ report ['sdmResources' ] = res
105150 for u in okta_users :
106151 if group ["name" ] in u .groups :
107152 if u .login not in accounts :
108153 continue
109154 overlapping += 1
110- aid = accounts [u .login ]
155+ aid = accounts [u .login ]. id
111156 if aid not in desired :
112157 desired [aid ] = set ()
113158 desired [aid ].add (res .id )
114159
160+ report ['bothUsersCount' ] = overlapping
161+ report ['sdmResourcesCount' ] = len (report ['sdmResources' ])
162+
115163 # revoke things
116164 revocations = 0
117165 for aid , curRes in current .items ():
118166 desRes = desired .get (aid , set ())
119167 for rid in curRes :
120168 if rid [0 ] not in desRes :
169+ if plan :
170+ print ('Plan: revoke {} from user {}' .format (rid [1 ], aid ))
171+ else :
172+ client .account_grants .delete (rid [1 ])
173+ report ['revocations' ].append (rid [1 ])
121174 revocations += 1
122- client . account_grants . delete ( rid [ 1 ])
175+ report [ 'permissionsRevoked' ] = revocations
123176
124177 # grant things
125178 grants = 0
@@ -128,12 +181,21 @@ def okta_sync():
128181 for rid in desRes :
129182 for cr in curRes :
130183 if rid != cr [0 ]:
184+ ag = strongdm .AccountGrant (resource_id = rid , account_id = aid )
185+ if plan :
186+ print ('Plan: grant {} to user {}' .format (rid , aid ))
187+ else :
188+ ag = client .account_grants .create (ag )
189+ report ['grants' ].append (ag )
131190 grants += 1
132- client .account_grants .create (
133- strongdm .AccountGrant (resource_id = rid , account_id = aid ))
134-
135- print ("{} Okta users, {} strongDM users, {} overlapping users, {} grants, {} revocations" .format (\
136- len (okta_users ),len (accounts ), overlapping , grants , revocations ))
191+ report ['permissionsGranted' ] = grants
192+ report ['complete' ] = str (datetime .datetime .now ())
193+
194+ if verbose :
195+ print (json .dumps (report , indent = 4 , sort_keys = True ))
196+ else :
197+ print ("{} Okta users, {} strongDM users, {} overlapping users, {} grants, {} revocations" .format (\
198+ len (okta_users ),len (accounts ), overlapping , grants , revocations ))
137199
138200
139201if __name__ == '__main__' :
0 commit comments