e04c0f7f9d2a4f020e60308522909015c09a8da9
[arvados.git] / sdk / python / arvados / commands / federation_migrate.py
1 #!/usr/bin/env python3
2 # Copyright (C) The Arvados Authors. All rights reserved.
3 #
4 # SPDX-License-Identifier: Apache-2.0
5
6 import arvados
7 import arvados.util
8 import csv
9 import sys
10 import argparse
11 import hmac
12
13 def main():
14
15     parser = argparse.ArgumentParser(description='Migrate users to federated identity, see https://doc.arvados.org/admin/???')
16     parser.add_argument('--tokens', type=str, required=True)
17     group = parser.add_mutually_exclusive_group(required=True)
18     group.add_argument('--report', type=str)
19     group.add_argument('--migrate', type=str)
20     args = parser.parse_args()
21
22     clusters = {}
23
24     print("Reading %s" % args.tokens)
25     with open(args.tokens, "rt") as f:
26         for r in csv.reader(f):
27             host = r[0]
28             token = r[1]
29             arv = arvados.api(host=host, token=token)
30             clusters[arv._rootDesc["uuidPrefix"]] = arv
31             cur = arv.users().current().execute()
32             if not cur["is_admin"]:
33                 raise Exception("Not admin of %s" % host)
34
35     if args.report:
36         users = []
37         for c, arv in clusters.items():
38             print("Getting user list from %s" % c)
39             ul = arvados.util.list_all(arv.users().list)
40             for l in ul:
41                 if l["uuid"].startswith(c):
42                     users.append(l)
43
44         out = csv.writer(open(args.report, "wt"))
45
46         out.writerow(("email", "user uuid", "primary cluster/user"))
47
48         users = sorted(users, key=lambda u: u["email"]+"::"+u["uuid"])
49
50         accum = []
51         lastemail = None
52         for u in users:
53             if u["uuid"].endswith("-anonymouspublic") or u["uuid"].endswith("-000000000000000"):
54                 continue
55             if lastemail == None:
56                 lastemail = u["email"]
57             if u["email"] == lastemail:
58                 accum.append(u)
59             else:
60                 homeuuid = None
61                 for a in accum:
62                     if homeuuid is None:
63                         homeuuid = a["uuid"]
64                     if a["uuid"] != homeuuid:
65                         homeuuid = ""
66                 for a in accum:
67                     out.writerow((a["email"], a["uuid"], homeuuid[0:5]))
68                 lastemail = u["email"]
69                 accum = [u]
70
71         homeuuid = None
72         for a in accum:
73             if homeuuid is None:
74                 homeuuid = a["uuid"]
75             if a["uuid"] != homeuuid:
76                 homeuuid = ""
77         for a in accum:
78             out.writerow((a["email"], a["uuid"], homeuuid[0:5]))
79
80         print("Wrote %s" % args.report)
81
82     if args.migrate:
83         rows = []
84         by_email = {}
85         with open(args.migrate, "rt") as f:
86             for r in csv.reader(f):
87                 if r[0] == "email":
88                     continue
89                 by_email.setdefault(r[0], [])
90                 by_email[r[0]].append(r)
91                 rows.append(r)
92         for r in rows:
93             if r[2] == "":
94                 print("(%s) Skipping %s, no home cluster specified" % (r[0], r[1]))
95             if r[1].startswith(r[2]):
96                 continue
97             candidates = []
98             for b in by_email[r[0]]:
99                 if b[1].startswith(r[2]):
100                     candidates.append(b)
101             if len(candidates) == 0:
102                 print("(%s) No user listed to migrate %s to %s" % (r[0], r[1], r[2]))
103                 continue
104             if len(candidates) > 1:
105                 print("(%s) Multiple users listed to migrate %s to %s, use full uuid" % (r[0], r[1], r[2]))
106                 continue
107             new_user_uuid = candidates[0][1]
108             print("(%s) Migrating %s to %s" % (r[0], r[1], new_user_uuid))
109             oldcluster = r[1][0:5]
110             newhomecluster = r[2][0:5]
111             homearv = clusters[newhomecluster]
112             # create a token
113             newtok = homearv.api_client_authorizations().create(body={"api_client_authorization": {'owner_uuid': new_user_uuid}}).execute()
114             salted = 'v2/' + newtok["uuid"] + '/' + hmac.new(newtok["api_token"].encode(), msg=oldcluster.encode(), digestmod='sha1').hexdigest()
115             arvados.api(host=arv._rootDesc["rootUrl"][8:-1], token=salted).users().current().execute()
116
117             # now migrate from local user to remote user.
118             arv = clusters[oldcluster]
119
120             grp = arv.groups().create(body={
121                 "owner_uuid": new_user_uuid,
122                 "name": "Migrated from %s (%s)" % (r[0], r[1]),
123                 "group_class": "project"
124             }, ensure_unique_name=True).execute()
125             arv.users().merge(old_user_uuid=r[1],
126                               new_user_uuid=new_user_uuid,
127                               new_owner_uuid=grp["uuid"],
128                               redirect_to_new_user=True).execute()
129
130 if __name__ == "__main__":
131     main()