mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-05 06:41:38 +00:00
Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31f83d33f5 | ||
|
|
597256d048 | ||
|
|
62594a2898 | ||
|
|
00582d486c | ||
|
|
cda626b01c | ||
|
|
7d84da1520 | ||
|
|
11b96b488f | ||
|
|
1853c0ca32 | ||
|
|
0b8fb177c4 | ||
|
|
4e80434956 | ||
|
|
c2f53577ab | ||
|
|
4974150357 | ||
|
|
1586d97295 | ||
|
|
5f65898c33 | ||
|
|
88e7941db3 | ||
|
|
6c715263e0 | ||
|
|
7088962d44 | ||
|
|
429bb0957d | ||
|
|
424fda55dd | ||
|
|
1b26a11281 | ||
|
|
56f52c8623 | ||
|
|
908edff878 | ||
|
|
487e1dc4c1 | ||
|
|
244398e096 | ||
|
|
fafd9e2bd8 | ||
|
|
367ea4df39 | ||
|
|
630abbd0fc | ||
|
|
fe20428a14 | ||
|
|
0e36681ec1 | ||
|
|
884cbc52a3 | ||
|
|
88c17af8ef | ||
|
|
549670e45f | ||
|
|
4fa0e58e80 | ||
|
|
d60b9b2b47 | ||
|
|
3368bd3879 | ||
|
|
dbc47c5420 | ||
|
|
f86b5a2bf3 | ||
|
|
0e0c126726 | ||
|
|
45e0e57668 | ||
|
|
7ee1edbab8 | ||
|
|
747ad9f29a | ||
|
|
7e128dc6c3 | ||
|
|
5e1352077a | ||
|
|
22fc54b2fa | ||
|
|
b67e068991 | ||
|
|
40f5bb07d8 | ||
|
|
c1063d1967 | ||
|
|
964cd19949 | ||
|
|
f55305a800 | ||
|
|
8392856ec5 | ||
|
|
01e1551838 | ||
|
|
d3f042433d | ||
|
|
0e5635cc2a | ||
|
|
73677544a3 | ||
|
|
7c46d8548e | ||
|
|
186381426a | ||
|
|
af1e695661 | ||
|
|
4ccd51269a | ||
|
|
560cfe225f | ||
|
|
e9e4c3d333 | ||
|
|
dbca6e3b88 | ||
|
|
ad465ed20c | ||
|
|
9370f7ce15 | ||
|
|
d9151a866b | ||
|
|
7937fd00d4 | ||
|
|
d2199a5b9c | ||
|
|
6e765325c1 | ||
|
|
18119b3d64 | ||
|
|
378a7c2d6c | ||
|
|
1270a315b2 | ||
|
|
931b2cc700 | ||
|
|
e145ac0ad1 | ||
|
|
ab8e882e94 | ||
|
|
b66d671b74 | ||
|
|
f662a13778 | ||
|
|
845aa122e1 | ||
|
|
bb19336d06 | ||
|
|
774948cf9d | ||
|
|
e26e077c83 | ||
|
|
f264ffd040 | ||
|
|
7e16e4880b | ||
|
|
dd1ee6ff44 | ||
|
|
90d628cc75 | ||
|
|
d5a0b33f04 |
69
.travis.yml
69
.travis.yml
@@ -5,33 +5,12 @@ dist: xenial
|
||||
|
||||
env:
|
||||
global:
|
||||
- BUILD_PYTHON_VERSION=3.8.3
|
||||
- MIN_PYTHON_VERSION=3.8.3
|
||||
- BUILD_PYTHON_VERSION=3.8.5
|
||||
- MIN_PYTHON_VERSION=3.8.5
|
||||
- BUILD_OPENSSL_VERSION=1.1.1g
|
||||
- MIN_OPENSSL_VERSION=1.1.1g
|
||||
- PATCHELF_VERSION=0.10
|
||||
- PYINSTALLER_COMMIT=3010fdfaa037e9b19e936711d0c0be9b314b03c6
|
||||
- secure: "FSKvLaiqhKz21SVgAQZI3bSX34Ffyev4l+R2G//QXNDu6UVQcuFsykzw+eZEG7fkhotXr8BMDL7xIkookiL8eLwUtcd/Z95HCjPBBHcmCSQleyvuuJBxdrQ9xldmiGLzMCYiumSH9OH4uJhQ39Yjnjsa8TK+PlTci6a/BTzlYyBSyDYDf7Iv/uhfQPDHL3pNwrQPHf4fL6/jcvo+uaPcv83AVZkNzZjjyoi9Aa+uh9xlbyHg11jp44463qqxoxTdYik3pYuXRBPjknjOGcnFHqn+QOVSdRQoiwbmT8xVuYuCzTv9THhuJ//i5u7s4y3Xyl7u17B3tdm86UlMpQHy/w9EsYaSBPOU4oPNomRtOnTSugh0v9ZBwptP5XfbslII/iA+LQdzTHhchn0W0CRyDqjOMSestWlrsq5NZJtBJTYHbebllOhEI7xbj9tY+re1zFWSPMOPgHJP23ovsdk3hD9OT93AzRHInCx5IxL6QvEgRhAancRuGkf2rGP0g/vX9fQ0Il3rNMSQxHB5CyHUBtUJ9nhU79YkMDZicD0jFMEwjWJO3itAp3ynoLXRgktgQCYUfgc9SpdWKD5SXLCYnSo22JD3D1P6h2EertRHaoKRLb+CRXQC/lM8uh/W+BjA2Xe6Vut2I/72ndjM+10T7E2xk1CFyCH37a5p8cH26Fs="
|
||||
- secure: "J9380tGLOZWa7dSH1y5Il8T5JQpN6ad81gI6VR1HIU0svpRdjgikyDA7ca2MKYDUYYY9yVSkTV6gCl6iIU/9+SKaYugpP+tkvdGYkC2moJdcTgYM/WOnIK9ExQ3BPhN1neGxJjPTwKo1ft27mtZ2I5vuCiBwIcnKWLnKPyW3PD+mWpfqiLuEzkHoAh6G3jC4qbcCrZDeX/knE+PzqESUEi+8k1G8gYcSDWujba9ypSsqZ8T/MXagGla6l7y2Rz+/KZTJmFHwKAA10V+xPLVqxoiqi4ar66yUqy0BamwRXPcseI+ns3Q+4lUpMqVQ5GlRy7LF1xC8myjmcAexXk0F9hg+CMzewKI8UgmQH/ZJvQZEh8s6mW26+CqA4d3zMQkWaR0WtEtpiuH7AGHCflIqvEQ6UiG7ia3B8iZfW2wl0j/kqx4OuHkS3r0pWKVVIIvCj9Ow2BHP7SpiV1AcUGsVxzwbgTh67fitna3Z3c6Uj8ccQlNr7ZIt1az6Wf3w5njijkLOiBpQSLKunTTCTSge/JzBTKUcie3RE9vzirl58gUxAt36nDtPWnory+RttMZrOkBVbTeSxp+IUe8pNwLFPHABsafXsjkfzBOtFmm+0ZXWt2Rlog5NvlemJfQUWDlsL4g+BSakzN+4sIPKzSauWDHyaEeULY7Uprkil6c5zwo="
|
||||
- secure: "szcjWHPr0Bf1KCkyTrV5Fu3ADhWk+pg8YWucjXHdybmhaQIKG7iBNg8LJ5d0OBTwAg31wK4ZgyLVSa2gKrAZ3UeDjykJFsR711xDSQOod51Wrgqu4FbXDewE817DUk3Cwe1l5DCu3/fjEw4vbm8B/qb7iMTRKCq6hJd97FwT5oauP0QHNPer9JjrW4F0Hk9ttkgEU2dXWvBMsTJsDOGNI3ddABE2HskxV4T4thelDYGKBDHhUOAsRwSjXgWy77Tvz98psPIvd+6+WPYNRdRWcPDyAR3Z1O/fNjUymrQI6eMaHoSFrmhDS5lbhjINRfdUmECyfCfIFeLWWiw4g4bq7l+4HBORbei55tAIjhEsxJQoqHi0Q5dD5TFh8IiWqowkFbpvNonMSIpKtB0cyT5jU1G/jRA7MPcIvSrdzHaDkoDNHJgAeZfgjOhzTGYYD19lGIljz5BQBcNFZY2dJbja+Jr4He2CMAOBOdERa4Zn1VyNfOmd8Bn5hu0C9D2ybnSCxjXXq5TRiktR8X7WycVZYfqMZXAwP9FEHVitJ4MZEGUc7S92K5gX4wmjcJjLS+Xo/0nsduQm8PuiMjbcPM7/oGx8Xm1KuSfHdKWMBoaesPaDvRX+YcuiNstXf1DkCWl72TsFABzddlNUMl/s2YSKkCSHAJ5ILqrB28Gx89kzVlg="
|
||||
- secure: "CPsDgSoZIHLyjpUbYEsx1GbB+UZcPXCEsz9qT6XRM+qMFKLlSnHxoJ7gMrJtqfjTh+1gLB3UTjQjFr+jAenqLWzaJh7Zdtpg9BOG6aXC8CAi1h0U5ZMNSA/+5lQxOXuhN8HmXI1r8xPNRFXBdZFea+072/2HJTwmgYlVTTQ8FrbXQJCzs2cFqxnVeFmuG3N49AJuoy3B+P2DMpqPzABbQt7Jf5H9Foq+16iXxhRkYA8/8H2nF7ZE8IdJuRpqhBUoPF/8Mt2QBLIlvNIdIzFEy2O7ZhwL9Dt08AG9v5c91QPzjsLok2e+5hFGaeMpGQJCE1V3uHyrOAeyZs1QYr7qBWbjQCVh0Phxz9yOA9RPfnQdjyJTq4QEj6vVYvCi5K/CGp/DnnLnGyYJKZOt7nBx02fTVI136UXqt29eF5NRkCpnUqah2s0OXkijsw5Y9LsbwiUxELKtCthESOwN8e/ZNvn/gjPfZWIaB0gupRNugL8Xo9Cx6vFmaW8wzm1IutJvo1mWvkWMvuYYjvd4aDyP6s7PFnSX7DoD/pfxoQuzjwHO2OD93nCOx0ofnNojLNooeJPKLikiwS4qKc8exWd+TlnccKSkXO8Y8S+XRgeE652YNlf+9DH79lNLeK0N0W2tRExX5JaEyJQCuvK0WZi02kxvjmUHhwLAOv9ueC2UCRs="
|
||||
- secure: "JMv9mkSQBJXvUznXcyvngFaOfd2fHYEQQTH8BnS03pqAL0HkWSheG/R2HFJlJv1VJ9MDMf+wVzwMvU/kzkOT68nIgyFWnLBkT6fw+Mw7t/dG96/nOh7DPuaTVzKS0xRbMhDaqZOA9GW13TVnpVdIw79vbhQM69N9Gj80j8oM1cHgMi+fkJSDU3EN/vMJOGKSB3DTpyqAG/nYyZ38tLib8ic12za/YL3HIu8QRHa3fr37+cyrVKgGebGg++yK34p8lC1W4apiO30drmHVQhKWrWnmdcssdGmVM7NystMGGAzUwsJcmRFJuREv1LXiijDzjIRVduceeRYgPi1KHB5ZdL9vji0gM1eHYZZefhcJb6WgPDbCtbjdlCId4v+1bNfsh+dMmhM+vBZDtDEt3UC3MBeYTmklQT2w9zCXHuPBQigj7W4zLm6GxnAXife0SQMfmr736QBvSLUJWtd2tRgS+dRG/LvWxrP5Urvfgs8iRtEVZLDpRR+bSjvLs1UEKLN13KMKYuVwieHbxJn2kY7d5wmZVBYdaokl0yrTkZZ6J9xIphxM8GXJU1BnMZY9zn+Xq3jm576QYnNYUwCtUjOis00Ct4UVuuFqIqZ7bJL7GT4AHTENAk+9GtXVCo3/jnv161rxgoSdnUG6VmrpnyA9jSpcIvMVcE129oghBJ2+PkU="
|
||||
- secure: "otePbb9W6CAuwzayL0dDAvCryzF2s5HHAIytvMW/xnzOuRMR03lumj9bhg56YCB/nIuTncAjToYmsMh2Acrp1Xcx8/DzqKse1K9nkfJgsB+EYeXmy8pVJnCxhHeLCzUPJjUfRoPBm+5GHAkeoviUyxenwbOFm3/Uhtay7i2ZU8K1FsECqx4FZXVh8rrGHqFGzHNimqDDvFUQM6f9a+BevEio8+/aDooaFfvVqPixDYXpi2+99DB/0hZKwcUpj6pOUUszATkAl0kJdOSWI6E7bQa63i6SfhcBNHfvbDqhpcAGs1TIXHbXwWGfySABKgT9mkq64CY3bc/QZ6HeWe6P5tJmt4mGBxDMOWMYj4qtCskQCCCiY9+sCx61Sj8W5w8exjQvvvFA088cKtbCKAx8FIil4CuYPtIjDQFkiNI2bC6BmkUen7qe4z7dSF2AZPb6CuSsBjoXL8Ezle3e1pRrkR+SYbxMSaZVcQoG8hinTqaXhlS5gXi97ZzRrJWn3tUB6JWEBeEkIfTJmrJolbLYIMcADNhKenrW8k6nV32JkvMsCgMFCIkJsIZeuQ9ZYgSZ3CXPODux/D2/4/gNa+353Fs3DTlDQxRTgoaYi9UDIRrKZAkoELFclxuGSKMCCheevQ8FMCmXfxUocwLSgjlO7g4rTJa9Kggn7VltucXiVWY="
|
||||
- secure: "WKdB+WpZG/7SKpPpgM6DAwzVg0QQuxFMJEZE324pagJ74xv+GlObTLm6kU4SAZf6TuRSChTXDAD4paJXkxpq3LlXjp9aKxPASbG5PgZMnGL6bZ40c/UUqrwVy2HLnXPKSNuDBX1RidGXmH3u4YvufjguNnlOLvHYfMHIRGjiEL4ZvC38GB/3YDmID1zI6w8NzTCpFvxeNdg1qhhOBUKyt+icRUwBu8DfgLidzTrO0j5jo3UeqO/w3E9t3nnRgSiO25mBYwWySm1QBK1VS3F5iG9jo1J6GKMOFbWi3lOBoqnWotpfwID+p1vMNX35phHwmMrtoBYfMTV4A2CvQpGyQhn58qyMoKnhLz+NIOxlHN83qcu65bTMG+7ji0pgUl3jhp3ZPyWUjRYQpoVAg5f3UAnUzJgBrOUC0N60ukJVacT/kvkO10CLfz+0+eefW53r+GCkTi2m8iDezZn1olinpLs+Mt0M4VfQ52RVtq3WB6uxN7RjgtrK5XGi2zVAsvfHp+vqFQ6uu+iioiWNji88cTlMuKheo+FWwozNQzd8+4Vxe9w01mLek57Q0dpXiKWL3vMS4Qc4T9E6UzutllDyg/c61LW6Afcqw3jL4HN0UNG3zFefno47oKrmB13HcxakLEC8sdNfE1kWGvB7jqD0snXsz/8rz7rf0IqnK0kaCAc="
|
||||
- secure: "Lah0Q4bWyEJXTBLZHCNkuEuU5wlDQzLF6aQwW47RyVtyRDYW8uQRQFkbb/3oj4QAAgXl0sOcDFnRaaWmlOStPIIYKKVY8Mk6ufyDCz8inWoPNJoSbdqbAi761HacGUsEfSNDrPNB7fst47UZaX7WNAO3q1QjCCGSEJQVpJE2MDAjcSJhAgLPt3JRFSOSwvqxcDLZI+wF9AM81OWNp6QisAdI4LPUJK3L4M4dLh9+apFRb1OMld++scWah1SB5Qj6tPzMvX96mp1XfWB1fJXuf8Yvks02H6ZcAXubK2HXe3iJZKVqGB67kShNN52P+wg0ZZO4OP4kZ2PXahnB7hhxIfEfWsNVy/w4ww1N6K907BfT6RDmsgNP1ZP8kpq6H4pTnWgAy9WDxjatzbNFAHtMzakGjYJNFxZJVco2FL0ipNQ7htoVAA4sUb+VbRQv4O/oZTLMNnnco19+TFJzuZuS4Rjxj/zX63gXkj/W7wou3hw+NpI/PL2hUXIc5imXSLfVQwri8Nl+6IOjV2gWR/vR1VhQqbLsTF6TZQKNI4lvbRTs1nNeqNMW1lHizI3r9Kernj2noAsxJK7wFTZ64OUkQvSNphfmVow29JYQKXbpxbmRrdnmfmtnMsxUngpx9WsTPhrprt5hIAqiBspHemrs+H3LiIml6IY/3l9bcPlc0gA="
|
||||
- secure: "sNc7kC0CuH/TCZh2WwEN51GcA1fSpcliCHpFB+WX7ieQiRu3xKn2avby/T7vbvX0viXRER59arFGQF4i/dyr2g4tlZLVRYjPeiApfduKZ7Lb+vZGro3cWesfHG6Abk2VcgZZli1IDrgrHH5qAWnA9xNnKvKBL9NDM73Zj92BmlDFVEzadTii8brWvced/YP3jNXEmM5ZIufgpe2yidBB2bLWYJXb3Cf1MvzMG4tqNAtZTrI32q50mokz/uTqp3MRJ+cR8sOI+2+2xSbT0zZGLSRZf96/7FKtE0QIDxdWAe4XdlHq1CluRVk30Ju5BEn0QzoYLryCIuw3JjDl1Yksw4IA5imljZJlOmWa2l6fX0HNxMw+z0R/1d2HARA8BY7/uQKv4guV3Cf3jpsWoKSsM1WxqOqsuEFOoRQ2eQNJEaSuC6+j/vzNoj61pOuG0R9OC2PFcFCZ9fomIrZMse+7M3WIj4+mp7e+JDK8DgVdUlqkBVCx1Ospseb5pm6lDx8F5NbgqZgGXgyoWVpqZnyYOoOutezMoD6MI2wXzJaepV/L3+LD5f6q3DAa/sRAEEsBFGyMHXiPYbziEiy8Hz09Sz3inT5rzS8OLOinwAI2sHiIYHTl340XfWdYz6AlNhYLCGwwmtkntbjOj5UVW06IgBBx44ujpZSUjv7SOrACPGU="
|
||||
- secure: "t9/mC8eSsxXIB2vjcZGtGISxrSY3Yd+XS4+/i1YG1mxSov3R6UdhVl2blLgrvFfVCSo61YMAzJbXKtZlXPzhLDXxpsSlWiatjNrKKo4D2unnlHsyEMJ4wfz8cJ8pKyynP7Kc1ZuaqTSMcEZgk85tlew8Zy/VH6g3Mc/7DvP4SxEDRsP+DdCplZc0vbxHDaV3iC93bwNRfy1UQypwJJ2WQviRema3Umneg8hVl2V35zbaBoy45ubCkUoCbRCDaUyoHA10GrE1OUOLsioar/dj6K2W3EhAtehHLWrUZhhl07rayrrRDQYDTdebifB/MWlvR5FjM/Dr5M4k2ciss4ol3IR5LypRLD+/YBtzeIuqkDbUkaBowY+oUj6OWlzEbzAUrUNa5mnyR2jhr8ivZUeEEWLxljsu8gWq65mzgiZt/u+kVCnMLciUv0//0nrsNsEMI9pau2ZxbcpItFVKdZYXFdmHWG+qPCgMcbgsUM3xqaIc7fNwbk2Aa6erIGqD2VkwWzD/xksweg4lsgQ0N1tXMfoWWKh4Xj/OFom+S+3w9uSx/jQma7nXm+PKQL8dIo7rqza3fz9biq5T6mhwUrqCpFIJv7mrMwaTT1UfOchjiLL3CGeO7Amv7Avmh3fhMPbypF0sws79d7ewZbyv+oSzn+pxlCdzBU1GYx6veApbzhg="
|
||||
- secure: "ljVx3TgpBJ/ylMKDVmXabi9UNi5YvrRM5UBTrRk7XPu1YFYS4FI1GcGlyvYhToc4fKt4jLhX9qU+s/rZY+odO0x/HpmJglMBCrY+QcWOzuyaP1U5dCET+evuqFdEAZIzLQc4VDjL1aQLZh+OG7bjoBClVAan6a+pmW0yxBC6rNtCWTESG4rY3wOeTpoI0Q1gM7gg5Zkj4Z+yLvYeJdoKHijM7C3/R/VVTqUFqArk+Js7Qb2qTqm03SHP0ahRQA8XSfbPebSkJyX9oLbidanBEaQE6sqnp9Qh+8VGcnn7VkSu6oq2+ZXz4xlSMrH2Iv2JXl68Td51LsLo9BxaMCL68ssgTFfXPSrrcLwholNEt1pXk5nhBl1l6MZ1UwUJyBm+AXZp/4sCK9/P0rGa2d1rOcpOz7nobH7BDktqEJkrR6VzkTMx1aOwtF+JSt7SJQ1RrRdm9uKfOZZsnw17+VgVAHo/ttY0C3cRl10oaF1C/IdliDfa5gJdZ2VSZtJxyewqKwGiZrqCRv2fQyIuGsqfHXsyHVL6q1KfVcHjaXBvh0o6xZ6duieFT4FNHg7clv1qPQV+cLh4L11nugiihRTeYQtKyUnP5YIL+jlcGNM6KqKhF9RN5c+zOqWNmEcz6O8nljY5mFWdIxL6dFfE3+4wpw7snFP0PrWIWl5SmrU5ipY="
|
||||
- secure: "L0ivSUmbOHNmKWVR2zeYcVR+Xr6780dYA829MyksXfg9OrieTS+qVqSODKexFW89dp4Wf8xFdT9f16xSqjA/xSuX12uiISMiTcFKP39ZnQZ//NDkx1T4pZaeNiysqT+2Ys9OaKTWxn8luFtAYp+DaluancQtEw6+M8H3jQyQZimGHl0QB6BK2A5VA2vko+7ebIdwu5Df5E7lc/LG42H+DdSFkSj7Meu5XMyPHSZRAdOE5doO++tqFKzgs2WbDRHRUP4R4LqtDlGn8+5qnQtQKUN4V9UGrKM95R34BhgUlgOqOFAjFDug71/1/rRyv9XnzKkAdTIbxV7nmxSL+APhQDqpwPr01EP9gtZBOycswV/igJYotgqkhzwNAYrmwOA5Ta8S18Ck0feDEqT20w8yKS4QwV2Ihg4q7CqDFu7i+9iSRUCd/RVrGtG0U5Zo1b3+1JcNxXH/ErUeyHPfrk4vktxfHmU+omqwkfvTRy5upn0Ycr57YHOJc/Iyur7vE07HcnBfAwV0d5KO6HJ7M1n6hmMxeyKmf+qiyQQnphySvHHAfa/9Sec7omwwKv4bn33DwFGc8GWvL6cZXWhmGhFvd+LslpZl0vZ+7Bz0tXlAg4t8V48y/ZtyoSpny0HP4UstdA43PttvZ6q2y8LUNk4LBP5btKmp27Fszuj/rldtj5g="
|
||||
- secure: "Dox8JthAJqWT9eh3Jt4Morbf4pGN9OjduJXe/lYMsmFvqNg7b94P2QdumWBPmVjDq4YjDVirMejBA/TNwORgKfg7pI7MOw+qqoHpT9xyPecXi3ecyBay13e16p0GNRlc5pUu5JcU8sgCpttvM0EAw6bOuQIhnnkIFesbOvwoxGYjMzjmWMNuikR3CjKbo0LDtD5NJXT1OMSqrRuh8NM/BoKyn/kCdSaq9wI2GUMbkg09/kFJkQOvtMXPkM7dIhr/9UC0ouMIyqe/MHa8O6Y4xESdqiTql9uz1+eZfHIRrgFlHfxDvkMv87Cx5OuL+O+qeT/a+RYLCRJspMoq94IYHGg2iyEfBO2YAkogl53wiEf2KF2JdiNGT0xId7bxCJj3efTuCAXV1oqaHpJli1Mhvs7zPEtf78B4tkWEgjhGr5pBLIlbhNjS5wtTHJX4BUzoiP+wODj4h7rjPAah42nWF8XOMlboVi56sOCLjiHBOvYObqyhSfiQxoi2XHphsrZqw6H03tr4Kqd9HVmuoSvRiv+NOu24Ubr6MrrQM2/G72TrTx0/aBlt8Dx5nx2oWZ9ZMiDUR3XlvDLUi45SpY5qESXz08nRlcdS9EvUpK7C+77bNvX+A3dIhsxnxuNaf2naf+QnYYbvh7q4Qbrj4v6EMYS90Uky1JHdoc2wMua8J+w="
|
||||
- secure: "Is2Lv/rxKKrXnxFns9KQYseD02tjY9qbgSteVtJavG1cLJDvkTwb6X+Thvgo4cxk5fQPiXScrQaYzHjVVuNleD+dyD1HC/8CU2Xq+tjBhPjdcccHFSbk06DpcETmLGRyMORXG9JkYlgXHLLXtu/9icppWEHgra+zvchVL2YDhofYne8FMNBb/lq0AAC2wgzAS33tW1+57HaYnzl0hf6+Q6lwqoH2/aTfGMRFDgyJ0HK+5IVfLnQJ+OuGFSrj8/0FWSggR5+EXDIddDgovFgaCghMjHYp21bzn0eIAJtuNFFultwk/UC1lT9joXTKEzgLTAh+w13yz1T3x9rNuv6FDKCotBIS/ZDtPmgvyZ8xvB4SzyULnTRSVn7YvspKR6PAO/qxGNudUD5H8tRKer4qKnKjHzSUcVBlRHb4yE6FqqY1Z9RLEcomWO43nJwb6saNHR9BYedyi0gA+EbA+P259QFClW1dWEQ2LQhDa+0VRssOqZ0BQblPFyz+e5Vc9kfAMbOuoss8fjkiYv+twXv7nT27xrVT5okfKDSiy5opZD6d36N5FibZPYiMrVx00YZdkFB+5EqQuJ7lqKUMkZJTeApLzj+h+/4aAOWd3paj5ghv7m+9ReohsNKFHyjaSy97RhMAZjzqgMMdD8rjUSKDhvNKvvQECWHlaXwL129GB0s="
|
||||
- secure: "l7vWxfcu1RgXbStq76Mzz2I5Iu4e31729OyLYqulXZzft3wO+idvgQKy/JSwajiKgOxlpBuI0wrncgIUYshcRvE4yB0y9+QIMDTegJzTADtRSUyVNCIZfTgvtOvzrlW0iCdVsxLBtYcJWJVPdjF2q59ED1jahd1AuJJqX5e61gfr0eZ9+cNiHbX1u3VpmGchFNWQF4KvebE4WKs5xWEds+AtbTODdQq3H6kKQK3fTVJnbz6WMO+bEWgWI7orfSE20lku/3Q3eMLhNOcPwH8WnUoTTvDWol5Cq5NfPhkKF5aV7kIbNXkxswM7yBPAPumBiXdM1BHpfd4+0YQ/fqRtnqxw85HEYpT8dRewHimCl4IccgGCR+G0tK8RNleKL28GWrz86gRVpXMEhOU3ILwb8as3SdQWLwhXXy5uKGJmsdrCNAH4/8eu3XczO5VN2wOo7narYuBgGcl2eLW9TOLyNVKFKxbQnDLBiOybLNoOV42DCRYt7v3Eknkv1Z0dmX6n23q1z9if9kkfAFgRQWsTbNZyWeIwWuX8b2a0Zq0znS3JflSKxzqS7RHADfBOJEVM86AiGkw0XkR4o31yDrY+zOrkJ3GxfV/HpqG7LzCd9tFeexanneDCE1FJhCkDjpnc0Mdyx+1gVf+u2MkgrU0BbCf2EESBAlC/FTRvxTmk/6s="
|
||||
- secure: "bvtQ348bxKwTtB8X0zMxeTsM0jEJozbS6/rzH/88Fk90a+KO8SdXen0Kj9/LahV4duMn2xTTRmxMCVj424FbcVTgBkJpIO7btqTcNASORkmK+9wGTK6Bgb9R5sHLbVrbrMYJzsMQR0MWxE8ibPLlyom+ssUIqr7HAjnRyYSDiKChGhgBfdE/0G9OE/DuQaU/ZkTuEYfc4527QNLJ8Bt1aLDdCHJLxzCojNaTHB215Tz7dmpnJjWa45BXxkMwLTpxIJuY7wA7K+2UBJfLvZv5QiPYyeUlosV7FwhCN9e90+dc2x8HPJbwe/Ysv5dm8/FfNTgTe6IkKw1z9kev9/W1tYCtqMzn9GnlIH2000ZUomhHKbc4UUl8sskF2jGr14rSz7pfaTyvXNvK7nKLKM7mZAO/cAaQvZoGEx0s6B7a7YX38NmJWcM0UUD603n4G9uVZNoxjyr8HWC6pho8MY8/u5aFKMPd+C8DaDWeSzNJDZRyi62v+t2iyyZuQUtVXtnDpv8GQW4tYFIJCkkrTm5VNNgD2x3WRsEr3LsBa3BonOmi3a1VrdIpyidDzBEWFUR59a/C0lU5J2UX5W1vu/Pnr9/8p898sDvvDbluz47OR9xoVOjtmGdt/ENJa4EOY1q/eRIN6zrt/cwwjIKgTcYOVk7DFDMXQuYlDeEFuHSJ5Xs="
|
||||
- secure: "VWY3aJGMFB+E5ObJpUUXNQrnkEpA1pbfey8Z0pe9X/n5ubmDBIAfCPP7hqT/lAF/1hj5+HA6oWEa2FzeJZOL9q8cuymHzkehiX9n3f6GktirMtEoG3Hk/CiW/ZxKf9KyBNRVsqsOiXvfzMcMSU6ZrSV+4ZHIVl0zO5cehSaaiTTqVU5K474N5TCc8SAvpSRpxjZVRXy8rWF7WMaG4Xm6aa/IELccjJL8s6sahk3wT++5pzewxv4XBWf4bNA7+MNLpnzKmm2S5T/uohsw00F/j82g9xFT3qYm6vgaR4ql0aNvxd9gZ/6vdVpqBIUZmcFi9yTSFSsSSH2cNGFk3IU9Ecdh1Psi3B5pTpYzy7BbLWyKo70wBkOoY8qCwJ/wtj+2TcfR72qR8ryXgkbIoXxe04VkPqRNEezk3U+UxUvLLDEWthxjuCRCydT8FaCfYm9N+l5Un8TNmY4W9mOSu5IXKJ7oSQDrJG65Z2uZKQrWm0ToOo1nNLFclFuM+K8JWNOKysfh451LHdbmA0rkoaLUat2ttDBtQpbw3h+Hwn3SW6BfELg6WDJXHyAPNRMPSc2Grk2o7mFfTMJhHQG4U+k2bL+AHJU0nPWzuEJyMraquqKOCcHz56A+6j1PN3wpLbIC18nNPN9Q3TDPiBmwzwEg+zLu0DKTu3Oe4IRKRNTuocI="
|
||||
- secure: "BSZK3v2RYsNKv/8lBZ/dKagYF+znGXIeDY271A2nCS8DXx+y0octwI5RT08+UqdD1AvhPOPjdnSXG+M2NOSLlfBoXF/0t9S39+qy3EhGgxYAcWcVDXnrOrHEOALg1cQkCA2D/CG+eeG/36SmAn6FINTLaAOilxWGGCeL0KOB5mqPPnPogaYhowIwDkj0Hfsfrw8011XzgbufNr15GIYJ+nP7f41NcgIbnNkqXY6Q1jiWs1DuKXNDr5WGPqztHtVf7RtcpVmShzptAcAMdRqCGKF7atCZhXPbd69j3qf7F533/tCSCIrszR/Wp4BKlJfVCRCk1oiEcfRvprJqeFJ+0WJCVzyD7hXfnkBg2Tb5PmzvxOFM+hsqRu4mY3UdjqXukzAe5O0O6Uo8sqzqQUIjUooRnNQ4GdPd+7wMbyBZn4PJaW6YTi/7zg7mAegqot6uyEGGpWb6iFYrf6DX75GUfTciNktv+ez0AeqYFwNBLgcOsmaq+3V+aR+dIxsxIzC/i5GCTHvMYOIp6Q2tPCuIug0tX6uxm++vMOoMLK+vG1fVQ0Cd8m6yQIWEXUu0VBs1MTlJD+vX6LsK8CGo0Dt3ZS3JmC4TGGo9CBSEkeDvnKvQeX6x3NBIFWvgt+Hjxh4S9U+vW+AaUXpV9k+NEhesC/8Ys0UGbGYsTDzHdx9TMBY="
|
||||
- secure: "sskZYJ/+xmHh5GNvw3QRf10+sBGOjlub29jOTxjXIRYUyZfkhjsF8CR9NP59CBFuqgN716Mj1uEVEcx0aaepr/zWweQSnqa9lZgFD9nDYALVNh1b4oyqfhYG1sw57ZHdBsJOBF4mKBnahly/QQj11Ya25GyIS2IewwCWVNtCOJYietSssG9l0+qc+RPG5Ub/lEKA3VRrtbUuApcIYszt33DumgZ4wzkzICl1SuOy8V15vHVlkBqASJheEDa4Hia+eAMo6e7OCE5KWjl7K2MxTvtszwFi7lgMCyPCOy6DkaULACfBnQeloh42B2Qhd/ta04vuRKg6y4fKSrnegpeOAa9aGxF1ufvG9IvPsRTsu4+w63b9xsQUN9MyDgxcJe0YeUHPgPYL6mqAjCIKvL62LlIyrQ8IAteoh3MC+4Xb8crXaLGINTcLwYvLwxsHMuC/58hdQ23I4DqnyZGAS3L1IhpX4QvYN6xc7H+ptaiQttweoH5VpMAduSrmSnNmvLOc4g2PvbRES6D95moQ7qk7iX6vpu+PevL5HtQCbB8SFyFLTYWsqZaG+4NrjpIi2hLzUT35meHwrvxG/MsfeqOVlnBfa93VnX75vxOkRFNY56sOtrTJaiVZ+rQUdszAZz3KGPbyrSlz3KxV+ZKxZH6/0oFteAJttXFjWQdICnKGDSo="
|
||||
- secure: "sdzU5bPs1w7Nzf3F5Gtk/iq2Kfq3zfLQNGcehLZp1gJXzNf7F6HZLOn6GaXQ/5RVlhqR5nOmzMpVQs88rZv+6PE6YqGMugTxHIQeNmXtGnuEDJSBvGT9Ok8ENxcwKwL93g9SCd9P8mTspxklOsW2Bk3No2+Zlc/aQguBcX44TwYF42KuBU4O7oS6pUg8NjnuQp2zTyhp0ouzyAudatPyu1BLci/3lbx+MehutQw1y4Om6g8tfwf950UONZQdqq9MBluu7yYb1oHkdC1J4qgwdCZkJslWIwQHCH5UE4AW9iVG0qVrLpzBGtV27Kfy/Vf8r2gMYzbiur2h2+zzWSDm8/bk8YLn7u1FBpjGJdJ0pX0ZrZr+hMV2vH3e54NvA5WyRU7tw8mZ1PoDYd/FM5KXIYbscSbSRCqTsbPgyVIHuoOWXeeSe+/Ef1ifZv3HHf6ARfUIWupKfAipxChc7QUMI/HEQ9QPsqgBaooZD9chGsWAgv+8tFxdteqkx4Yh+AuZp1rVykB/9vAamUBebQxy0oeGm65j6X1rksfjAPkfeDYB7L4Ruy7tUwPtvYrAHPoWrf9O0g/qDRsw0vdqp42CjszpIxhuPhVRDx0i0wquqw2LnIU3ejRv2R8d1SecVKBBcVsWLFvc9iR4rNIi5JnxITRtiwL1Xv3Vcgx7WwxExtE="
|
||||
- secure: "f9n1KmU5NuV4jGkrhLNiPD+3Cy7t4D7Rq8YsPlmyB2A6u6IHMuOVP95IwH6Zt0cmMDBrZGthj3/0iu5kzxzqD0m135PT17wSEqsDfDMyKRZJQuYSO/ESVxnSca3afRH7Ds7/ipQVd/ljZgwEnW3JMaOQiIdbbIquNOOTeY0/wsXkreDamXZaKKqUoeadkAV4AkKhM0xcMg+Vni1i71TYPBWrZPLVAu3ZrSvU/cE5mtBUIkbr9EgsEE+WR23QCgtwxKzNxrXetBcPXDsJb98/ABgpoItm5Ko/Zk6pkib44f+iQtn7Y6j3lieELCH5Sn0uy3RrMxkl7xicB7zPYME94NEPHCmshyVsH3RxWfcBG4kauRNBCLYLl5HYF2t1lWZ6In5qlx8xN3Tf0KrbM3vzAEOnnfZt83h3q5OuWl+jzTOcv9xHmeW0lnwEEfS0nxUV3KDqLFBczcUxKBmsA2aWnJZ2HV+kls6OaWZ987m6V2pIGR2uviGT7I4ngjCSOJzbwYbvJbJqYAI97FWP/5pqv97xHLCSSJh6TBO4NMQ7Ib/W1XT6NZyPOjNGSLpd79UA3cTzU2+UYr/7RxZpAKONSAMTJh7CX451XQwgovpFZ2quXs2BzqcVpy8AlUc45ygnFOALOANAkcRP5QFhmff0jpXstjPW83/GksbtaiLhIiQ="
|
||||
- PATCHELF_VERSION=0.11
|
||||
- PYINSTALLER_COMMIT=ad39eb8df209d02636399ffdc44521a97886cf8c
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
@@ -39,7 +18,14 @@ cache:
|
||||
- $HOME/ssl
|
||||
|
||||
jobs:
|
||||
allow_failures:
|
||||
- python: nightly
|
||||
fast_finish: true
|
||||
include:
|
||||
- os: linux
|
||||
name: "Linux 64-bit Focal"
|
||||
dist: focal
|
||||
language: shell
|
||||
- os: linux
|
||||
name: "Linux 64-bit Bionic"
|
||||
dist: bionic
|
||||
@@ -48,6 +34,12 @@ jobs:
|
||||
name: "Linux 64-bit Xenial"
|
||||
dist: xenial
|
||||
language: shell
|
||||
- os: linux
|
||||
name: "Linux ARM64 Focal"
|
||||
dist: focal
|
||||
language: shell
|
||||
arch: arm64
|
||||
filter_secrets: false
|
||||
- os: linux
|
||||
dist: bionic
|
||||
arch: arm64
|
||||
@@ -69,7 +61,12 @@ jobs:
|
||||
language: python
|
||||
python: 3.7
|
||||
- os: linux
|
||||
name: "Python nightly Source Testing"
|
||||
name: "Python 3.9 dev Source Testing"
|
||||
language: python
|
||||
python: 3.9-dev
|
||||
dist: focal
|
||||
- os: linux
|
||||
name: "Python trunk nightly Source Testing"
|
||||
language: python
|
||||
python: nightly
|
||||
- os: linux
|
||||
@@ -87,7 +84,11 @@ jobs:
|
||||
- os: osx
|
||||
name: "MacOS 10.15"
|
||||
language: generic
|
||||
osx_image: xcode11.4
|
||||
osx_image: xcode11.7
|
||||
- os: osx
|
||||
name: "MacOS 10.15 Universal Testing"
|
||||
language: generic
|
||||
osx_image: xcode12u
|
||||
- os: windows
|
||||
name: "Windows 64-bit"
|
||||
language: shell
|
||||
@@ -128,10 +129,11 @@ script:
|
||||
- export jid="$(cut -d'.' -f2 <<<"$TRAVIS_JOB_NUMBER")"
|
||||
- if [ "$TRAVIS_EVENT_TYPE" != "pull_request" ]; then export e2e=true; fi
|
||||
- if [ "$e2e" = true ]; then export gam_user=gam-travis-$jid@pdl.jaylee.us; fi
|
||||
- if [ "$e2e" = true ]; then openssl aes-256-cbc -K $encrypted_ab10ec38326e_key -iv $encrypted_ab10ec38326e_iv -in travis/oauth2service.json.enc -out $gampath/oauth2service.json -d; fi
|
||||
- if [ "$e2e" = true ]; then cat travis/cfg_template.json | $python travis/svars-write.py &> /dev/null; fi
|
||||
- if [ "$e2e" = true ]; then $gam info domain; fi
|
||||
- if [ "$e2e" = true ]; then openssl aes-256-cbc -K $encrypted_6294a53f809d_key -iv $encrypted_6294a53f809d_iv -in travis/creds.tar.enc -out travis/creds.tar -d; fi
|
||||
- if [ "$e2e" = true ]; then tar xvf travis/creds.tar -C $gampath; fi
|
||||
- if [ "$e2e" = true ]; then export OAUTHFILE=oauth2.txt-gam-travis-$jid; fi
|
||||
- if [ "$e2e" = true ]; then $gam oauth info; fi
|
||||
- if [ "$e2e" = true ]; then $gam info domain; fi
|
||||
- if [ "$e2e" = true ]; then $gam oauth refresh; fi
|
||||
- if [ "$e2e" = true ]; then $gam info user; fi
|
||||
- if [ "$e2e" = true ]; then export tstamp=$(date +%s%3N);
|
||||
@@ -164,7 +166,8 @@ script:
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show imap; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user $newuser delegate to ~email; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show delegates; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser label "✔ unicode checkmark ✔"; fi
|
||||
- if [ "$e2d" = true ]; then export biohazard=$(echo -e '\xe2\x98\xa3'); fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser label "$biohazard unicode biohazard $biohazard"; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show labels; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show labels > labels.txt; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user importemail subject "Travis import $newbase" message "This is a test import" labels IMPORTANT,UNREAD,INBOX,STARRED; fi
|
||||
@@ -211,6 +214,10 @@ script:
|
||||
- if [ "$e2e" = true ]; then $gam delete user $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam print users query "travis.jid=$jid" | $gam csv - gam delete user ~primaryEmail; fi
|
||||
- if [ "$e2e" = true ]; then $gam print mobile; fi
|
||||
- if [ "$e2e" = true ]; then $gam print devices; fi
|
||||
- if [ "$e2e" = true ]; then export sn="$jid$jid$jid$jid$jid-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')"; fi
|
||||
- if [ "$e2e" = true ]; then $gam create device serialnumber $sn devicetype android; fi
|
||||
- if [ "$e2e" = true ]; then $gam print devices filter "serial:$jid$jid$jid$jid$jid-" | $gam csv - gam delete device id ~name; fi
|
||||
- if [ "$e2e" = true ]; then $gam print cros allfields nolists; fi
|
||||
- if [ "$e2e" = true ]; then $gam report usageparameters customer; fi
|
||||
- if [ "$e2e" = true ]; then $gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins; fi
|
||||
|
||||
@@ -780,7 +780,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
<UserBasicAttribute> ::=
|
||||
(agreed2terms|agreedtoterms <Boolean>)|
|
||||
(changepassword|changepasswordatnextlogin <Boolean>)|
|
||||
(crypt|sha|sha1|sha-1|md5|nohash)|
|
||||
(base64-md5|base64-sha1|crypt|sha|sha1|sha-1|md5|nohash)|
|
||||
(customerid <String>)|
|
||||
(email|primaryemail|username <EmailAddress>)|
|
||||
(firstname|givenname <String>)|
|
||||
@@ -822,6 +822,7 @@ gam help
|
||||
|
||||
gam batch <FileName>|- [charset <Charset>]
|
||||
gam csv <FileName>|- [charset <Charset>] gam <GAM argument list>
|
||||
gam csvtest <FileName>|- [charset <Charset>] gam <GAM argument list>
|
||||
|
||||
You can make substitutions in <GAMArgumentList> with values from the CSV file.
|
||||
An argument containing exactly ~xxx is replaced by the value of field xxx from the CSV file
|
||||
@@ -942,6 +943,9 @@ gam report <ActivityApplicationName> [todrive]
|
||||
gam create admin <UserItem> <RoleItem> customer|(org_unit <OrgUnitItem>)
|
||||
gam delete admin <RoleAssignmentId>
|
||||
gam print admins [todrive] [user <UserItem>] [role <RoleItem>]
|
||||
gam create adminrole <String> privileges all|all_ou|<PrivilegesList> [description <String>]
|
||||
gam update adminrole <RoleItem> [name <String>] [privileges all|all_ou|<PrivilegesList>] [description <String>]
|
||||
gam delete adminrole <RoleItem>
|
||||
gam print adminroles|roles [todrive]
|
||||
|
||||
gam create domain <DomainName>
|
||||
@@ -1067,7 +1071,15 @@ gam calendar <CalendarItem> printevents <EventSelectProperty>* <EventDisplayProp
|
||||
|
||||
gam calendar <CalendarItem> modify <CalendarSettings>+
|
||||
|
||||
gam update cros <CrOSEntity> (<CrOSAttributes>+)|(action deprovision_same_model_replace|deprovision_different_model_replace|deprovision_retiring_device|disable|reenable [acknowledge_device_touch_requirement])
|
||||
<CrOSAction> ::=
|
||||
deprovision_same_model_replace|
|
||||
deprovision_different_model_replace|
|
||||
deprovision_retiring_device|
|
||||
deprovision_upgrade_transfer|
|
||||
disable|
|
||||
reenable
|
||||
|
||||
gam update cros <CrOSEntity> (<CrOSAttributes>+)|(action <CrOSAction> [acknowledge_device_touch_requirement])
|
||||
gam info cros <CrOSEntity> [nolists] [listlimit <Number>] [start <Date>] [end <Date>]
|
||||
[basic|full|allfields] <CrOSFieldName>* [fields <CrOSFieldNameList>] [downloadfile latest|<Time>] [targetfolder <FilePath>]
|
||||
|
||||
@@ -1441,3 +1453,7 @@ gam <UserTypeEntity> vacation <FalseValues>
|
||||
gam <UserTypeEntity> vacation <TrueValues> subject <String> (message <String>)|(file <FileName> [charset <Charset>]) (replace <Tag> <String>)* [html]
|
||||
[contactsonly] [domainonly] [startdate <Date>] [enddate <Date>]
|
||||
gam <UserTypeEntity> show vacation [format]
|
||||
|
||||
gam <UserTypeEntity> signout
|
||||
gam <UserTypeEntity> turnoff2sv
|
||||
|
||||
|
||||
@@ -1,486 +0,0 @@
|
||||
{
|
||||
"kind": "discovery#restDescription",
|
||||
"discoveryVersion": "v1",
|
||||
"id": "cloudprint:v2",
|
||||
"name": "cloudprint",
|
||||
"version": "v2",
|
||||
"revision": "20150605",
|
||||
"title": "Cloud Print API",
|
||||
"description": "Lets you access Cloud Print Printers",
|
||||
"ownerDomain": "google.com",
|
||||
"ownerName": "Google",
|
||||
"icons": {
|
||||
"x16": "http://www.google.com/images/icons/product/search-16.gif",
|
||||
"x32": "http://www.google.com/images/icons/product/search-32.gif"
|
||||
},
|
||||
"documentationLink": "https://developers.google.com/cloud-print",
|
||||
"protocol": "rest",
|
||||
"baseUrl": "https://www.google.com/",
|
||||
"basePath": "/cloudprint/",
|
||||
"rootUrl": "https://www.google.com/",
|
||||
"servicePath": "/cloudprint/",
|
||||
"parameters": {
|
||||
"prettyPrint": {
|
||||
"type": "boolean",
|
||||
"description": "Returns response with indentations and line breaks.",
|
||||
"default": "true",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"oauth2": {
|
||||
"scopes": {
|
||||
"https://www.googleapis.com/auth/cloudprint": {
|
||||
"description": "Manage Cloud Print"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"Job": {
|
||||
"id": "Job",
|
||||
"type": "object",
|
||||
"description": "Job Object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Job Title"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Unique ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Jobs": {
|
||||
"id": "Jobs",
|
||||
"type": "object",
|
||||
"description": "List of Jobs.",
|
||||
"properties": {
|
||||
"jobs": {
|
||||
"type": "array",
|
||||
"description": "List of job objects.",
|
||||
"items": {
|
||||
"$ref": "Job"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Printer": {
|
||||
"id": "Printer",
|
||||
"type": "object",
|
||||
"description": "Printer Object",
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"type": "string",
|
||||
"description": "Display Name"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Unique ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Printers": {
|
||||
"id": "Printers",
|
||||
"type": "object",
|
||||
"description": "List of Printers.",
|
||||
"properties": {
|
||||
"printers": {
|
||||
"type": "array",
|
||||
"description": "List of printer objects.",
|
||||
"items": {
|
||||
"$ref": "Printer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"jobs": {
|
||||
"methods": {
|
||||
"delete": {
|
||||
"id": "cloudprint.jobs.delete",
|
||||
"path": "deletejob",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"jobid": {
|
||||
"type": "string",
|
||||
"location": "query",
|
||||
"required": "true"
|
||||
}
|
||||
}
|
||||
},
|
||||
"fetch": {
|
||||
"id": "cloudprint.jobs.fetch",
|
||||
"path": "fetch",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Jobs"
|
||||
}
|
||||
},
|
||||
"getticket": {
|
||||
"id": "cloudprint.jobs.getticket",
|
||||
"path": "ticket",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"jobid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"use_cjt": {
|
||||
"type": "boolean",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
}
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"id": "cloudprint.jobs.list",
|
||||
"path": "jobs",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"q": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"offset": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"limit": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"sortorder": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Jobs"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"id": "cloudprint.jobs.update",
|
||||
"path": "control",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"jobid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"semantic_state_diff": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Jobs"
|
||||
}
|
||||
},
|
||||
"resubmit": {
|
||||
"id": "cloudprint.jobs.resubmit",
|
||||
"path": "resubmit",
|
||||
"httpMethod": "POST",
|
||||
"description": "resubmit a job to new printer.",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"jobid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"ticket": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Job"
|
||||
}
|
||||
},
|
||||
"submit": {
|
||||
"id": "cloudprint.jobs.submit",
|
||||
"path": "submit",
|
||||
"httpMethod": "POST",
|
||||
"description": "Send a print job to cloud print.",
|
||||
"request": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"ticket": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"contentType": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Job"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"printers": {
|
||||
"methods": {
|
||||
"get": {
|
||||
"id": "cloudprint.printers.get",
|
||||
"path": "printer",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"extra_fields": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Printer"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"id": "cloudprint.printers.list",
|
||||
"path": "search",
|
||||
"httpMethod": "GET",
|
||||
"description": "List all printers",
|
||||
"parameters": {
|
||||
"q": {
|
||||
"type": "string",
|
||||
"description": "Query list of printers",
|
||||
"location": "query"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "limit results to printers of type",
|
||||
"location": "query"
|
||||
},
|
||||
"connection_status": {
|
||||
"type": "string",
|
||||
"description": "limit results to printers with this status",
|
||||
"location": "query"
|
||||
},
|
||||
"extra_fields": {
|
||||
"type": "string",
|
||||
"description": "include extra fields",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Printers"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"id": "cloudprint.printers.share",
|
||||
"path": "share",
|
||||
"httpMethod": "GET",
|
||||
"description": "Share printer with user, group or domain",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"skip_notification": {
|
||||
"type": "boolean",
|
||||
"location": "query"
|
||||
},
|
||||
"public": {
|
||||
"type": "boolean",
|
||||
"location": "query"
|
||||
}
|
||||
}
|
||||
},
|
||||
"unshare": {
|
||||
"id": "cloudprint.printers.unshare",
|
||||
"path": "unshare",
|
||||
"httpMethod": "GET",
|
||||
"description": "unshare printer with user, group or domain",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"public": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"id": "cloudprint.printers.delete",
|
||||
"path": "delete",
|
||||
"httpMethod": "GET",
|
||||
"description": "delete a printer",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
}
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"id": "cloudprint.printers.update",
|
||||
"path": "update",
|
||||
"httpMethod": "GET",
|
||||
"description": "update a printer",
|
||||
"parameters": {
|
||||
"isTosAccepted": {
|
||||
"type": "boolean",
|
||||
"location": "query"
|
||||
},
|
||||
"gcpVersion": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"setupUrl": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"supportUrl": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"firmware": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"currentQuota": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"public": {
|
||||
"type": "boolean",
|
||||
"location": "query"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"proxy": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"manufacturer": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"defaultDisplayName": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"displayName": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"updateUrl": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"ownerId": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"model": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"quotaEnabled": {
|
||||
"type": "boolean",
|
||||
"location": "query"
|
||||
},
|
||||
"dailyQuota": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ gamversion="latest"
|
||||
adminuser=""
|
||||
regularuser=""
|
||||
gam_glibc_vers="2.27 2.23"
|
||||
gam_macos_vers="10.14.6 10.13.6"
|
||||
gam_macos_vers="10.15.4 10.14.6 10.13.6"
|
||||
|
||||
while getopts "hd:a:o:b:lp:u:r:v:" OPTION
|
||||
do
|
||||
@@ -218,6 +218,10 @@ fi
|
||||
# Temp dir for archive
|
||||
#temp_archive_dir=$(mktemp -d)
|
||||
temp_archive_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir')
|
||||
|
||||
# Clean up after ourselves even if we are killed with CTRL-C
|
||||
trap "rm -rf $temp_archive_dir" EXIT
|
||||
|
||||
echo_yellow "Downloading file $name from $browser_download_url to $temp_archive_dir."
|
||||
# Save archive to temp w/o losing our path
|
||||
(cd $temp_archive_dir && curl -O -L $browser_download_url)
|
||||
@@ -372,6 +376,3 @@ echo_green "GAM installation and setup complete!"
|
||||
if [ "$update_profile" = true ]; then
|
||||
echo_green "Please restart your terminal shell or to get started right away run:\n\n$alias_line"
|
||||
fi
|
||||
|
||||
# Clean up after ourselves even if we are killed with CTRL-C
|
||||
trap "rm -rf $temp_archive_dir" EXIT
|
||||
|
||||
@@ -7,11 +7,9 @@ from PyInstaller.utils.hooks import copy_metadata
|
||||
|
||||
sys.modules['FixTk'] = None
|
||||
|
||||
extra_files = [('cloudprint-v2.json', 'cloudprint-v2.json')]
|
||||
|
||||
# dynamically determine where httplib2/cacerts.txt lives
|
||||
proot = os.path.dirname(importlib.import_module('httplib2').__file__)
|
||||
extra_files += [(os.path.join(proot, 'cacerts.txt'), 'httplib2')]
|
||||
extra_files = [(os.path.join(proot, 'cacerts.txt'), 'httplib2')]
|
||||
|
||||
extra_files += copy_metadata('google-api-python-client')
|
||||
|
||||
|
||||
3403
src/gam/__init__.py
3403
src/gam/__init__.py
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,46 @@
|
||||
"""Authentication/Credentials general purpose and convenience methods."""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
from google.auth.jwt import Credentials as JWTCredentials
|
||||
|
||||
from gam.auth import oauth
|
||||
from gam.var import _FN_OAUTH2_TXT
|
||||
from gam.var import _FN_OAUTH2SERVICE_JSON
|
||||
from gam.var import GC_OAUTH2_TXT
|
||||
from gam.var import GC_OAUTH2SERVICE_JSON
|
||||
from gam.var import GC_ENABLE_DASA
|
||||
from gam.var import GC_Values
|
||||
|
||||
# TODO: Move logic that determines file name into this module. We should be able
|
||||
# to discover the file location without accessing a private member or waiting
|
||||
# for a global initialization.
|
||||
DEFAULT_OAUTH_STORAGE_FILE = _FN_OAUTH2_TXT
|
||||
|
||||
|
||||
def get_admin_credentials_filename():
|
||||
"""Gets the name of the file that stores the admin account credentials."""
|
||||
# If the environment globals are loaded, use the set global value. It may have
|
||||
# some custom name in it. Otherwise, just use the default name.
|
||||
if GC_Values[GC_OAUTH2_TXT]:
|
||||
return GC_Values[GC_OAUTH2_TXT]
|
||||
return DEFAULT_OAUTH_STORAGE_FILE
|
||||
if GC_Values[GC_ENABLE_DASA]:
|
||||
return GC_Values[GC_OAUTH2SERVICE_JSON] if GC_Values[GC_OAUTH2SERVICE_JSON] else _FN_OAUTH2SERVICE_JSON
|
||||
else:
|
||||
return GC_Values[GC_OAUTH2_TXT] if GC_Values[GC_OAUTH2_TXT] else _FN_OAUTH2_TXT
|
||||
|
||||
|
||||
def get_admin_credentials():
|
||||
def get_admin_credentials(api=None):
|
||||
"""Gets oauth.Credentials that are authenticated as the domain's admin user."""
|
||||
credential_file = get_admin_credentials_filename()
|
||||
return oauth.Credentials.from_credentials_file(credential_file)
|
||||
if not os.path.isfile(credential_file):
|
||||
raise oauth.InvalidCredentialsFileError
|
||||
with open(credential_file, 'r') as f:
|
||||
creds_data = json.load(f)
|
||||
# Validate that enable DASA matches content of authorization file
|
||||
if GC_Values[GC_ENABLE_DASA] and 'private_key' in creds_data:
|
||||
audience = f'https://{api}.googleapis.com/'
|
||||
return JWTCredentials.from_service_account_info(creds_data,
|
||||
audience=audience)
|
||||
elif not GC_Values[GC_ENABLE_DASA] and 'token' in creds_data:
|
||||
return oauth.Credentials.from_credentials_file(credential_file)
|
||||
else:
|
||||
raise oauth.InvalidCredentialsFileError
|
||||
|
||||
@@ -220,7 +220,7 @@ def write_csv_file(csvRows, titles, list_type, todrive):
|
||||
except IOError as e:
|
||||
controlflow.system_error_exit(6, e)
|
||||
if todrive:
|
||||
admin_email = gam._getValueFromOAuth('email')
|
||||
admin_email = gam._get_admin_email()
|
||||
_, drive = gam.buildDrive3GAPIObject(admin_email)
|
||||
if not drive:
|
||||
print(f'''\nGAM is not authorized to create Drive files. Please run:
|
||||
|
||||
@@ -246,9 +246,9 @@ def get_all_pages(service,
|
||||
display a unique property of the first item in the current page.
|
||||
LAST_ITEM_MARKER : In conjunction with `message_attribute` arg, will
|
||||
display a unique property of the last item in the current page.
|
||||
message_attribute: String, the name of a signature field within a single
|
||||
returned item which identifies that unique item. This field is used with
|
||||
`page_message` to templatize a paging status message.
|
||||
message_attribute: String or list, the name of a signature field within a
|
||||
single returned item which identifies that unique item. This field is used
|
||||
with `page_message` to templatize a paging status message.
|
||||
soft_errors: Bool, If True, writes non-fatal errors to stderr.
|
||||
throw_reasons: A list of Google HTTP error reason strings indicating the
|
||||
errors generated by this request should be re-thrown. All other HTTP
|
||||
@@ -293,11 +293,17 @@ def get_all_pages(service,
|
||||
if message_attribute:
|
||||
first_item = page_items[0] if num_page_items > 0 else {}
|
||||
last_item = page_items[-1] if num_page_items > 1 else first_item
|
||||
show_message = show_message.replace(
|
||||
FIRST_ITEM_MARKER,
|
||||
str(first_item.get(message_attribute, '')))
|
||||
show_message = show_message.replace(
|
||||
LAST_ITEM_MARKER, str(last_item.get(message_attribute, '')))
|
||||
if type(message_attribute) is str:
|
||||
first_item = str(first_item.get(message_attribute, ''))
|
||||
last_item = str(last_item.get(message_attribute, ''))
|
||||
else:
|
||||
for attr in message_attribute:
|
||||
first_item = first_item.get(attr, {})
|
||||
last_item = last_item.get(attr, {})
|
||||
first_item = str(first_item)
|
||||
last_item = str(last_item)
|
||||
show_message = show_message.replace(FIRST_ITEM_MARKER, first_item)
|
||||
show_message = show_message.replace(LAST_ITEM_MARKER, last_item)
|
||||
sys.stderr.write('\r')
|
||||
sys.stderr.flush()
|
||||
sys.stderr.write(show_message)
|
||||
|
||||
@@ -37,7 +37,7 @@ def buildCalendarDataGAPIObject(calname):
|
||||
if not calname.endswith('.calendar.google.com'):
|
||||
cal = gam.buildGAPIServiceObject('calendar', calendarId, False)
|
||||
if cal is None:
|
||||
_, cal = buildCalendarGAPIObject(gam._getValueFromOAuth('email'))
|
||||
_, cal = buildCalendarGAPIObject(gam._get_admin_email())
|
||||
return (calendarId, cal)
|
||||
|
||||
|
||||
@@ -372,6 +372,7 @@ def getEventAttributes(i, calendarId, cal, body, action):
|
||||
# calendars are notified of changes
|
||||
sendUpdates = 'externalOnly'
|
||||
action = 'update' if body else 'add'
|
||||
timeZone = None
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg in ['notifyattendees', 'sendnotifications', 'sendupdates']:
|
||||
|
||||
9
src/gam/gapi/cloudidentity/__init__.py
Normal file
9
src/gam/gapi/cloudidentity/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import gam
|
||||
|
||||
|
||||
def build(api='cloudidentity'):
|
||||
return gam.buildGAPIObject(api)
|
||||
|
||||
def build_dwd(api='cloudidentity'):
|
||||
admin = gam._get_admin_email()
|
||||
return gam.buildGAPIServiceObject(api, admin, True)
|
||||
297
src/gam/gapi/cloudidentity/devices.py
Normal file
297
src/gam/gapi/cloudidentity/devices.py
Normal file
@@ -0,0 +1,297 @@
|
||||
import csv
|
||||
import sys
|
||||
|
||||
import googleapiclient
|
||||
|
||||
import gam
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import fileutils
|
||||
from gam import gapi
|
||||
from gam import utils
|
||||
from gam.gapi import errors as gapi_errors
|
||||
from gam.gapi import cloudidentity as gapi_cloudidentity
|
||||
from gam.gapi.directory import customer as gapi_directory_customer
|
||||
from gam.gapi.directory import groups as gapi_directory_groups
|
||||
|
||||
|
||||
def create():
|
||||
ci = gapi_cloudidentity.build_dwd()
|
||||
customer = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
|
||||
device_types = gapi.get_enum_values_minus_unspecified(
|
||||
ci._rootDesc['schemas']['GoogleAppsCloudidentityDevicesV1Device']['properties']['deviceType']['enum'])
|
||||
body = {}
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'serialnumber':
|
||||
body['serialNumber'] = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'devicetype':
|
||||
body['deviceType'] = sys.argv[i+1].upper()
|
||||
if body['deviceType'] not in device_types:
|
||||
controlflow.expected_argument_exit('device_type',
|
||||
', '.join(device_types),
|
||||
sys.argv[i+1])
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam create device')
|
||||
if not body.get('serialNumber') or not body.get('deviceType'):
|
||||
controlflow.system_error_exit(
|
||||
3, 'serial_number and device_type are required arguments for "gam create device".')
|
||||
result = gapi.call(ci.devices(), 'create', customer=customer, body=body)
|
||||
print(f'Created device {result["response"]["name"]}')
|
||||
|
||||
def info():
|
||||
ci = gapi_cloudidentity.build_dwd()
|
||||
customer = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
|
||||
name = sys.argv[3]
|
||||
if not name.startswith('devices/'):
|
||||
name = f'devices/{name}'
|
||||
device = gapi.call(ci.devices(), 'get', name=name, customer=customer)
|
||||
device_users = gapi.get_all_pages(ci.devices().deviceUsers(), 'list',
|
||||
'deviceUsers', parent=name, customer=customer)
|
||||
display.print_json(device)
|
||||
print('Device Users:')
|
||||
display.print_json(device_users)
|
||||
|
||||
def _generic_action(action, device_user=False):
|
||||
ci = gapi_cloudidentity.build_dwd()
|
||||
customer = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
|
||||
|
||||
# bah, inconsistencies in API
|
||||
if action == 'delete':
|
||||
kwargs = {'customer': customer}
|
||||
else:
|
||||
kwargs = {'body': {'customer': customer}}
|
||||
|
||||
if device_user:
|
||||
endpoint = ci.devices().deviceUsers()
|
||||
else:
|
||||
endpoint = ci.devices()
|
||||
name = None
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
# The API calls it "name" but GAM will expose as "id" to avoid admin confusion.
|
||||
if myarg == 'id':
|
||||
name = sys.argv[i+1]
|
||||
if not name.startswith('devices/'):
|
||||
name = f'devices/{name}'
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], f'gam {action} device')
|
||||
if not name:
|
||||
controlflow.system_error_exit(3, f'id is a required argument for "gam {action} device".')
|
||||
op = gapi.call(endpoint, action, name=name, **kwargs)
|
||||
print(op)
|
||||
|
||||
def delete():
|
||||
_generic_action('delete')
|
||||
|
||||
def cancel_wipe():
|
||||
_generic_action('cancelWipe')
|
||||
|
||||
def wipe():
|
||||
_generic_action('wipe')
|
||||
|
||||
def approve_user():
|
||||
_generic_action('approve', True)
|
||||
|
||||
def block_user():
|
||||
_generic_action('block', True)
|
||||
|
||||
def cancel_wipe_user():
|
||||
_generic_action('cancelWipe', True)
|
||||
|
||||
def delete_user():
|
||||
_generic_action('delete', True)
|
||||
|
||||
def wipe_user():
|
||||
_generic_action('wipe', True)
|
||||
|
||||
def print_():
|
||||
ci = gapi_cloudidentity.build_dwd()
|
||||
customer = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
|
||||
parent = 'devices/-'
|
||||
filter = None
|
||||
get_device_users = True
|
||||
get_device_views = ['COMPANY_INVENTORY', 'USER_ASSIGNED_DEVICES']
|
||||
titles = []
|
||||
csvRows = []
|
||||
todrive = False
|
||||
sortHeaders = False
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg in ['filter', 'query']:
|
||||
filter = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'nocompanydevices':
|
||||
get_device_views.remove('COMPANY_INVENTORY')
|
||||
i += 1
|
||||
elif myarg == 'nopersonaldevices':
|
||||
get_device_views.remove('USER_ASSIGNED_DEVICES')
|
||||
i += 1
|
||||
elif myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg == 'sortheaders':
|
||||
sortHeaders = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam print devices')
|
||||
view_name_map = {
|
||||
'COMPANY_INVENTORY': 'Company Devices',
|
||||
'USER_ASSIGNED_DEVICES': 'Personal Devices',
|
||||
}
|
||||
devices = []
|
||||
for view in get_device_views:
|
||||
view_name = view_name_map.get(view, 'Devices')
|
||||
page_message = gapi.got_total_items_msg(view_name, '...\n')
|
||||
devices += gapi.get_all_pages(ci.devices(), 'list', 'devices',
|
||||
customer=customer, page_message=page_message,
|
||||
pageSize=100, filter=filter, view=view)
|
||||
if get_device_users:
|
||||
page_message = gapi.got_total_items_msg('Device Users', '...\n')
|
||||
device_users = gapi.get_all_pages(ci.devices().deviceUsers(), 'list',
|
||||
'deviceUsers', customer=customer, parent=parent,
|
||||
page_message=page_message, pageSize=20, filter=filter)
|
||||
for device_user in device_users:
|
||||
for device in devices:
|
||||
if device_user.get('name').startswith(device.get('name')):
|
||||
if 'users' not in device:
|
||||
device['users'] = []
|
||||
device['users'].append(device_user)
|
||||
break
|
||||
for device in devices:
|
||||
device = utils.flatten_json(device)
|
||||
for a_key in device:
|
||||
if a_key not in titles:
|
||||
titles.append(a_key)
|
||||
csvRows.append(device)
|
||||
if sortHeaders:
|
||||
display.sort_csv_titles(['name',], titles)
|
||||
display.write_csv_file(csvRows, titles, 'Devices', todrive)
|
||||
|
||||
|
||||
def sync():
|
||||
ci = gapi_cloudidentity.build_dwd()
|
||||
device_types = gapi.get_enum_values_minus_unspecified(
|
||||
ci._rootDesc['schemas']['GoogleAppsCloudidentityDevicesV1Device']['properties']['deviceType']['enum'])
|
||||
customer = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
|
||||
filter = None
|
||||
csv_file = None
|
||||
serialnumber_column = 'serialNumber'
|
||||
devicetype_column = 'deviceType'
|
||||
static_devicetype = None
|
||||
assetid_column = None
|
||||
unassigned_missing_action = 'delete'
|
||||
assigned_missing_action = 'donothing'
|
||||
missing_actions = ['delete', 'wipe', 'donothing']
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg in ['filter', 'query']:
|
||||
filter = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'csvfile':
|
||||
csv_file = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'serialnumbercolumn':
|
||||
serialnumber_column = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'devicetypecolumn':
|
||||
devicetype_column = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'staticdevicetype':
|
||||
static_devicetype = sys.argv[i+1].upper()
|
||||
if static_devicetype not in device_types:
|
||||
controlflow.expected_argument_exit('device_type',
|
||||
', '.join(device_types),
|
||||
sys.argv[i+1])
|
||||
i += 2
|
||||
elif myarg == 'assetidcolumn':
|
||||
assetid_column = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'unassigned_missing_action':
|
||||
unassigned_missing_action = sys.argv[i+1].lower().replace('_', '')
|
||||
if unassigned_missing_action not in missing_actions:
|
||||
controlflow.expected_argument_exit('unassigned_missing_action',
|
||||
', '.join(missing_actions),
|
||||
sys.argv[i+1])
|
||||
i += 2
|
||||
elif myarg == 'assigned_missing_action':
|
||||
assigned_missing_action = sys.argv[i+1].lower().replace('_', '')
|
||||
if assigned_missing_action not in missing_actions:
|
||||
controlflow.expected_argument_exit('assigned_missing_action',
|
||||
', '.join(missing_actions),
|
||||
sys.argv[i+1])
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam sync devices')
|
||||
f = fileutils.open_file(csv_file)
|
||||
input_file = csv.DictReader(f, restval='')
|
||||
if serialnumber_column not in input_file.fieldnames:
|
||||
controlflow.csv_field_error_exit(serialnumber_column, input_file.fieldnames)
|
||||
if not static_devicetype and devicetype_column not in input_file.fieldnames:
|
||||
controlflow.csv_field_error_exit(devicetype_column, input_file.fieldnames)
|
||||
if assetid_column and assetid_column not in input_file.fieldnames:
|
||||
controlflow.csv_field_error_exit(assetid_column, input_file.fieldnames)
|
||||
local_devices = []
|
||||
for row in input_file:
|
||||
# upper() is very important to comparison since Google
|
||||
# always return uppercase serials
|
||||
serialnumber = row[serialnumber_column].strip().upper()
|
||||
local_device = {'serialNumber': serialnumber}
|
||||
if static_devicetype:
|
||||
local_device['deviceType'] = static_devicetype
|
||||
else:
|
||||
local_device['deviceType'] = row[devicetype_column].strip()
|
||||
if assetid_column:
|
||||
local_device['assetTag'] = row[assetid_column].strip()
|
||||
local_devices.append(local_device)
|
||||
fileutils.close_file(f)
|
||||
page_message = gapi.got_total_items_msg('Company Devices', '...\n')
|
||||
device_fields = ['serialNumber', 'deviceType', 'lastSyncTime', 'name']
|
||||
if assetid_column:
|
||||
device_fields.append('assetTag')
|
||||
fields = f'nextPageToken,devices({",".join(device_fields)})'
|
||||
remote_devices = gapi.get_all_pages(ci.devices(), 'list', 'devices',
|
||||
customer=customer, page_message=page_message,
|
||||
pageSize=100, filter=filter, view='COMPANY_INVENTORY', fields=fields)
|
||||
remote_device_map = {}
|
||||
for remote_device in remote_devices:
|
||||
sn = remote_device['serialNumber']
|
||||
last_sync = remote_device.pop('lastSyncTime')
|
||||
name = remote_device.pop('name')
|
||||
remote_device_map[sn] = {'name': name}
|
||||
if last_sync == '1970-01-01T00:00:00Z':
|
||||
remote_device_map[sn]['unassigned'] = True
|
||||
devices_to_add = [device for device in local_devices if device not in remote_devices]
|
||||
missing_devices = [device for device in remote_devices if device not in local_devices]
|
||||
print(f'Need to add {len(devices_to_add)} and remove {len(missing_devices)} devices...')
|
||||
for add_device in devices_to_add:
|
||||
print(f'Creating {add_device["serialNumber"]}')
|
||||
try:
|
||||
result = gapi.call(ci.devices(), 'create', customer=customer,
|
||||
throw_reasons=[gapi_errors.ErrorReason.FOUR_O_NINE], body=add_device)
|
||||
print(f' created {result["response"]["deviceType"]} device {result["response"]["name"]} with serial {result["response"]["serialNumber"]}')
|
||||
except googleapiclient.errors.HttpError:
|
||||
print(f' {add_device["serialNumber"]} already exists')
|
||||
for missing_device in missing_devices:
|
||||
sn = missing_device['serialNumber']
|
||||
name = remote_device_map[sn]['name']
|
||||
unassigned = remote_device_map[sn].get('unassigned')
|
||||
action = unassigned_missing_action if unassigned else assigned_missing_action
|
||||
if action == 'donothing':
|
||||
pass
|
||||
else:
|
||||
if action == 'delete':
|
||||
kwargs = {'customer': customer}
|
||||
else:
|
||||
kwargs = {'body': {'customer': customer}}
|
||||
gapi.call(ci.devices(), unassigned_missing_action,
|
||||
name=name, **kwargs)
|
||||
print(f'{action}d {sn}')
|
||||
752
src/gam/gapi/cloudidentity/groups.py
Normal file
752
src/gam/gapi/cloudidentity/groups.py
Normal file
@@ -0,0 +1,752 @@
|
||||
import csv
|
||||
|
||||
import gam
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
from gam import utils
|
||||
from gam.gapi import errors as gapi_errors
|
||||
from gam.gapi import cloudidentity as gapi_cloudidentity
|
||||
from gam.gapi.directory import customer as gapi_directory_customer
|
||||
from gam.gapi.directory import groups as gapi_directory_groups
|
||||
|
||||
|
||||
def create():
|
||||
ci = gapi_cloudidentity.build('cloudidentity_beta')
|
||||
initialGroupConfig = 'EMPTY'
|
||||
gapi_directory_customer.setTrueCustomerId()
|
||||
parent = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
|
||||
body = {
|
||||
'groupKey': {
|
||||
'id': gam.normalizeEmailAddressOrUID(sys.argv[3], noUid=True)
|
||||
},
|
||||
'parent': parent,
|
||||
'labels': {
|
||||
'cloudidentity.googleapis.com/groups.discussion_forum': ''
|
||||
},
|
||||
}
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'name':
|
||||
body['displayName'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'description':
|
||||
body['description'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg in ['alias', 'aliases']:
|
||||
# As of 2020/06/25 this doesn't work (yet?)
|
||||
aliases = sys.argv[i + 1].split(' ')
|
||||
body['additionalGroupKeys'] = []
|
||||
for alias in aliases:
|
||||
body['additionalGroupKeys'].append({'id': alias})
|
||||
i += 2
|
||||
elif myarg in ['dynamic']:
|
||||
# As of 2020/06/25 this doesn't work (yet?)
|
||||
body['dynamicGroupMetadata'] = {
|
||||
'queries': [{
|
||||
'query': sys.argv[i + 1],
|
||||
'resourceType': 'USER'
|
||||
}]
|
||||
}
|
||||
i += 2
|
||||
elif myarg in ['makeowner']:
|
||||
initialGroupConfig = 'WITH_INITIAL_OWNER'
|
||||
i += 1
|
||||
else:
|
||||
print('should not get here')
|
||||
sys.exit(5)
|
||||
print(f'Creating group {body["groupKey"]["id"]}')
|
||||
gapi.call(ci.groups(),
|
||||
'create',
|
||||
initialGroupConfig=initialGroupConfig,
|
||||
body=body)
|
||||
|
||||
|
||||
def delete():
|
||||
ci = gapi_cloudidentity.build('cloudidentity_beta')
|
||||
group = sys.argv[3]
|
||||
name = group_email_to_id(ci, group)
|
||||
print(f'Deleting group {group}')
|
||||
gapi.call(ci.groups(), 'delete', name=name)
|
||||
|
||||
|
||||
def info():
|
||||
ci = gapi_cloudidentity.build('cloudidentity_beta')
|
||||
group = gam.normalizeEmailAddressOrUID(sys.argv[3])
|
||||
getUsers = True
|
||||
showJoinDate = True
|
||||
showUpdateDate = False
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'nousers':
|
||||
getUsers = False
|
||||
i += 1
|
||||
elif myarg == 'nojoindate':
|
||||
showJoinDate = False
|
||||
i += 1
|
||||
elif myarg == 'showupdatedate':
|
||||
showUpdateDate = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(myarg, 'gam info cigroup')
|
||||
name = group_email_to_id(ci, group)
|
||||
basic_info = gapi.call(ci.groups(), 'get', name=name)
|
||||
display.print_json(basic_info)
|
||||
if getUsers:
|
||||
if not showJoinDate and not showUpdateDate:
|
||||
view = 'BASIC'
|
||||
pageSize = 1000
|
||||
else:
|
||||
view = 'FULL'
|
||||
pageSize = 500
|
||||
members = gapi.get_all_pages(ci.groups().memberships(),
|
||||
'list',
|
||||
'memberships',
|
||||
parent=name,
|
||||
fields='*',
|
||||
pageSize=pageSize,
|
||||
view=view)
|
||||
print('Members:')
|
||||
for member in members:
|
||||
role = get_single_role(member.get('roles', [])).lower()
|
||||
email = member.get('memberKey', {}).get('id')
|
||||
jc_string = ''
|
||||
if showJoinDate:
|
||||
joined = member.get('createTime', 'Unknown')
|
||||
jc_string += f' joined {joined}'
|
||||
if showUpdateDate:
|
||||
updated = member.get('updateTime', 'Unknown')
|
||||
jc_string += f' updated {updated}'
|
||||
print(
|
||||
f'{role}: {email}{jc_string}'
|
||||
# f' {member.get("role", ROLE_MEMBER).lower()}: {member.get("email", member["id"])} ({member["type"].lower()})'
|
||||
)
|
||||
print(f'Total {len(members)} users in group')
|
||||
|
||||
|
||||
def info_member():
|
||||
ci = gapi_cloudidentity.build('cloudidentity_beta')
|
||||
member = gam.normalizeEmailAddressOrUID(sys.argv[3])
|
||||
group = gam.normalizeEmailAddressOrUID(sys.argv[4])
|
||||
group_name = gapi.call(ci.groups(),
|
||||
'lookup',
|
||||
groupKey_id=group,
|
||||
fields='name').get('name')
|
||||
member_name = gapi.call(ci.groups().memberships(),
|
||||
'lookup',
|
||||
parent=group_name,
|
||||
memberKey_id=member,
|
||||
fields='name').get('name')
|
||||
member_details = gapi.call(ci.groups().memberships(),
|
||||
'get',
|
||||
name=member_name)
|
||||
display.print_json(member_details)
|
||||
|
||||
|
||||
UPDATE_GROUP_SUBCMDS = ['add', 'clear', 'delete', 'remove', 'sync', 'update']
|
||||
GROUP_ROLES_MAP = {
|
||||
'owner': ROLE_OWNER,
|
||||
'owners': ROLE_OWNER,
|
||||
'manager': ROLE_MANAGER,
|
||||
'managers': ROLE_MANAGER,
|
||||
'member': ROLE_MEMBER,
|
||||
'members': ROLE_MEMBER,
|
||||
}
|
||||
|
||||
|
||||
def print_():
|
||||
ci = gapi_cloudidentity.build('cloudidentity_beta')
|
||||
i = 3
|
||||
members = membersCountOnly = managers = managersCountOnly = owners = ownersCountOnly = False
|
||||
gapi_directory_customer.setTrueCustomerId()
|
||||
parent = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
|
||||
memberDelimiter = '\n'
|
||||
todrive = False
|
||||
titles = []
|
||||
csvRows = []
|
||||
roles = []
|
||||
sortHeaders = False
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg == 'delimiter':
|
||||
memberDelimiter = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'sortheaders':
|
||||
sortHeaders = True
|
||||
i += 1
|
||||
elif myarg in ['members', 'memberscount']:
|
||||
roles.append(ROLE_MEMBER)
|
||||
members = True
|
||||
if myarg == 'memberscount':
|
||||
membersCountOnly = True
|
||||
i += 1
|
||||
elif myarg in ['owners', 'ownerscount']:
|
||||
roles.append(ROLE_OWNER)
|
||||
owners = True
|
||||
if myarg == 'ownerscount':
|
||||
ownersCountOnly = True
|
||||
i += 1
|
||||
elif myarg in ['managers', 'managerscount']:
|
||||
roles.append(ROLE_MANAGER)
|
||||
managers = True
|
||||
if myarg == 'managerscount':
|
||||
managersCountOnly = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam print cigroups')
|
||||
if roles:
|
||||
if members:
|
||||
display.add_titles_to_csv_file([
|
||||
'MembersCount',
|
||||
], titles)
|
||||
if not membersCountOnly:
|
||||
display.add_titles_to_csv_file([
|
||||
'Members',
|
||||
], titles)
|
||||
if managers:
|
||||
display.add_titles_to_csv_file([
|
||||
'ManagersCount',
|
||||
], titles)
|
||||
if not managersCountOnly:
|
||||
display.add_titles_to_csv_file([
|
||||
'Managers',
|
||||
], titles)
|
||||
if owners:
|
||||
display.add_titles_to_csv_file([
|
||||
'OwnersCount',
|
||||
], titles)
|
||||
if not ownersCountOnly:
|
||||
display.add_titles_to_csv_file([
|
||||
'Owners',
|
||||
], titles)
|
||||
gam.printGettingAllItems('Groups', None)
|
||||
page_message = gapi.got_total_items_first_last_msg('Groups')
|
||||
entityList = gapi.get_all_pages(ci.groups(),
|
||||
'list',
|
||||
'groups',
|
||||
page_message=page_message,
|
||||
message_attribute=['groupKey', 'id'],
|
||||
parent=parent,
|
||||
view='FULL',
|
||||
pageSize=500)
|
||||
i = 0
|
||||
count = len(entityList)
|
||||
for groupEntity in entityList:
|
||||
i += 1
|
||||
groupEmail = groupEntity['groupKey']['id']
|
||||
group = utils.flatten_json(groupEntity)
|
||||
for a_key in group:
|
||||
if a_key not in titles:
|
||||
titles.append(a_key)
|
||||
groupKey_id = groupEntity['name']
|
||||
if roles:
|
||||
sys.stderr.write(
|
||||
f' Getting {roles} for {groupEmail}{gam.currentCountNL(i, count)}'
|
||||
)
|
||||
page_message = gapi.got_total_items_first_last_msg('Members')
|
||||
validRoles, _, _ = gam._getRoleVerification(
|
||||
'.'.join(roles), 'nextPageToken,members(email,id,role)')
|
||||
groupMembers = gapi.get_all_pages(ci.groups().memberships(),
|
||||
'list',
|
||||
'memberships',
|
||||
page_message=page_message,
|
||||
message_attribute=['memberKey', 'id'],
|
||||
soft_errors=True,
|
||||
parent=groupKey_id,
|
||||
view='BASIC')
|
||||
if members:
|
||||
membersList = []
|
||||
membersCount = 0
|
||||
if managers:
|
||||
managersList = []
|
||||
managersCount = 0
|
||||
if owners:
|
||||
ownersList = []
|
||||
ownersCount = 0
|
||||
for member in groupMembers:
|
||||
member_email = member['memberKey']['id']
|
||||
role = get_single_role(member.get('roles'))
|
||||
if not validRoles or role in validRoles:
|
||||
if role == ROLE_MEMBER:
|
||||
if members:
|
||||
membersCount += 1
|
||||
if not membersCountOnly:
|
||||
membersList.append(member_email)
|
||||
elif role == ROLE_MANAGER:
|
||||
if managers:
|
||||
managersCount += 1
|
||||
if not managersCountOnly:
|
||||
managersList.append(member_email)
|
||||
elif role == ROLE_OWNER:
|
||||
if owners:
|
||||
ownersCount += 1
|
||||
if not ownersCountOnly:
|
||||
ownersList.append(member_email)
|
||||
elif members:
|
||||
membersCount += 1
|
||||
if not membersCountOnly:
|
||||
membersList.append(member_email)
|
||||
if members:
|
||||
group['MembersCount'] = membersCount
|
||||
if not membersCountOnly:
|
||||
group['Members'] = memberDelimiter.join(membersList)
|
||||
if managers:
|
||||
group['ManagersCount'] = managersCount
|
||||
if not managersCountOnly:
|
||||
group['Managers'] = memberDelimiter.join(managersList)
|
||||
if owners:
|
||||
group['OwnersCount'] = ownersCount
|
||||
if not ownersCountOnly:
|
||||
group['Owners'] = memberDelimiter.join(ownersList)
|
||||
csvRows.append(group)
|
||||
if sortHeaders:
|
||||
display.sort_csv_titles([
|
||||
'Email',
|
||||
], titles)
|
||||
display.write_csv_file(csvRows, titles, 'Groups', todrive)
|
||||
|
||||
|
||||
def print_members():
|
||||
ci = gapi_cloudidentity.build('cloudidentity_beta')
|
||||
todrive = False
|
||||
gapi_directory_customer.setTrueCustomerId()
|
||||
parent = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
|
||||
roles = []
|
||||
titles = ['group']
|
||||
csvRows = []
|
||||
groups_to_get = []
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg in ['role', 'roles']:
|
||||
for role in sys.argv[i + 1].lower().replace(',', ' ').split():
|
||||
if role in GROUP_ROLES_MAP:
|
||||
roles.append(GROUP_ROLES_MAP[role])
|
||||
else:
|
||||
controlflow.system_error_exit(
|
||||
2,
|
||||
f'{role} is not a valid role for "gam print group-members {myarg}"'
|
||||
)
|
||||
i += 2
|
||||
elif myarg in ['cigroup', 'cigroups']:
|
||||
group_email = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
|
||||
groups_to_get = [group_email]
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam print cigroup-members')
|
||||
if not groups_to_get:
|
||||
gam.printGettingAllItems('Groups', None)
|
||||
page_message = gapi.got_total_items_first_last_msg('Groups')
|
||||
groups_to_get = gapi.get_all_pages(
|
||||
ci.groups(),
|
||||
'list',
|
||||
'groups',
|
||||
message_attribute=['groupKey', 'id'],
|
||||
page_message=page_message,
|
||||
parent=parent,
|
||||
view='BASIC',
|
||||
pageSize=1000,
|
||||
fields='nextPageToken,groups(groupKey(id))')
|
||||
groups_to_get = [group['groupKey']['id'] for group in groups_to_get]
|
||||
i = 0
|
||||
count = len(groups_to_get)
|
||||
for group_email in groups_to_get:
|
||||
i += 1
|
||||
|
||||
sys.stderr.write(
|
||||
f'Getting members for {group_email}{gam.currentCountNL(i, count)}')
|
||||
group_id = group_email_to_id(ci, group_email)
|
||||
print(f'Getting members of cigroup {group_email}...')
|
||||
page_message = f' {gapi.got_total_items_first_last_msg("Members")}'
|
||||
group_members = gapi.get_all_pages(
|
||||
ci.groups().memberships(),
|
||||
'list',
|
||||
'memberships',
|
||||
soft_errors=True,
|
||||
parent=group_id,
|
||||
view='FULL',
|
||||
pageSize=500,
|
||||
page_message=page_message,
|
||||
message_attribute=['memberKey', 'id'])
|
||||
#fields='nextPageToken,memberships(memberKey,roles,createTime,updateTime)')
|
||||
if roles:
|
||||
group_members = filter_members_to_roles(group_members, roles)
|
||||
for member in group_members:
|
||||
# reduce role to a single value
|
||||
member['role'] = get_single_role(member.pop('roles'))
|
||||
member = utils.flatten_json(member)
|
||||
for title in member:
|
||||
if title not in titles:
|
||||
titles.append(title)
|
||||
member['group'] = group_email
|
||||
csvRows.append(member)
|
||||
display.write_csv_file(csvRows, titles, 'Group Members', todrive)
|
||||
|
||||
|
||||
def update():
|
||||
|
||||
# Convert foo@googlemail.com to foo@gmail.com; eliminate periods in name for foo.bar@gmail.com
|
||||
def _cleanConsumerAddress(emailAddress, mapCleanToOriginal):
|
||||
atLoc = emailAddress.find('@')
|
||||
if atLoc > 0:
|
||||
if emailAddress[atLoc + 1:] in ['gmail.com', 'googlemail.com']:
|
||||
cleanEmailAddress = emailAddress[:atLoc].replace(
|
||||
'.', '') + '@gmail.com'
|
||||
if cleanEmailAddress != emailAddress:
|
||||
mapCleanToOriginal[cleanEmailAddress] = emailAddress
|
||||
return cleanEmailAddress
|
||||
return emailAddress
|
||||
|
||||
def _getRoleAndUsers():
|
||||
checkSuspended = None
|
||||
role = None
|
||||
i = 5
|
||||
if sys.argv[i].lower() in GROUP_ROLES_MAP:
|
||||
role = GROUP_ROLES_MAP[sys.argv[i].lower()]
|
||||
i += 1
|
||||
if sys.argv[i].lower() in ['suspended', 'notsuspended']:
|
||||
checkSuspended = sys.argv[i].lower() == 'suspended'
|
||||
i += 1
|
||||
if sys.argv[i].lower() in usergroup_types:
|
||||
users_email = gam.getUsersToModify(entity_type=sys.argv[i].lower(),
|
||||
entity=sys.argv[i + 1],
|
||||
checkSuspended=checkSuspended,
|
||||
groupUserMembersOnly=False)
|
||||
else:
|
||||
users_email = [
|
||||
gam.normalizeEmailAddressOrUID(sys.argv[i],
|
||||
checkForCustomerId=True)
|
||||
]
|
||||
return (role, users_email)
|
||||
|
||||
ci = gapi_cloudidentity.build('cloudidentity_beta')
|
||||
group = sys.argv[3]
|
||||
myarg = sys.argv[4].lower()
|
||||
items = []
|
||||
if myarg in UPDATE_GROUP_SUBCMDS:
|
||||
group = gam.normalizeEmailAddressOrUID(group)
|
||||
if group.startswith('groups/'):
|
||||
parent = group
|
||||
else:
|
||||
parent = group_email_to_id(ci, group)
|
||||
if not parent:
|
||||
return
|
||||
if myarg == 'add':
|
||||
role, users_email = _getRoleAndUsers()
|
||||
if not role:
|
||||
role = ROLE_MEMBER
|
||||
if len(users_email) > 1:
|
||||
sys.stderr.write(
|
||||
f'Group: {group}, Will add {len(users_email)} {role}s.\n')
|
||||
for user_email in users_email:
|
||||
item = [
|
||||
'gam', 'update', 'cigroup', f'id:{parent}', 'add', role,
|
||||
user_email
|
||||
]
|
||||
items.append(item)
|
||||
elif len(users_email) > 0:
|
||||
body = {
|
||||
'memberKey': {
|
||||
'id': users_email[0]
|
||||
},
|
||||
'roles': [{
|
||||
'name': ROLE_MEMBER
|
||||
}]
|
||||
}
|
||||
if role != ROLE_MEMBER:
|
||||
body['roles'].append({'name': role})
|
||||
add_text = [f'as {role}']
|
||||
for i in range(2):
|
||||
try:
|
||||
gapi.call(
|
||||
ci.groups().memberships(),
|
||||
'create',
|
||||
throw_reasons=[
|
||||
gapi_errors.ErrorReason.FOUR_O_NINE,
|
||||
gapi_errors.ErrorReason.MEMBER_NOT_FOUND,
|
||||
gapi_errors.ErrorReason.RESOURCE_NOT_FOUND,
|
||||
gapi_errors.ErrorReason.INVALID_MEMBER,
|
||||
gapi_errors.ErrorReason.
|
||||
CYCLIC_MEMBERSHIPS_NOT_ALLOWED
|
||||
],
|
||||
parent=parent,
|
||||
body=body)
|
||||
print(
|
||||
f' Group: {group}, {users_email[0]} Added {" ".join(add_text)}'
|
||||
)
|
||||
break
|
||||
except (gapi_errors.GapiMemberNotFoundError,
|
||||
gapi_errors.GapiResourceNotFoundError,
|
||||
gapi_errors.GapiInvalidMemberError,
|
||||
gapi_errors.GapiCyclicMembershipsNotAllowedError
|
||||
) as e:
|
||||
print(
|
||||
f' Group: {group}, {users_email[0]} Add {" ".join(add_text)} Failed: {str(e)}'
|
||||
)
|
||||
break
|
||||
elif myarg == 'sync':
|
||||
syncMembersSet = set()
|
||||
syncMembersMap = {}
|
||||
role, users_email = _getRoleAndUsers()
|
||||
for user_email in users_email:
|
||||
if user_email in ('*', GC_Values[GC_CUSTOMER_ID]):
|
||||
syncMembersSet.add(GC_Values[GC_CUSTOMER_ID])
|
||||
else:
|
||||
syncMembersSet.add(
|
||||
_cleanConsumerAddress(user_email.lower(),
|
||||
syncMembersMap))
|
||||
currentMembersSet = set()
|
||||
currentMembersMap = {}
|
||||
for current_email in gam.getUsersToModify(
|
||||
entity_type='cigroup',
|
||||
entity=group,
|
||||
member_type=role,
|
||||
groupUserMembersOnly=False):
|
||||
if current_email == GC_Values[GC_CUSTOMER_ID]:
|
||||
currentMembersSet.add(current_email)
|
||||
else:
|
||||
currentMembersSet.add(
|
||||
_cleanConsumerAddress(current_email.lower(),
|
||||
currentMembersMap))
|
||||
to_add = [
|
||||
syncMembersMap.get(emailAddress, emailAddress)
|
||||
for emailAddress in syncMembersSet - currentMembersSet
|
||||
]
|
||||
to_remove = [
|
||||
currentMembersMap.get(emailAddress, emailAddress)
|
||||
for emailAddress in currentMembersSet - syncMembersSet
|
||||
]
|
||||
sys.stderr.write(
|
||||
f'Group: {group}, Will add {len(to_add)} and remove {len(to_remove)} {role}s.\n'
|
||||
)
|
||||
for user in to_add:
|
||||
item = [
|
||||
'gam', 'update', 'cigroup', f'id:{parent}', 'add', role,
|
||||
user
|
||||
]
|
||||
items.append(item)
|
||||
for user in to_remove:
|
||||
items.append([
|
||||
'gam', 'update', 'cigroup', f'id:{parent}', 'remove', user
|
||||
])
|
||||
elif myarg in ['delete', 'remove']:
|
||||
_, users_email = _getRoleAndUsers()
|
||||
if len(users_email) > 1:
|
||||
sys.stderr.write(
|
||||
f'Group: {group}, Will remove {len(users_email)} emails.\n')
|
||||
for user_email in users_email:
|
||||
items.append([
|
||||
'gam', 'update', 'cigroup', f'id:{parent}', 'remove',
|
||||
user_email
|
||||
])
|
||||
elif len(users_email) == 1:
|
||||
name = membership_email_to_id(ci, parent, users_email[0])
|
||||
try:
|
||||
gapi.call(ci.groups().memberships(),
|
||||
'delete',
|
||||
throw_reasons=[
|
||||
gapi_errors.ErrorReason.MEMBER_NOT_FOUND,
|
||||
gapi_errors.ErrorReason.INVALID_MEMBER
|
||||
],
|
||||
name=name)
|
||||
print(f' Group: {group}, {users_email[0]} Removed')
|
||||
except (gapi_errors.GapiMemberNotFoundError,
|
||||
gapi_errors.GapiInvalidMemberError) as e:
|
||||
print(
|
||||
f' Group: {group}, {users_email[0]} Remove Failed: {str(e)}'
|
||||
)
|
||||
elif myarg == 'update':
|
||||
role, users_email = _getRoleAndUsers()
|
||||
if not role:
|
||||
role = ROLE_MEMBER
|
||||
if len(users_email) > 1:
|
||||
sys.stderr.write(
|
||||
f'Group: {group}, Will update {len(users_email)} {role}s.\n'
|
||||
)
|
||||
for user_email in users_email:
|
||||
item = [
|
||||
'gam', 'update', 'cigroup', f'id:{parent}', 'update',
|
||||
role, user_email
|
||||
]
|
||||
items.append(item)
|
||||
elif len(users_email) > 0:
|
||||
name = membership_email_to_id(ci, parent, users_email[0])
|
||||
addRoles = []
|
||||
removeRoles = []
|
||||
new_role = {'role': role}
|
||||
current_roles = gapi.call(ci.groups().memberships(),
|
||||
'get',
|
||||
name=name,
|
||||
fields='roles').get('roles', [])
|
||||
current_roles = [role['name'] for role in current_roles]
|
||||
for crole in current_roles:
|
||||
if crole != ROLE_MEMBER and crole != role:
|
||||
removeRoles.append(crole)
|
||||
if role not in current_roles:
|
||||
addRoles.append({'name': role})
|
||||
bodys = []
|
||||
if addRoles:
|
||||
bodys.append({'addRoles': addRoles})
|
||||
if removeRoles:
|
||||
bodys.append({'removeRoles': removeRoles})
|
||||
for body in bodys:
|
||||
try:
|
||||
gapi.call(ci.groups().memberships(),
|
||||
'modifyMembershipRoles',
|
||||
throw_reasons=[
|
||||
gapi_errors.ErrorReason.MEMBER_NOT_FOUND,
|
||||
gapi_errors.ErrorReason.INVALID_MEMBER
|
||||
],
|
||||
name=name,
|
||||
body=body)
|
||||
except (gapi_errors.GapiMemberNotFoundError,
|
||||
gapi_errors.GapiInvalidMemberError) as e:
|
||||
print(
|
||||
f' Group: {group}, {users_email[0]} Update to {role} Failed: {str(e)}'
|
||||
)
|
||||
break
|
||||
print(
|
||||
f' Group: {group}, {users_email[0]} Updated to {role}'
|
||||
)
|
||||
|
||||
else: # clear
|
||||
roles = []
|
||||
i = 5
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg.upper() in [ROLE_OWNER, ROLE_MANAGER, ROLE_MEMBER]:
|
||||
roles.append(myarg.upper())
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(
|
||||
sys.argv[i], 'gam update cigroup clear')
|
||||
if not roles:
|
||||
roles = [ROLE_MEMBER]
|
||||
group = gam.normalizeEmailAddressOrUID(group)
|
||||
member_type_message = f'{",".join(roles).lower()}s'
|
||||
sys.stderr.write(
|
||||
f'Getting {member_type_message} of {group} (may take some time for large groups)...\n'
|
||||
)
|
||||
page_message = gapi.got_total_items_msg(f'{member_type_message}',
|
||||
'...')
|
||||
try:
|
||||
result = gapi.get_all_pages(
|
||||
ci.groups().memberships(),
|
||||
'list',
|
||||
'memberships',
|
||||
page_message=page_message,
|
||||
throw_reasons=gapi_errors.MEMBERS_THROW_REASONS,
|
||||
parent=parent,
|
||||
fields='nextPageToken,memberships(memberKey,roles)')
|
||||
result = filter_members_to_roles(result, roles)
|
||||
if not result:
|
||||
print('Group already has 0 members')
|
||||
return
|
||||
users_email = [member['memberKey']['id'] for member in result]
|
||||
sys.stderr.write(
|
||||
f'Group: {group}, Will remove {len(users_email)} {", ".join(roles).lower()}s.\n'
|
||||
)
|
||||
for user_email in users_email:
|
||||
items.append([
|
||||
'gam', 'update', 'cigroup', group, 'remove', user_email
|
||||
])
|
||||
except (gapi_errors.GapiGroupNotFoundError,
|
||||
gapi_errors.GapiDomainNotFoundError,
|
||||
gapi_errors.GapiInvalidError,
|
||||
gapi_errors.GapiForbiddenError):
|
||||
gam.entityUnknownWarning('Group', group, 0, 0)
|
||||
if items:
|
||||
gam.run_batch(items)
|
||||
else:
|
||||
i = 4
|
||||
body = {}
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'name':
|
||||
body['displayName'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'description':
|
||||
body['description'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'security':
|
||||
body['labels'] = {
|
||||
'cloudidentity.googleapis.com/groups.security': '',
|
||||
'cloudidentity.googleapis.com/groups.discussion_forum': ''
|
||||
}
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam update cigroup')
|
||||
updateMask = ','.join(body.keys())
|
||||
name = group_email_to_id(ci, group)
|
||||
print(f'Updating group {group}')
|
||||
gapi.call(ci.groups(),
|
||||
'patch',
|
||||
updateMask=updateMask,
|
||||
name=name,
|
||||
body=body)
|
||||
|
||||
|
||||
def group_email_to_id(ci, group, i=0, count=0):
|
||||
group = gam.normalizeEmailAddressOrUID(group)
|
||||
try:
|
||||
return gapi.call(ci.groups(),
|
||||
'lookup',
|
||||
throw_reasons=gapi_errors.GROUP_GET_THROW_REASONS,
|
||||
retry_reasons=gapi_errors.GROUP_GET_RETRY_REASONS,
|
||||
groupKey_id=group,
|
||||
fields='name').get('name')
|
||||
except (gapi_errors.GapiGroupNotFoundError,
|
||||
gapi_errors.GapiDomainNotFoundError,
|
||||
gapi_errors.GapiDomainCannotUseApisError,
|
||||
gapi_errors.GapiForbiddenError, gapi_errors.GapiBadRequestError):
|
||||
gam.entityUnknownWarning('Group', group, i, count)
|
||||
return None
|
||||
|
||||
|
||||
def membership_email_to_id(ci, parent, membership, i=0, count=0):
|
||||
membership = gam.normalizeEmailAddressOrUID(membership)
|
||||
try:
|
||||
return gapi.call(ci.groups().memberships(),
|
||||
'lookup',
|
||||
throw_reasons=gapi_errors.GROUP_GET_THROW_REASONS,
|
||||
retry_reasons=gapi_errors.GROUP_GET_RETRY_REASONS,
|
||||
parent=parent,
|
||||
memberKey_id=membership,
|
||||
fields='name').get('name')
|
||||
except (gapi_errors.GapiGroupNotFoundError,
|
||||
gapi_errors.GapiDomainNotFoundError,
|
||||
gapi_errors.GapiDomainCannotUseApisError,
|
||||
gapi_errors.GapiForbiddenError, gapi_errors.GapiBadRequestError):
|
||||
gam.entityUnknownWarning('Membership', membership, i, count)
|
||||
return None
|
||||
|
||||
|
||||
def get_single_role(roles):
|
||||
''' returns the highest role of member '''
|
||||
roles = [role.get('name') for role in roles]
|
||||
if not roles:
|
||||
return ROLE_MEMBER
|
||||
for a_role in [ROLE_OWNER, ROLE_MANAGER, ROLE_MEMBER]:
|
||||
if a_role in roles:
|
||||
return a_role
|
||||
return roles[0]
|
||||
|
||||
|
||||
def filter_members_to_roles(members, roles):
|
||||
filtered_members = []
|
||||
for member in members:
|
||||
role = get_single_role(member.get('roles', []))
|
||||
if role in roles:
|
||||
filtered_members.append(member)
|
||||
return filtered_members
|
||||
@@ -1,5 +1,5 @@
|
||||
import gam
|
||||
|
||||
|
||||
def buildGAPIObject():
|
||||
def build():
|
||||
return gam.buildGAPIObject('directory')
|
||||
|
||||
58
src/gam/gapi/directory/asps.py
Normal file
58
src/gam/gapi/directory/asps.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import sys
|
||||
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
from gam import utils
|
||||
|
||||
|
||||
def info(users):
|
||||
cd = gapi_directory.build()
|
||||
for user in users:
|
||||
asps = gapi.get_items(cd.asps(), 'list', 'items', userKey=user)
|
||||
if asps:
|
||||
print(f'Application-Specific Passwords for {user}')
|
||||
for asp in asps:
|
||||
if asp['creationTime'] == '0':
|
||||
created_date = 'Unknown'
|
||||
else:
|
||||
created_date = utils.formatTimestampYMDHMS(
|
||||
asp['creationTime'])
|
||||
if asp['lastTimeUsed'] == '0':
|
||||
used_date = 'Never'
|
||||
else:
|
||||
last_used = asp['lastTimeUsed']
|
||||
used_date = utils.formatTimestampYMDHMS(last_used)
|
||||
print(f' ID: {asp["codeId"]}\n' \
|
||||
f' Name: {asp["name"]}\n' \
|
||||
f' Created: {created_date}\n' \
|
||||
f' Last Used: {used_date}\n')
|
||||
else:
|
||||
print(f' no ASPs for {user}\n')
|
||||
|
||||
|
||||
def delete(users, cd=None, codeIdList=None):
|
||||
if not cd:
|
||||
cd = gapi_directory.build()
|
||||
if not codeIdList:
|
||||
codeIdList = sys.argv[5].lower()
|
||||
if codeIdList == 'all':
|
||||
allCodeIds = True
|
||||
else:
|
||||
allCodeIds = False
|
||||
codeIds = codeIdList.replace(',', ' ').split()
|
||||
for user in users:
|
||||
if allCodeIds:
|
||||
print(f'Getting Application Specific Passwords for {user}')
|
||||
asps = gapi.get_items(cd.asps(),
|
||||
'list',
|
||||
'items',
|
||||
userKey=user,
|
||||
fields='items/codeId')
|
||||
codeIds = [asp['codeId'] for asp in asps]
|
||||
if not codeIds:
|
||||
print('No ASPs')
|
||||
for codeId in codeIds:
|
||||
gapi.call(cd.asps(), 'delete', userKey=user, codeId=codeId)
|
||||
print(f'deleted ASP {codeId} for {user}')
|
||||
@@ -7,11 +7,12 @@ from gam import display
|
||||
from gam import fileutils
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
from gam.gapi.directory import orgunits as gapi_directory_orgunits
|
||||
from gam import utils
|
||||
|
||||
|
||||
def doUpdateCros():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
i, devices = getCrOSDeviceEntity(3, cd)
|
||||
update_body = {}
|
||||
action_body = {}
|
||||
@@ -32,7 +33,7 @@ def doUpdateCros():
|
||||
update_body['annotatedAssetId'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg in ['ou', 'org']:
|
||||
orgUnitPath = gam.getOrgUnitItem(sys.argv[i + 1])
|
||||
orgUnitPath = gapi_directory_orgunits.getOrgUnitItem(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'action':
|
||||
action = sys.argv[i + 1].lower().replace('_', '').replace('-', '')
|
||||
@@ -52,11 +53,15 @@ def doUpdateCros():
|
||||
elif action in ['deprovisionretiringdevice']:
|
||||
action = 'deprovision'
|
||||
deprovisionReason = 'retiring_device'
|
||||
elif action == 'deprovisionupgradetransfer':
|
||||
action = 'deprovision'
|
||||
deprovisionReason = 'upgrade_transfer'
|
||||
elif action not in ['disable', 'reenable']:
|
||||
controlflow.system_error_exit(2, f'expected action of ' \
|
||||
f'deprovision_same_model_replace, ' \
|
||||
f'deprovision_different_model_replace, ' \
|
||||
f'deprovision_retiring_device, disable or reenable,'
|
||||
f'deprovision_retiring_device, ' \
|
||||
f'deprovision_upgrade_transfer, disable or reenable,'
|
||||
f' got {action}')
|
||||
action_body = {'action': action}
|
||||
if deprovisionReason:
|
||||
@@ -120,7 +125,7 @@ def doUpdateCros():
|
||||
|
||||
|
||||
def doGetCrosInfo():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
i, devices = getCrOSDeviceEntity(3, cd)
|
||||
downloadfile = None
|
||||
targetFolder = GC_Values[GC_DRIVE_DIR]
|
||||
@@ -330,7 +335,7 @@ def doGetCrosInfo():
|
||||
|
||||
|
||||
def doPrintCrosActivity():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
todrive = False
|
||||
titles = [
|
||||
'deviceId', 'annotatedAssetId', 'annotatedLocation', 'serialNumber',
|
||||
@@ -354,7 +359,7 @@ def doPrintCrosActivity():
|
||||
queries = gam.getQueries(myarg, sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'limittoou':
|
||||
orgUnitPath = gam.getOrgUnitItem(sys.argv[i + 1])
|
||||
orgUnitPath = gapi_directory_orgunits.getOrgUnitItem(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'todrive':
|
||||
todrive = True
|
||||
@@ -501,7 +506,7 @@ def doPrintCrosDevices():
|
||||
elif myarg in CROS_SYSTEM_RAM_FREE_REPORTS_ARGUMENTS:
|
||||
selectedLists['systemRamFreeReports'] = True
|
||||
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
todrive = False
|
||||
fieldsList = []
|
||||
fieldsTitles = {}
|
||||
@@ -522,7 +527,7 @@ def doPrintCrosDevices():
|
||||
queries = gam.getQueries(myarg, sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'limittoou':
|
||||
orgUnitPath = gam.getOrgUnitItem(sys.argv[i + 1])
|
||||
orgUnitPath = gapi_directory_orgunits.getOrgUnitItem(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'todrive':
|
||||
todrive = True
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import datetime
|
||||
|
||||
import gam
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import gapi
|
||||
@@ -8,7 +9,7 @@ from gam.gapi import reports as gapi_reports
|
||||
|
||||
|
||||
def doGetCustomerInfo():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
customer_info = gapi.call(cd.customers(),
|
||||
'get',
|
||||
customerKey=GC_Values[GC_CUSTOMER_ID])
|
||||
@@ -69,7 +70,7 @@ def doGetCustomerInfo():
|
||||
customerId = GC_Values[GC_CUSTOMER_ID]
|
||||
if customerId == MY_CUSTOMER:
|
||||
customerId = None
|
||||
rep = gapi_reports.buildGAPIObject()
|
||||
rep = gapi_reports.build()
|
||||
usage = None
|
||||
throw_reasons = [
|
||||
gapi.errors.ErrorReason.INVALID, gapi.errors.ErrorReason.FORBIDDEN
|
||||
@@ -108,7 +109,7 @@ def doGetCustomerInfo():
|
||||
|
||||
|
||||
def doUpdateCustomer():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
body = {}
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
@@ -138,3 +139,11 @@ def doUpdateCustomer():
|
||||
customerKey=GC_Values[GC_CUSTOMER_ID],
|
||||
body=body)
|
||||
print('Updated customer')
|
||||
|
||||
|
||||
def setTrueCustomerId():
|
||||
if GC_Values[GC_CUSTOMER_ID] == MY_CUSTOMER:
|
||||
cd = gapi_directory.build()
|
||||
GC_Values[GC_CUSTOMER_ID] = gapi.call(cd.customers(), 'get',
|
||||
customerKey=GC_Values[GC_CUSTOMER_ID],
|
||||
fields='id').get('id', GC_Values[GC_CUSTOMER_ID])
|
||||
|
||||
76
src/gam/gapi/directory/domainaliases.py
Normal file
76
src/gam/gapi/directory/domainaliases.py
Normal file
@@ -0,0 +1,76 @@
|
||||
import sys
|
||||
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
from gam import utils
|
||||
|
||||
|
||||
def create():
|
||||
cd = gapi_directory.build()
|
||||
body = {'domainAliasName': sys.argv[3], 'parentDomainName': sys.argv[4]}
|
||||
print(f'Adding {body["domainAliasName"]} alias for ' \
|
||||
f'{body["parentDomainName"]}')
|
||||
gapi.call(cd.domainAliases(),
|
||||
'insert',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
body=body)
|
||||
|
||||
|
||||
def delete():
|
||||
cd = gapi_directory.build()
|
||||
domainAliasName = sys.argv[3]
|
||||
print(f'Deleting domain alias {domainAliasName}')
|
||||
gapi.call(cd.domainAliases(),
|
||||
'delete',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
domainAliasName=domainAliasName)
|
||||
|
||||
|
||||
def info():
|
||||
cd = gapi_directory.build()
|
||||
alias = sys.argv[3]
|
||||
result = gapi.call(cd.domainAliases(),
|
||||
'get',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
domainAliasName=alias)
|
||||
if 'creationTime' in result:
|
||||
result['creationTime'] = utils.formatTimestampYMDHMSF(
|
||||
result['creationTime'])
|
||||
display.print_json(result)
|
||||
|
||||
|
||||
def print_():
|
||||
cd = gapi_directory.build()
|
||||
todrive = False
|
||||
titles = [
|
||||
'domainAliasName',
|
||||
]
|
||||
csvRows = []
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam print domainaliases')
|
||||
results = gapi.call(cd.domainAliases(),
|
||||
'list',
|
||||
customer=GC_Values[GC_CUSTOMER_ID])
|
||||
for domainAlias in results['domainAliases']:
|
||||
domainAlias_attributes = {}
|
||||
for attr in domainAlias:
|
||||
if attr in ['kind', 'etag']:
|
||||
continue
|
||||
if attr == 'creationTime':
|
||||
domainAlias[attr] = utils.formatTimestampYMDHMSF(
|
||||
domainAlias[attr])
|
||||
if attr not in titles:
|
||||
titles.append(attr)
|
||||
domainAlias_attributes[attr] = domainAlias[attr]
|
||||
csvRows.append(domainAlias_attributes)
|
||||
display.write_csv_file(csvRows, titles, 'Domains', todrive)
|
||||
124
src/gam/gapi/directory/domains.py
Normal file
124
src/gam/gapi/directory/domains.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import sys
|
||||
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
from gam.gapi.directory import customer as gapi_directory_customer
|
||||
from gam import utils
|
||||
|
||||
|
||||
def create():
|
||||
cd = gapi_directory.build()
|
||||
domain_name = sys.argv[3]
|
||||
body = {'domainName': domain_name}
|
||||
gapi.call(cd.domains(),
|
||||
'insert',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
body=body)
|
||||
print(f'Added domain {domain_name}')
|
||||
|
||||
|
||||
def info():
|
||||
if (len(sys.argv) < 4) or (sys.argv[3] == 'logo'):
|
||||
gapi_directory_customer.doGetCustomerInfo()
|
||||
return
|
||||
cd = gapi_directory.build()
|
||||
domainName = sys.argv[3]
|
||||
result = gapi.call(cd.domains(),
|
||||
'get',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
domainName=domainName)
|
||||
if 'creationTime' in result:
|
||||
result['creationTime'] = utils.formatTimestampYMDHMSF(
|
||||
result['creationTime'])
|
||||
if 'domainAliases' in result:
|
||||
for i in range(0, len(result['domainAliases'])):
|
||||
if 'creationTime' in result['domainAliases'][i]:
|
||||
result['domainAliases'][i][
|
||||
'creationTime'] = utils.formatTimestampYMDHMSF(
|
||||
result['domainAliases'][i]['creationTime'])
|
||||
display.print_json(result)
|
||||
|
||||
|
||||
def update():
|
||||
cd = gapi_directory.build()
|
||||
domain_name = sys.argv[3]
|
||||
i = 4
|
||||
body = {}
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'primary':
|
||||
body['customerDomain'] = domain_name
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam update domain')
|
||||
gapi.call(cd.customers(),
|
||||
'update',
|
||||
customerKey=GC_Values[GC_CUSTOMER_ID],
|
||||
body=body)
|
||||
print(f'{domain_name} is now the primary domain.')
|
||||
|
||||
|
||||
def delete():
|
||||
cd = gapi_directory.build()
|
||||
domainName = sys.argv[3]
|
||||
print(f'Deleting domain {domainName}')
|
||||
gapi.call(cd.domains(),
|
||||
'delete',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
domainName=domainName)
|
||||
|
||||
|
||||
def print_():
|
||||
cd = gapi_directory.build()
|
||||
todrive = False
|
||||
titles = [
|
||||
'domainName',
|
||||
]
|
||||
csvRows = []
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam print domains')
|
||||
results = gapi.call(cd.domains(),
|
||||
'list',
|
||||
customer=GC_Values[GC_CUSTOMER_ID])
|
||||
for domain in results.get('domains', []):
|
||||
domain_attributes = {}
|
||||
domain['type'] = ['secondary', 'primary'][domain['isPrimary']]
|
||||
for attr in domain:
|
||||
if attr in ['kind', 'etag', 'domainAliases', 'isPrimary']:
|
||||
continue
|
||||
if attr in [
|
||||
'creationTime',
|
||||
]:
|
||||
domain[attr] = utils.formatTimestampYMDHMSF(domain[attr])
|
||||
if attr not in titles:
|
||||
titles.append(attr)
|
||||
domain_attributes[attr] = domain[attr]
|
||||
csvRows.append(domain_attributes)
|
||||
if 'domainAliases' in domain:
|
||||
for aliasdomain in domain['domainAliases']:
|
||||
aliasdomain['domainName'] = aliasdomain['domainAliasName']
|
||||
del aliasdomain['domainAliasName']
|
||||
aliasdomain['type'] = 'alias'
|
||||
aliasdomain_attributes = {}
|
||||
for attr in aliasdomain:
|
||||
if attr in ['kind', 'etag']:
|
||||
continue
|
||||
if attr in [
|
||||
'creationTime',
|
||||
]:
|
||||
aliasdomain[attr] = utils.formatTimestampYMDHMSF(
|
||||
aliasdomain[attr])
|
||||
if attr not in titles:
|
||||
titles.append(attr)
|
||||
aliasdomain_attributes[attr] = aliasdomain[attr]
|
||||
csvRows.append(aliasdomain_attributes)
|
||||
display.write_csv_file(csvRows, titles, 'Domains', todrive)
|
||||
1246
src/gam/gapi/directory/groups.py
Normal file
1246
src/gam/gapi/directory/groups.py
Normal file
File diff suppressed because it is too large
Load Diff
239
src/gam/gapi/directory/mobiledevices.py
Normal file
239
src/gam/gapi/directory/mobiledevices.py
Normal file
@@ -0,0 +1,239 @@
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
import gam
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
from gam import utils
|
||||
|
||||
|
||||
def delete():
|
||||
cd = gapi_directory.build()
|
||||
resourceId = sys.argv[3]
|
||||
gapi.call(cd.mobiledevices(),
|
||||
'delete',
|
||||
resourceId=resourceId,
|
||||
customerId=GC_Values[GC_CUSTOMER_ID])
|
||||
|
||||
|
||||
|
||||
def info():
|
||||
cd = gapi_directory.build()
|
||||
resourceId = sys.argv[3]
|
||||
info = gapi.call(cd.mobiledevices(),
|
||||
'get',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
resourceId=resourceId)
|
||||
if 'deviceId' in info:
|
||||
info['deviceId'] = info['deviceId'].encode('unicode-escape').decode(
|
||||
UTF8)
|
||||
attrib = 'securityPatchLevel'
|
||||
if attrib in info and int(info[attrib]):
|
||||
info[attrib] = utils.formatTimestampYMDHMS(info[attrib])
|
||||
display.print_json(info)
|
||||
|
||||
|
||||
|
||||
def print_():
|
||||
cd = gapi_directory.build()
|
||||
todrive = False
|
||||
titles = []
|
||||
csvRows = []
|
||||
fields = None
|
||||
projection = orderBy = sortOrder = None
|
||||
queries = [None]
|
||||
delimiter = ' '
|
||||
listLimit = 1
|
||||
appsLimit = -1
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg in ['query', 'queries']:
|
||||
queries = gam.getQueries(myarg, sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'delimiter':
|
||||
delimiter = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'listlimit':
|
||||
listLimit = gam.getInteger(sys.argv[i + 1], myarg, minVal=-1)
|
||||
i += 2
|
||||
elif myarg == 'appslimit':
|
||||
appsLimit = gam.getInteger(sys.argv[i + 1], myarg, minVal=-1)
|
||||
i += 2
|
||||
elif myarg == 'fields':
|
||||
fields = f'nextPageToken,mobiledevices({sys.argv[i+1]})'
|
||||
i += 2
|
||||
elif myarg == 'orderby':
|
||||
orderBy = sys.argv[i + 1].lower()
|
||||
validOrderBy = [
|
||||
'deviceid', 'email', 'lastsync', 'model', 'name', 'os',
|
||||
'status', 'type'
|
||||
]
|
||||
if orderBy not in validOrderBy:
|
||||
controlflow.expected_argument_exit('orderby',
|
||||
', '.join(validOrderBy),
|
||||
orderBy)
|
||||
if orderBy == 'lastsync':
|
||||
orderBy = 'lastSync'
|
||||
elif orderBy == 'deviceid':
|
||||
orderBy = 'deviceId'
|
||||
i += 2
|
||||
elif myarg in SORTORDER_CHOICES_MAP:
|
||||
sortOrder = SORTORDER_CHOICES_MAP[myarg]
|
||||
i += 1
|
||||
elif myarg in PROJECTION_CHOICES_MAP:
|
||||
projection = PROJECTION_CHOICES_MAP[myarg]
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam print mobile')
|
||||
for query in queries:
|
||||
gam.printGettingAllItems('Mobile Devices', query)
|
||||
page_message = gapi.got_total_items_msg('Mobile Devices', '...\n')
|
||||
all_mobile = gapi.get_all_pages(cd.mobiledevices(),
|
||||
'list',
|
||||
'mobiledevices',
|
||||
page_message=page_message,
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
query=query,
|
||||
projection=projection,
|
||||
fields=fields,
|
||||
orderBy=orderBy,
|
||||
sortOrder=sortOrder)
|
||||
for mobile in all_mobile:
|
||||
row = {}
|
||||
for attrib in mobile:
|
||||
if attrib in ['kind', 'etag']:
|
||||
continue
|
||||
if attrib in ['name', 'email', 'otherAccountsInfo']:
|
||||
if attrib not in titles:
|
||||
titles.append(attrib)
|
||||
if listLimit > 0:
|
||||
row[attrib] = delimiter.join(
|
||||
mobile[attrib][0:listLimit])
|
||||
elif listLimit == 0:
|
||||
row[attrib] = delimiter.join(mobile[attrib])
|
||||
elif attrib == 'applications':
|
||||
if appsLimit >= 0:
|
||||
if attrib not in titles:
|
||||
titles.append(attrib)
|
||||
applications = []
|
||||
j = 0
|
||||
for app in mobile[attrib]:
|
||||
j += 1
|
||||
if appsLimit and (j > appsLimit):
|
||||
break
|
||||
appDetails = []
|
||||
for field in [
|
||||
'displayName', 'packageName', 'versionName'
|
||||
]:
|
||||
appDetails.append(app.get(field, '<None>'))
|
||||
appDetails.append(
|
||||
str(app.get('versionCode', '<None>')))
|
||||
permissions = app.get('permission', [])
|
||||
if permissions:
|
||||
appDetails.append('/'.join(permissions))
|
||||
else:
|
||||
appDetails.append('<None>')
|
||||
applications.append('-'.join(appDetails))
|
||||
row[attrib] = delimiter.join(applications)
|
||||
else:
|
||||
if attrib not in titles:
|
||||
titles.append(attrib)
|
||||
if attrib == 'deviceId':
|
||||
row[attrib] = mobile[attrib].encode(
|
||||
'unicode-escape').decode(UTF8)
|
||||
elif attrib == 'securityPatchLevel' and int(mobile[attrib]):
|
||||
row[attrib] = utils.formatTimestampYMDHMS(
|
||||
mobile[attrib])
|
||||
else:
|
||||
row[attrib] = mobile[attrib]
|
||||
csvRows.append(row)
|
||||
display.sort_csv_titles(
|
||||
['resourceId', 'deviceId', 'serialNumber', 'name', 'email', 'status'],
|
||||
titles)
|
||||
display.write_csv_file(csvRows, titles, 'Mobile', todrive)
|
||||
|
||||
|
||||
def update():
|
||||
cd = gapi_directory.build
|
||||
resourceIds = sys.argv[3]
|
||||
match_users = None
|
||||
doit = False
|
||||
if resourceIds[:6] == 'query:':
|
||||
query = resourceIds[6:]
|
||||
fields = 'nextPageToken,mobiledevices(resourceId,email)'
|
||||
page_message = gapi.got_total_items_msg('Mobile Devices', '...\n')
|
||||
devices = gapi.get_all_pages(cd.mobiledevices(),
|
||||
'list',
|
||||
page_message=page_message,
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
items='mobiledevices',
|
||||
query=query,
|
||||
fields=fields)
|
||||
else:
|
||||
devices = [{'resourceId': resourceIds, 'email': ['not set']}]
|
||||
doit = True
|
||||
i = 4
|
||||
body = {}
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'action':
|
||||
body['action'] = sys.argv[i + 1].lower()
|
||||
validActions = [
|
||||
'wipe', 'wipeaccount', 'accountwipe', 'wipe_account',
|
||||
'account_wipe', 'approve', 'block',
|
||||
'cancel_remote_wipe_then_activate',
|
||||
'cancel_remote_wipe_then_block'
|
||||
]
|
||||
if body['action'] not in validActions:
|
||||
controlflow.expected_argument_exit('action',
|
||||
', '.join(validActions),
|
||||
body['action'])
|
||||
if body['action'] == 'wipe':
|
||||
body['action'] = 'admin_remote_wipe'
|
||||
elif body['action'].replace('_',
|
||||
'') in ['accountwipe', 'wipeaccount']:
|
||||
body['action'] = 'admin_account_wipe'
|
||||
i += 2
|
||||
elif myarg in ['ifusers', 'matchusers']:
|
||||
match_users = gam.getUsersToModify(entity_type=sys.argv[i + 1].lower(),
|
||||
entity=sys.argv[i + 2])
|
||||
i += 3
|
||||
elif myarg == 'doit':
|
||||
doit = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam update mobile')
|
||||
if body:
|
||||
if doit:
|
||||
print(f'Updating {len(devices)} devices')
|
||||
describe_as = 'Performing'
|
||||
else:
|
||||
print(
|
||||
f'Showing {len(devices)} changes that would be made, not actually making changes because doit argument not specified'
|
||||
)
|
||||
describe_as = 'Would perform'
|
||||
for device in devices:
|
||||
device_user = device.get('email', [''])[0]
|
||||
if match_users and device_user not in match_users:
|
||||
print(
|
||||
f'Skipping device for user {device_user} that did not match match_users argument'
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f'{describe_as} {body["action"]} on user {device_user} device {device["resourceId"]}'
|
||||
)
|
||||
if doit:
|
||||
gapi.call(cd.mobiledevices(),
|
||||
'action',
|
||||
resourceId=device['resourceId'],
|
||||
body=body,
|
||||
customerId=GC_Values[GC_CUSTOMER_ID])
|
||||
|
||||
|
||||
422
src/gam/gapi/directory/orgunits.py
Normal file
422
src/gam/gapi/directory/orgunits.py
Normal file
@@ -0,0 +1,422 @@
|
||||
import sys
|
||||
|
||||
import gam
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
from gam.gapi import errors as gapi_errors
|
||||
from gam import utils
|
||||
|
||||
|
||||
def create():
|
||||
cd = gapi_directory.build()
|
||||
name = getOrgUnitItem(sys.argv[3], pathOnly=True, absolutePath=False)
|
||||
parent = ''
|
||||
body = {}
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'description':
|
||||
body['description'] = sys.argv[i + 1].replace('\\n', '\n')
|
||||
i += 2
|
||||
elif myarg == 'parent':
|
||||
parent = getOrgUnitItem(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'noinherit':
|
||||
body['blockInheritance'] = True
|
||||
i += 1
|
||||
elif myarg == 'inherit':
|
||||
body['blockInheritance'] = False
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam create org')
|
||||
if parent.startswith('id:'):
|
||||
parent = gapi.call(cd.orgunits(),
|
||||
'get',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
orgUnitPath=parent,
|
||||
fields='orgUnitPath')['orgUnitPath']
|
||||
if parent == '/':
|
||||
orgUnitPath = parent + name
|
||||
else:
|
||||
orgUnitPath = parent + '/' + name
|
||||
if orgUnitPath.count('/') > 1:
|
||||
body['parentOrgUnitPath'], body['name'] = orgUnitPath.rsplit('/', 1)
|
||||
else:
|
||||
body['parentOrgUnitPath'] = '/'
|
||||
body['name'] = orgUnitPath[1:]
|
||||
parent = body['parentOrgUnitPath']
|
||||
gapi.call(cd.orgunits(),
|
||||
'insert',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
body=body,
|
||||
retry_reasons=[gapi_errors.ErrorReason.DAILY_LIMIT_EXCEEDED])
|
||||
print(f'Created OrgUnit {body["name"]}')
|
||||
|
||||
|
||||
def delete():
|
||||
cd = gapi_directory.build()
|
||||
name = getOrgUnitItem(sys.argv[3])
|
||||
print(f'Deleting organization {name}')
|
||||
gapi.call(cd.orgunits(),
|
||||
'delete',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
orgUnitPath=encodeOrgUnitPath(makeOrgUnitPathRelative(name)))
|
||||
|
||||
|
||||
def info(name=None, return_attrib=None):
|
||||
cd = gapi_directory.build()
|
||||
checkSuspended = None
|
||||
if not name:
|
||||
name = getOrgUnitItem(sys.argv[3])
|
||||
get_users = True
|
||||
show_children = False
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'nousers':
|
||||
get_users = False
|
||||
i += 1
|
||||
elif myarg in ['children', 'child']:
|
||||
show_children = True
|
||||
i += 1
|
||||
elif myarg in ['suspended', 'notsuspended']:
|
||||
checkSuspended = myarg == 'suspended'
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam info org')
|
||||
if name == '/':
|
||||
orgs = gapi.call(cd.orgunits(),
|
||||
'list',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
type='children',
|
||||
fields='organizationUnits/parentOrgUnitId')
|
||||
if 'organizationUnits' in orgs and orgs['organizationUnits']:
|
||||
name = orgs['organizationUnits'][0]['parentOrgUnitId']
|
||||
else:
|
||||
topLevelOrgId = getTopLevelOrgId(cd, '/')
|
||||
if topLevelOrgId:
|
||||
name = topLevelOrgId
|
||||
else:
|
||||
name = makeOrgUnitPathRelative(name)
|
||||
result = gapi.call(cd.orgunits(),
|
||||
'get',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
orgUnitPath=encodeOrgUnitPath(name))
|
||||
if return_attrib:
|
||||
return result[return_attrib]
|
||||
display.print_json(result)
|
||||
if get_users:
|
||||
name = result['orgUnitPath']
|
||||
page_message = gapi.got_total_items_first_last_msg('Users')
|
||||
users = gapi.get_all_pages(
|
||||
cd.users(),
|
||||
'list',
|
||||
'users',
|
||||
page_message=page_message,
|
||||
message_attribute='primaryEmail',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
query=orgUnitPathQuery(name, checkSuspended),
|
||||
fields='users(primaryEmail,orgUnitPath),nextPageToken')
|
||||
if checkSuspended is None:
|
||||
print('Users:')
|
||||
elif not checkSuspended:
|
||||
print('Users (Not suspended):')
|
||||
else:
|
||||
print('Users (Suspended):')
|
||||
for user in users:
|
||||
if show_children or (name.lower() == user['orgUnitPath'].lower()):
|
||||
sys.stdout.write(f' {user["primaryEmail"]}')
|
||||
if name.lower() != user['orgUnitPath'].lower():
|
||||
print(' (child)')
|
||||
else:
|
||||
print('')
|
||||
|
||||
|
||||
def print_():
|
||||
print_order = [
|
||||
'orgUnitPath', 'orgUnitId', 'name', 'description', 'parentOrgUnitPath',
|
||||
'parentOrgUnitId', 'blockInheritance'
|
||||
]
|
||||
cd = gapi_directory.build()
|
||||
listType = 'all'
|
||||
orgUnitPath = '/'
|
||||
todrive = False
|
||||
fields = ['orgUnitPath', 'name', 'orgUnitId', 'parentOrgUnitId']
|
||||
titles = []
|
||||
csvRows = []
|
||||
parentOrgIds = []
|
||||
retrievedOrgIds = []
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg == 'toplevelonly':
|
||||
listType = 'children'
|
||||
i += 1
|
||||
elif myarg == 'fromparent':
|
||||
orgUnitPath = getOrgUnitItem(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'allfields':
|
||||
fields = None
|
||||
i += 1
|
||||
elif myarg == 'fields':
|
||||
fields += sys.argv[i + 1].split(',')
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam print orgs')
|
||||
gam.printGettingAllItems('Organizational Units', None)
|
||||
if fields:
|
||||
get_fields = ','.join(fields)
|
||||
list_fields = f'organizationUnits({get_fields})'
|
||||
else:
|
||||
list_fields = None
|
||||
get_fields = None
|
||||
orgs = gapi.call(cd.orgunits(),
|
||||
'list',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
type=listType,
|
||||
orgUnitPath=orgUnitPath,
|
||||
fields=list_fields)
|
||||
if not 'organizationUnits' in orgs:
|
||||
topLevelOrgId = getTopLevelOrgId(cd, orgUnitPath)
|
||||
if topLevelOrgId:
|
||||
parentOrgIds.append(topLevelOrgId)
|
||||
orgunits = []
|
||||
else:
|
||||
orgunits = orgs['organizationUnits']
|
||||
for row in orgunits:
|
||||
retrievedOrgIds.append(row['orgUnitId'])
|
||||
if row['parentOrgUnitId'] not in parentOrgIds:
|
||||
parentOrgIds.append(row['parentOrgUnitId'])
|
||||
missing_parents = set(parentOrgIds) - set(retrievedOrgIds)
|
||||
for missing_parent in missing_parents:
|
||||
try:
|
||||
result = gapi.call(cd.orgunits(),
|
||||
'get',
|
||||
throw_reasons=['required'],
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
orgUnitPath=missing_parent,
|
||||
fields=get_fields)
|
||||
orgunits.append(result)
|
||||
except:
|
||||
pass
|
||||
for row in orgunits:
|
||||
orgEntity = {}
|
||||
for key, value in list(row.items()):
|
||||
if key in ['kind', 'etag', 'etags']:
|
||||
continue
|
||||
if key not in titles:
|
||||
titles.append(key)
|
||||
orgEntity[key] = value
|
||||
csvRows.append(orgEntity)
|
||||
for title in titles:
|
||||
if title not in print_order:
|
||||
print_order.append(title)
|
||||
titles = sorted(titles, key=print_order.index)
|
||||
# sort results similar to how they list in admin console
|
||||
csvRows.sort(key=lambda x: x['orgUnitPath'].lower(), reverse=False)
|
||||
display.write_csv_file(csvRows, titles, 'Orgs', todrive)
|
||||
|
||||
|
||||
def update():
|
||||
cd = gapi_directory.build()
|
||||
orgUnitPath = getOrgUnitItem(sys.argv[3])
|
||||
if sys.argv[4].lower() in ['move', 'add']:
|
||||
entity_type = sys.argv[5].lower()
|
||||
if entity_type in usergroup_types:
|
||||
users = getUsersToModify(entity_type=entity_type,
|
||||
entity=sys.argv[6])
|
||||
else:
|
||||
entity_type = 'users'
|
||||
users = getUsersToModify(entity_type=entity_type,
|
||||
entity=sys.argv[5])
|
||||
if (entity_type.startswith('cros')) or (
|
||||
(entity_type == 'all') and (sys.argv[6].lower() == 'cros')):
|
||||
for l in range(0, len(users), 50):
|
||||
move_body = {'deviceIds': users[l:l + 50]}
|
||||
print(
|
||||
f' moving {len(move_body["deviceIds"])} devices to {orgUnitPath}'
|
||||
)
|
||||
gapi.call(cd.chromeosdevices(),
|
||||
'moveDevicesToOu',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
orgUnitPath=orgUnitPath,
|
||||
body=move_body)
|
||||
else:
|
||||
i = 0
|
||||
count = len(users)
|
||||
for user in users:
|
||||
i += 1
|
||||
sys.stderr.write(
|
||||
f' moving {user} to {orgUnitPath}{currentCountNL(i, count)}'
|
||||
)
|
||||
try:
|
||||
gapi.call(cd.users(),
|
||||
'update',
|
||||
throw_reasons=[
|
||||
gapi_errors.ErrorReason.CONDITION_NOT_MET
|
||||
],
|
||||
userKey=user,
|
||||
body={'orgUnitPath': orgUnitPath})
|
||||
except gapi_errors.GapiConditionNotMetError:
|
||||
pass
|
||||
else:
|
||||
body = {}
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'name':
|
||||
body['name'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'description':
|
||||
body['description'] = sys.argv[i + 1].replace('\\n', '\n')
|
||||
i += 2
|
||||
elif myarg == 'parent':
|
||||
parent = getOrgUnitItem(sys.argv[i + 1])
|
||||
if parent.startswith('id:'):
|
||||
body['parentOrgUnitId'] = parent
|
||||
else:
|
||||
body['parentOrgUnitPath'] = parent
|
||||
i += 2
|
||||
elif myarg == 'noinherit':
|
||||
body['blockInheritance'] = True
|
||||
i += 1
|
||||
elif myarg == 'inherit':
|
||||
body['blockInheritance'] = False
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam update org')
|
||||
gapi.call(cd.orgunits(),
|
||||
'update',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
orgUnitPath=encodeOrgUnitPath(
|
||||
makeOrgUnitPathRelative(orgUnitPath)),
|
||||
body=body)
|
||||
|
||||
|
||||
def orgUnitPathQuery(path, checkSuspended):
|
||||
query = "orgUnitPath='{0}'".format(path.replace(
|
||||
"'", "\\'")) if path != '/' else ''
|
||||
if checkSuspended is not None:
|
||||
query += f' isSuspended={checkSuspended}'
|
||||
return query
|
||||
|
||||
|
||||
def makeOrgUnitPathAbsolute(path):
|
||||
if path == '/':
|
||||
return path
|
||||
if path.startswith('/'):
|
||||
return path.rstrip('/')
|
||||
if path.startswith('id:'):
|
||||
return path
|
||||
if path.startswith('uid:'):
|
||||
return path[1:]
|
||||
return '/' + path.rstrip('/')
|
||||
|
||||
|
||||
def makeOrgUnitPathRelative(path):
|
||||
if path == '/':
|
||||
return path
|
||||
if path.startswith('/'):
|
||||
return path[1:].rstrip('/')
|
||||
if path.startswith('id:'):
|
||||
return path
|
||||
if path.startswith('uid:'):
|
||||
return path[1:]
|
||||
return path.rstrip('/')
|
||||
|
||||
|
||||
def encodeOrgUnitPath(path):
|
||||
if path.find('+') == -1 and path.find('%') == -1:
|
||||
return path
|
||||
encpath = ''
|
||||
for c in path:
|
||||
if c == '+':
|
||||
encpath += '%2B'
|
||||
elif c == '%':
|
||||
encpath += '%25'
|
||||
else:
|
||||
encpath += c
|
||||
return encpath
|
||||
|
||||
|
||||
def getOrgUnitItem(orgUnit, pathOnly=False, absolutePath=True):
|
||||
if pathOnly and (orgUnit.startswith('id:') or orgUnit.startswith('uid:')):
|
||||
controlflow.system_error_exit(
|
||||
2, f'{orgUnit} is not valid in this context')
|
||||
if absolutePath:
|
||||
return makeOrgUnitPathAbsolute(orgUnit)
|
||||
return makeOrgUnitPathRelative(orgUnit)
|
||||
|
||||
|
||||
def getTopLevelOrgId(cd, orgUnitPath):
|
||||
try:
|
||||
# create a temp org so we can learn what the top level org ID is (sigh)
|
||||
temp_org = gapi.call(cd.orgunits(),
|
||||
'insert',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
body={
|
||||
'name': 'temp-delete-me',
|
||||
'parentOrgUnitPath': orgUnitPath
|
||||
},
|
||||
fields='parentOrgUnitId,orgUnitId')
|
||||
gapi.call(cd.orgunits(),
|
||||
'delete',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
orgUnitPath=temp_org['orgUnitId'])
|
||||
return temp_org['parentOrgUnitId']
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def getOrgUnitId(orgUnit, cd=None):
|
||||
if cd is None:
|
||||
cd = buildGAPIObject('directory')
|
||||
orgUnit = getOrgUnitItem(orgUnit)
|
||||
if orgUnit[:3] == 'id:':
|
||||
return (orgUnit, orgUnit)
|
||||
if orgUnit == '/':
|
||||
result = gapi.call(cd.orgunits(),
|
||||
'list',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
orgUnitPath='/',
|
||||
type='children',
|
||||
fields='organizationUnits(parentOrgUnitId)')
|
||||
if result.get('organizationUnits', []):
|
||||
return (orgUnit, result['organizationUnits'][0]['parentOrgUnitId'])
|
||||
topLevelOrgId = getTopLevelOrgId(cd, '/')
|
||||
if topLevelOrgId:
|
||||
return (orgUnit, topLevelOrgId)
|
||||
return (orgUnit, '/') #Bogus but should never happen
|
||||
result = gapi.call(cd.orgunits(),
|
||||
'get',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
orgUnitPath=encodeOrgUnitPath(
|
||||
makeOrgUnitPathRelative(orgUnit)),
|
||||
fields='orgUnitId')
|
||||
return (orgUnit, result['orgUnitId'])
|
||||
|
||||
|
||||
def buildOrgUnitIdToNameMap():
|
||||
cd = gapi_directory.build()
|
||||
result = gapi.call(cd.orgunits(),
|
||||
'list',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
fields='organizationUnits(orgUnitPath,orgUnitId)',
|
||||
type='all')
|
||||
GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME] = {}
|
||||
for orgUnit in result['organizationUnits']:
|
||||
GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME][
|
||||
orgUnit['orgUnitId']] = orgUnit['orgUnitPath']
|
||||
|
||||
|
||||
def orgunit_from_orgunitid(orgunitid):
|
||||
if not GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME]:
|
||||
buildOrgUnitIdToNameMap()
|
||||
return GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME].get(orgunitid, orgunitid)
|
||||
32
src/gam/gapi/directory/privileges.py
Normal file
32
src/gam/gapi/directory/privileges.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from gam.var import GC_Values, GC_CUSTOMER_ID
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
|
||||
|
||||
def flatten_privilege_list(privs, parent=None):
|
||||
flat_privs = []
|
||||
for priv in privs:
|
||||
children = []
|
||||
if parent:
|
||||
priv['parent'] = parent
|
||||
if priv.get('childPrivileges'):
|
||||
children = flatten_privilege_list(priv['childPrivileges'],
|
||||
parent=priv['privilegeName'])
|
||||
priv['children'] = ' '.join(
|
||||
[child['privilegeName'] for child in children])
|
||||
del (priv['childPrivileges'])
|
||||
flat_privs = flat_privs + children
|
||||
flat_privs.append(priv)
|
||||
return flat_privs
|
||||
|
||||
|
||||
def print_(return_only=False):
|
||||
cd = gapi_directory.build()
|
||||
privs = gapi.call(cd.privileges(),
|
||||
'list',
|
||||
customer=GC_Values[GC_CUSTOMER_ID])
|
||||
privs = flatten_privilege_list(privs.get('items', []))
|
||||
if return_only:
|
||||
return privs
|
||||
display.print_json(privs)
|
||||
@@ -12,7 +12,7 @@ from gam import utils
|
||||
|
||||
def printBuildings():
|
||||
to_drive = False
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
titles = []
|
||||
csvRows = []
|
||||
fieldsList = ['buildingId']
|
||||
@@ -67,7 +67,7 @@ def printBuildings():
|
||||
|
||||
|
||||
def printResourceCalendars():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
todrive = False
|
||||
fieldsList = []
|
||||
fieldsTitles = {}
|
||||
@@ -182,7 +182,7 @@ RESCAL_ARGUMENT_TO_PROPERTY_MAP = {
|
||||
|
||||
def printFeatures():
|
||||
to_drive = False
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
titles = []
|
||||
csvRows = []
|
||||
fieldsList = ['name']
|
||||
@@ -260,7 +260,7 @@ def _getBuildingAttributes(args, body={}):
|
||||
|
||||
|
||||
def createBuilding():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
body = {
|
||||
'floorNames': ['1'],
|
||||
'buildingId': str(uuid.uuid4()),
|
||||
@@ -346,7 +346,7 @@ def getBuildingNameById(cd, buildingId):
|
||||
|
||||
|
||||
def updateBuilding():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
buildingId = getBuildingByNameOrId(cd, sys.argv[3])
|
||||
body = _getBuildingAttributes(sys.argv[4:])
|
||||
print(f'Updating building {buildingId}...')
|
||||
@@ -358,7 +358,7 @@ def updateBuilding():
|
||||
|
||||
|
||||
def getBuildingInfo():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
buildingId = getBuildingByNameOrId(cd, sys.argv[3])
|
||||
building = gapi.call(cd.resources().buildings(),
|
||||
'get',
|
||||
@@ -374,7 +374,7 @@ def getBuildingInfo():
|
||||
|
||||
|
||||
def deleteBuilding():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
buildingId = getBuildingByNameOrId(cd, sys.argv[3])
|
||||
print(f'Deleting building {buildingId}...')
|
||||
gapi.call(cd.resources().buildings(),
|
||||
@@ -397,7 +397,7 @@ def _getFeatureAttributes(args, body={}):
|
||||
|
||||
|
||||
def createFeature():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
body = _getFeatureAttributes(sys.argv[3:])
|
||||
print(f'Creating feature {body["name"]}...')
|
||||
gapi.call(cd.resources().features(),
|
||||
@@ -410,7 +410,7 @@ def updateFeature():
|
||||
# update does not work for name and name is only field to be updated
|
||||
# if additional writable fields are added to feature in the future
|
||||
# we'll add support for update as well as rename
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
oldName = sys.argv[3]
|
||||
body = {'newName': sys.argv[5:]}
|
||||
print(f'Updating feature {oldName}...')
|
||||
@@ -422,7 +422,7 @@ def updateFeature():
|
||||
|
||||
|
||||
def deleteFeature():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
featureKey = sys.argv[3]
|
||||
print(f'Deleting feature {featureKey}...')
|
||||
gapi.call(cd.resources().features(),
|
||||
@@ -480,7 +480,7 @@ def _getResourceCalendarAttributes(cd, args, body={}):
|
||||
|
||||
|
||||
def createResourceCalendar():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
body = {'resourceId': sys.argv[3], 'resourceName': sys.argv[4]}
|
||||
body = _getResourceCalendarAttributes(cd, sys.argv[5:], body)
|
||||
print(f'Creating resource {body["resourceId"]}...')
|
||||
@@ -491,7 +491,7 @@ def createResourceCalendar():
|
||||
|
||||
|
||||
def updateResourceCalendar():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
resId = sys.argv[3]
|
||||
body = _getResourceCalendarAttributes(cd, sys.argv[4:])
|
||||
# Use patch since it seems to work better.
|
||||
@@ -506,7 +506,7 @@ def updateResourceCalendar():
|
||||
|
||||
|
||||
def getResourceCalendarInfo():
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
resId = sys.argv[3]
|
||||
resource = gapi.call(cd.resources().calendars(),
|
||||
'get',
|
||||
@@ -526,7 +526,7 @@ def getResourceCalendarInfo():
|
||||
|
||||
def deleteResourceCalendar():
|
||||
resId = sys.argv[3]
|
||||
cd = gapi_directory.buildGAPIObject()
|
||||
cd = gapi_directory.build()
|
||||
print(f'Deleting resource calendar {resId}')
|
||||
gapi.call(cd.resources().calendars(),
|
||||
'delete',
|
||||
|
||||
124
src/gam/gapi/directory/roles.py
Normal file
124
src/gam/gapi/directory/roles.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import sys
|
||||
|
||||
from gam.var import GC_Values, GC_CUSTOMER_ID
|
||||
import gam
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
from gam.gapi.directory import privileges as gapi_directory_privileges
|
||||
|
||||
|
||||
def getPrivileges(body, privs, action):
|
||||
all_privileges = gapi_directory_privileges.print_(return_only=True)
|
||||
if privs == 'ALL':
|
||||
body['rolePrivileges'] = [
|
||||
{'privilegeName': p['privilegeName'], 'serviceId': p['serviceId']} for p in all_privileges
|
||||
]
|
||||
elif privs == 'ALL_OU':
|
||||
body['rolePrivileges'] = [
|
||||
{'privilegeName': p['privilegeName'], 'serviceId': p['serviceId']} for p in all_privileges if p.get('isOuScopable')
|
||||
]
|
||||
else:
|
||||
body.setdefault('rolePrivileges', [])
|
||||
for priv in privs.split(','):
|
||||
for p in all_privileges:
|
||||
if priv == p['privilegeName']:
|
||||
body['rolePrivileges'].append({'privilegeName': p['privilegeName'], 'serviceId': p['serviceId']})
|
||||
break
|
||||
else:
|
||||
controlflow.invalid_argument_exit(priv,
|
||||
f'gam {action} adminrole privileges')
|
||||
|
||||
def create():
|
||||
cd = gapi_directory.build()
|
||||
body = {'roleName': sys.argv[3]}
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'privileges':
|
||||
getPrivileges(body, sys.argv[i + 1].upper(), 'create')
|
||||
i += 2
|
||||
elif myarg == 'description':
|
||||
body['roleDescription'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam create adminrole')
|
||||
|
||||
if not body.get('rolePrivileges'):
|
||||
controlflow.missing_argument_exit('privileges',
|
||||
'gam create adminrole')
|
||||
print(f'Creating role {body["roleName"]}')
|
||||
gapi.call(cd.roles(),
|
||||
'insert',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
body=body)
|
||||
|
||||
def update():
|
||||
cd = gapi_directory.build()
|
||||
body = {}
|
||||
roleId = gam.getRoleId(sys.argv[3])
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'privileges':
|
||||
getPrivileges(body, sys.argv[i + 1].upper(), 'update')
|
||||
i += 2
|
||||
elif myarg == 'description':
|
||||
body['roleDescription'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'name':
|
||||
body['roleName'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam update adminrole')
|
||||
|
||||
print(f'Updating role {roleId}')
|
||||
gapi.call(cd.roles(),
|
||||
'patch',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
roleId=roleId,
|
||||
body=body)
|
||||
|
||||
|
||||
def delete():
|
||||
cd = gapi_directory.build()
|
||||
roleId = gam.getRoleId(sys.argv[3])
|
||||
print(f'Deleting role {roleId}')
|
||||
gapi.call(cd.roles(),
|
||||
'delete',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
roleId=roleId)
|
||||
|
||||
|
||||
def print_():
|
||||
cd = gapi_directory.build()
|
||||
todrive = False
|
||||
titles = [
|
||||
'roleId', 'roleName', 'roleDescription', 'isSuperAdminRole',
|
||||
'isSystemRole'
|
||||
]
|
||||
fields = f'nextPageToken,items({",".join(titles)})'
|
||||
csvRows = []
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam print adminroles')
|
||||
roles = gapi.get_all_pages(cd.roles(),
|
||||
'list',
|
||||
'items',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
fields=fields)
|
||||
for role in roles:
|
||||
role_attrib = {}
|
||||
for key, value in list(role.items()):
|
||||
role_attrib[key] = value
|
||||
csvRows.append(role_attrib)
|
||||
display.write_csv_file(csvRows, titles, 'Admin Roles', todrive)
|
||||
32
src/gam/gapi/directory/users.py
Normal file
32
src/gam/gapi/directory/users.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import gam
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
|
||||
def signout(users):
|
||||
cd = gapi_directory.build()
|
||||
i = 0
|
||||
count = len(users)
|
||||
for user in users:
|
||||
i += 1
|
||||
user = gam.normalizeEmailAddressOrUID(user)
|
||||
print(f'Signing Out {user}{gam.currentCount(i, count)}')
|
||||
gapi.call(cd.users(),
|
||||
'signOut',
|
||||
soft_errors=True,
|
||||
userKey=user)
|
||||
|
||||
|
||||
def turn_off_2sv(users):
|
||||
cd = gapi_directory.build()
|
||||
i = 0
|
||||
count = len(users)
|
||||
for user in users:
|
||||
i += 1
|
||||
user = gam.normalizeEmailAddressOrUID(user)
|
||||
print(f'Turning Off 2-Step Verification for {user}{gam.currentCount(i, count)}')
|
||||
gapi.call(cd.twoStepVerification(),
|
||||
'turnOff',
|
||||
soft_errors=True,
|
||||
userKey=user)
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ class ErrorReason(Enum):
|
||||
FAILED_PRECONDITION = 'failedPrecondition'
|
||||
FORBIDDEN = 'forbidden'
|
||||
FOUR_O_NINE = '409'
|
||||
FOUR_O_O = '400'
|
||||
FOUR_O_THREE = '403'
|
||||
FOUR_TWO_NINE = '429'
|
||||
GATEWAY_TIMEOUT = 'gatewayTimeout'
|
||||
|
||||
@@ -13,7 +13,7 @@ from gam import gapi
|
||||
from gam import utils
|
||||
|
||||
|
||||
def buildGAPIObject():
|
||||
def build():
|
||||
return gam.buildGAPIObject('reports')
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ REPORT_CHOICE_MAP = {
|
||||
|
||||
|
||||
def showUsageParameters():
|
||||
rep = buildGAPIObject()
|
||||
rep = build()
|
||||
throw_reasons = [
|
||||
gapi.errors.ErrorReason.INVALID, gapi.errors.ErrorReason.BAD_REQUEST
|
||||
]
|
||||
@@ -56,7 +56,7 @@ def showUsageParameters():
|
||||
kwargs = {}
|
||||
elif report == 'user':
|
||||
endpoint = rep.userUsageReport()
|
||||
kwargs = {'userKey': gam._getValueFromOAuth('email')}
|
||||
kwargs = {'userKey': gam._get_admin_email()}
|
||||
else:
|
||||
controlflow.expected_argument_exit('usageparameters',
|
||||
['user', 'customer'], report)
|
||||
@@ -115,7 +115,7 @@ REPORTS_PARAMETERS_SIMPLE_TYPES = [
|
||||
|
||||
|
||||
def showUsage():
|
||||
rep = buildGAPIObject()
|
||||
rep = build()
|
||||
throw_reasons = [
|
||||
gapi.errors.ErrorReason.INVALID, gapi.errors.ErrorReason.BAD_REQUEST
|
||||
]
|
||||
@@ -264,7 +264,7 @@ def showUsage():
|
||||
|
||||
|
||||
def showReport():
|
||||
rep = buildGAPIObject()
|
||||
rep = build()
|
||||
throw_reasons = [gapi.errors.ErrorReason.INVALID]
|
||||
report = sys.argv[2].lower()
|
||||
report = REPORT_CHOICE_MAP.get(report.replace('_', ''), report)
|
||||
@@ -316,7 +316,9 @@ def showReport():
|
||||
eventName = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'user':
|
||||
userKey = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
|
||||
userKey = sys.argv[i + 1].lower()
|
||||
if userKey != 'all':
|
||||
userKey = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in ['filter', 'filters']:
|
||||
filters = sys.argv[i + 1]
|
||||
|
||||
188
src/gam/gapi/siteverification.py
Normal file
188
src/gam/gapi/siteverification.py
Normal file
@@ -0,0 +1,188 @@
|
||||
import json
|
||||
import sys
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import gam
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import fileutils
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
from gam.gapi import errors as gapi_errors
|
||||
from gam.gapi.directory import customer as gapi_directory_customer
|
||||
from gam import transport
|
||||
from gam import utils
|
||||
|
||||
import gam
|
||||
|
||||
|
||||
def build():
|
||||
return gam.buildGAPIObject('siteVerification')
|
||||
|
||||
|
||||
def create():
|
||||
verif = build()
|
||||
a_domain = sys.argv[3]
|
||||
txt_record = gapi.call(verif.webResource(),
|
||||
'getToken',
|
||||
body={
|
||||
'site': {
|
||||
'type': 'INET_DOMAIN',
|
||||
'identifier': a_domain
|
||||
},
|
||||
'verificationMethod': 'DNS_TXT'
|
||||
})
|
||||
print(f'TXT Record Name: {a_domain}')
|
||||
print(f'TXT Record Value: {txt_record["token"]}')
|
||||
print()
|
||||
cname_record = gapi.call(verif.webResource(),
|
||||
'getToken',
|
||||
body={
|
||||
'site': {
|
||||
'type': 'INET_DOMAIN',
|
||||
'identifier': a_domain
|
||||
},
|
||||
'verificationMethod': 'DNS_CNAME'
|
||||
})
|
||||
cname_token = cname_record['token']
|
||||
cname_list = cname_token.split(' ')
|
||||
cname_subdomain = cname_list[0]
|
||||
cname_value = cname_list[1]
|
||||
print(f'CNAME Record Name: {cname_subdomain}.{a_domain}')
|
||||
print(f'CNAME Record Value: {cname_value}')
|
||||
print('')
|
||||
webserver_file_record = gapi.call(
|
||||
verif.webResource(),
|
||||
'getToken',
|
||||
body={
|
||||
'site': {
|
||||
'type': 'SITE',
|
||||
'identifier': f'http://{a_domain}/'
|
||||
},
|
||||
'verificationMethod': 'FILE'
|
||||
})
|
||||
webserver_file_token = webserver_file_record['token']
|
||||
print(f'Saving web server verification file to: {webserver_file_token}')
|
||||
fileutils.write_file(webserver_file_token,
|
||||
f'google-site-verification: {webserver_file_token}',
|
||||
continue_on_error=True)
|
||||
print(f'Verification File URL: http://{a_domain}/{webserver_file_token}')
|
||||
print()
|
||||
webserver_meta_record = gapi.call(
|
||||
verif.webResource(),
|
||||
'getToken',
|
||||
body={
|
||||
'site': {
|
||||
'type': 'SITE',
|
||||
'identifier': f'http://{a_domain}/'
|
||||
},
|
||||
'verificationMethod': 'META'
|
||||
})
|
||||
print(f'Meta URL: http://{a_domain}/')
|
||||
print(f'Meta HTML Header Data: {webserver_meta_record["token"]}')
|
||||
print()
|
||||
|
||||
|
||||
def info():
|
||||
verif = build()
|
||||
sites = gapi.get_items(verif.webResource(), 'list', 'items')
|
||||
if sites:
|
||||
for site in sites:
|
||||
print(f'Site: {site["site"]["identifier"]}')
|
||||
print(f'Type: {site["site"]["type"]}')
|
||||
print('Owners:')
|
||||
for owner in site['owners']:
|
||||
print(f' {owner}')
|
||||
print()
|
||||
else:
|
||||
print('No Sites Verified.')
|
||||
|
||||
|
||||
def update():
|
||||
verif = build()
|
||||
a_domain = sys.argv[3]
|
||||
verificationMethod = sys.argv[4].upper()
|
||||
if verificationMethod == 'CNAME':
|
||||
verificationMethod = 'DNS_CNAME'
|
||||
elif verificationMethod in ['TXT', 'TEXT']:
|
||||
verificationMethod = 'DNS_TXT'
|
||||
if verificationMethod in ['DNS_TXT', 'DNS_CNAME']:
|
||||
verify_type = 'INET_DOMAIN'
|
||||
identifier = a_domain
|
||||
else:
|
||||
verify_type = 'SITE'
|
||||
identifier = f'http://{a_domain}/'
|
||||
body = {
|
||||
'site': {
|
||||
'type': verify_type,
|
||||
'identifier': identifier
|
||||
},
|
||||
'verificationMethod': verificationMethod
|
||||
}
|
||||
try:
|
||||
verify_result = gapi.call(
|
||||
verif.webResource(),
|
||||
'insert',
|
||||
throw_reasons=[gapi_errors.ErrorReason.BAD_REQUEST],
|
||||
verificationMethod=verificationMethod,
|
||||
body=body)
|
||||
except gapi_errors.GapiBadRequestError as e:
|
||||
print(f'ERROR: {str(e)}')
|
||||
verify_data = gapi.call(verif.webResource(), 'getToken', body=body)
|
||||
print(f'Method: {verify_data["method"]}')
|
||||
print(f'Expected Token: {verify_data["token"]}')
|
||||
if verify_data['method'] in ['DNS_CNAME', 'DNS_TXT']:
|
||||
simplehttp = transport.create_http()
|
||||
base_url = 'https://dns.google/resolve?'
|
||||
query_params = {}
|
||||
if verify_data['method'] == 'DNS_CNAME':
|
||||
cname_token = verify_data['token']
|
||||
cname_list = cname_token.split(' ')
|
||||
cname_subdomain = cname_list[0]
|
||||
query_params['name'] = f'{cname_subdomain}.{a_domain}'
|
||||
query_params['type'] = 'cname'
|
||||
else:
|
||||
query_params['name'] = a_domain
|
||||
query_params['type'] = 'txt'
|
||||
full_url = base_url + urlencode(query_params)
|
||||
(_, c) = simplehttp.request(full_url, 'GET')
|
||||
result = json.loads(c)
|
||||
status = result['Status']
|
||||
if status == 0 and 'Answer' in result:
|
||||
answers = result['Answer']
|
||||
if verify_data['method'] == 'DNS_CNAME':
|
||||
answer = answers[0]['data']
|
||||
else:
|
||||
answer = 'no matching record found'
|
||||
for possible_answer in answers:
|
||||
possible_answer['data'] = possible_answer['data'].strip(
|
||||
'"')
|
||||
if possible_answer['data'].startswith(
|
||||
'google-site-verification'):
|
||||
answer = possible_answer['data']
|
||||
break
|
||||
print(
|
||||
f'Unrelated TXT record: {possible_answer["data"]}')
|
||||
print(f'Found DNS Record: {answer}')
|
||||
elif status == 0:
|
||||
controlflow.system_error_exit(1, 'DNS record not found')
|
||||
else:
|
||||
controlflow.system_error_exit(
|
||||
status,
|
||||
DNS_ERROR_CODES_MAP.get(status, f'Unknown error {status}'))
|
||||
return
|
||||
print('SUCCESS!')
|
||||
print(f'Verified: {verify_result["site"]["identifier"]}')
|
||||
print(f'ID: {verify_result["id"]}')
|
||||
print(f'Type: {verify_result["site"]["type"]}')
|
||||
print('All Owners:')
|
||||
try:
|
||||
for owner in verify_result['owners']:
|
||||
print(f' {owner}')
|
||||
except KeyError:
|
||||
pass
|
||||
print()
|
||||
print(
|
||||
f'You can now add {a_domain} or it\'s subdomains as secondary or domain aliases of the {GC_Values[GC_DOMAIN]} G Suite Account.'
|
||||
)
|
||||
@@ -8,7 +8,7 @@ import platform
|
||||
import re
|
||||
|
||||
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
|
||||
GAM_VERSION = '5.10'
|
||||
GAM_VERSION = '5.20'
|
||||
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
GAM_URL = 'https://git.io/gam'
|
||||
@@ -26,17 +26,18 @@ GAM_PROJECT_FILEPATH = 'https://raw.githubusercontent.com/jay0lee/GAM/master/src
|
||||
true_values = ['on', 'yes', 'enabled', 'true', '1']
|
||||
false_values = ['off', 'no', 'disabled', 'false', '0']
|
||||
usergroup_types = [
|
||||
'user', 'users', 'group', 'group_ns', 'grooup_susp', 'ou', 'org', 'ou_ns',
|
||||
'org_ns', 'ou_susp', 'org_susp', 'ou_and_children', 'ou_and_child',
|
||||
'ou_and_children_ns', 'ou_and_child_ns', 'ou_and_children_susp',
|
||||
'ou_and_child_susp', 'query', 'queries', 'license', 'licenses', 'licence',
|
||||
'licences', 'file', 'csv', 'csvfile', 'all', 'cros', 'cros_sn', 'crosquery',
|
||||
'crosqueries', 'crosfile', 'croscsv', 'croscsvfile'
|
||||
'user', 'users', 'group', 'group_ns', 'group_susp', 'group_inde', 'ou',
|
||||
'org', 'ou_ns', 'org_ns', 'ou_susp', 'org_susp', 'ou_and_children',
|
||||
'ou_and_child', 'ou_and_children_ns', 'ou_and_child_ns',
|
||||
'ou_and_children_susp', 'ou_and_child_susp', 'query', 'queries', 'license',
|
||||
'licenses', 'licence', 'licences', 'file', 'csv', 'csvfile', 'all', 'cros',
|
||||
'cros_sn', 'crosquery', 'crosqueries', 'crosfile', 'croscsv', 'croscsvfile'
|
||||
]
|
||||
ERROR_PREFIX = 'ERROR: '
|
||||
WARNING_PREFIX = 'WARNING: '
|
||||
UTF8 = 'utf-8'
|
||||
UTF8_SIG = 'utf-8-sig'
|
||||
FN_ENABLEDASA_TXT = 'enabledasa.txt'
|
||||
FN_EXTRA_ARGS_TXT = 'extra-args.txt'
|
||||
FN_LAST_UPDATE_CHECK_TXT = 'lastupdatecheck.txt'
|
||||
MY_CUSTOMER = 'my_customer'
|
||||
@@ -222,13 +223,19 @@ V1_DISCOVERY_APIS = {
|
||||
'appsactivity',
|
||||
'calendar',
|
||||
'drive',
|
||||
'gmail',
|
||||
'groupssettings',
|
||||
'licensing',
|
||||
'oauth2',
|
||||
'reseller',
|
||||
'siteVerification',
|
||||
'storage',
|
||||
}
|
||||
|
||||
API_NAME_MAPPING = {
|
||||
'directory': 'admin',
|
||||
'reports': 'admin',
|
||||
'datatransfer': 'admin',
|
||||
'drive3': 'drive',
|
||||
'cloudresourcemanagerv1': 'cloudresourcemanager',
|
||||
'cloudidentity_beta': 'cloudidentity',
|
||||
}
|
||||
|
||||
API_VER_MAPPING = {
|
||||
@@ -236,7 +243,8 @@ API_VER_MAPPING = {
|
||||
'appsactivity': 'v1',
|
||||
'calendar': 'v3',
|
||||
'classroom': 'v1',
|
||||
'cloudprint': 'v2',
|
||||
'cloudidentity': 'v1',
|
||||
'cloudidentity_beta': 'v1beta1',
|
||||
'cloudresourcemanager': 'v2',
|
||||
'cloudresourcemanagerv1': 'v1',
|
||||
'datatransfer': 'datatransfer_v1',
|
||||
@@ -253,6 +261,7 @@ API_VER_MAPPING = {
|
||||
'reports': 'reports_v1',
|
||||
'reseller': 'v1',
|
||||
'servicemanagement': 'v1',
|
||||
'serviceusage': 'v1',
|
||||
'sheets': 'v4',
|
||||
'siteVerification': 'v1',
|
||||
'storage': 'v1',
|
||||
@@ -268,6 +277,7 @@ API_SCOPE_MAPPING = {
|
||||
'https://www.googleapis.com/auth/drive',
|
||||
],
|
||||
'calendar': ['https://www.googleapis.com/auth/calendar',],
|
||||
'cloudidentity': ['https://www.googleapis.com/auth/cloud-identity',],
|
||||
'drive': ['https://www.googleapis.com/auth/drive',],
|
||||
'drive3': ['https://www.googleapis.com/auth/drive',],
|
||||
'gmail': [
|
||||
@@ -1054,6 +1064,8 @@ GM_CURRENT_API_SCOPES = 'scoc'
|
||||
# Values retrieved from oauth2service.json
|
||||
GM_OAUTH2SERVICE_JSON_DATA = 'oajd'
|
||||
GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID = 'oaci'
|
||||
# Full path to enabledasa.txt
|
||||
GM_ENABLEDASA_TXT = 'enda'
|
||||
# File containing time of last GAM update check
|
||||
GM_LAST_UPDATE_CHECK_TXT = 'lupc'
|
||||
# Dictionary mapping OrgUnit ID to Name
|
||||
@@ -1093,6 +1105,7 @@ GM_Globals = {
|
||||
GM_CURRENT_API_SCOPES: [],
|
||||
GM_OAUTH2SERVICE_JSON_DATA: None,
|
||||
GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID: None,
|
||||
GM_ENABLEDASA_TXT: '',
|
||||
GM_LAST_UPDATE_CHECK_TXT: '',
|
||||
GM_MAP_ORGUNIT_ID_TO_NAME: None,
|
||||
GM_MAP_ROLE_ID_TO_NAME: None,
|
||||
@@ -1126,6 +1139,8 @@ GC_CLIENT_SECRETS_JSON = 'client_secrets_json'
|
||||
GC_CONFIG_DIR = 'config_dir'
|
||||
# custmerId from gam.cfg or retrieved from Google
|
||||
GC_CUSTOMER_ID = 'customer_id'
|
||||
# Enable Delegated Admin Service Accounts admin user
|
||||
GC_ADMIN_EMAIL = 'admin_email'
|
||||
# If debug_level > 0: extra_args[u'prettyPrint'] = True,
|
||||
# httplib2.debuglevel = gam_debug_level, appsObj.debug = True
|
||||
GC_DEBUG_LEVEL = 'debug_level'
|
||||
@@ -1136,6 +1151,8 @@ GC_DECODED_ID_TOKEN = 'decoded_id_token'
|
||||
GC_DOMAIN = 'domain'
|
||||
# Google Drive download directory
|
||||
GC_DRIVE_DIR = 'drive_dir'
|
||||
# Enable Delegated Admin Service Accounts
|
||||
GC_ENABLE_DASA = 'enabledasa'
|
||||
# If no_browser is False, writeCSVfile won't open a browser when todrive is set
|
||||
# and doRequestOAuth prints a link and waits for the verification code when
|
||||
# oauth2.txt is being created
|
||||
@@ -1177,6 +1194,7 @@ GC_CA_FILE = 'ca_file'
|
||||
|
||||
TLS_MIN = 'TLSv1_2' if hasattr(ssl.SSLContext(), 'minimum_version') else None
|
||||
GC_Defaults = {
|
||||
GC_ADMIN_EMAIL: '',
|
||||
GC_AUTO_BATCH_MIN: 0,
|
||||
GC_BATCH_SIZE: 50,
|
||||
GC_CACHE_DIR: '',
|
||||
@@ -1189,6 +1207,7 @@ GC_Defaults = {
|
||||
GC_DECODED_ID_TOKEN: '',
|
||||
GC_DOMAIN: '',
|
||||
GC_DRIVE_DIR: '',
|
||||
GC_ENABLE_DASA: False,
|
||||
GC_NO_BROWSER: False,
|
||||
GC_NO_CACHE: False,
|
||||
GC_NO_SHORT_URLS: False,
|
||||
@@ -1226,6 +1245,9 @@ GC_VAR_TYPE = 'type'
|
||||
GC_VAR_LIMITS = 'lmit'
|
||||
|
||||
GC_VAR_INFO = {
|
||||
GC_ADMIN_EMAIL: {
|
||||
GC_VAR_TYPE: GC_TYPE_STRING
|
||||
},
|
||||
GC_AUTO_BATCH_MIN: {
|
||||
GC_VAR_TYPE: GC_TYPE_INTEGER,
|
||||
GC_VAR_LIMITS: (0, None)
|
||||
@@ -1265,6 +1287,9 @@ GC_VAR_INFO = {
|
||||
GC_DRIVE_DIR: {
|
||||
GC_VAR_TYPE: GC_TYPE_DIRECTORY
|
||||
},
|
||||
GC_ENABLE_DASA: {
|
||||
GC_VAR_TYPE: GC_TYPE_BOOLEAN
|
||||
},
|
||||
GC_NO_BROWSER: {
|
||||
GC_VAR_TYPE: GC_TYPE_BOOLEAN
|
||||
},
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"_class": "OAuth2Credentials",
|
||||
"_module": "oauth2client.client",
|
||||
"access_token": "",
|
||||
"client_id": "118850122376-72t6r2666n5rbjlfebftqat5qjai2def.apps.googleusercontent.com",
|
||||
"client_secret": "",
|
||||
"invalid": false,
|
||||
"refresh_token": "",
|
||||
"token_expiry": "2010-04-17T15:18:45Z",
|
||||
"token_uri": "https://accounts.google.com/o/oauth2/token",
|
||||
"user_agent": ""
|
||||
}
|
||||
BIN
src/travis/creds.tar.enc
Normal file
BIN
src/travis/creds.tar.enc
Normal file
Binary file not shown.
@@ -34,8 +34,10 @@ else
|
||||
mkdir python
|
||||
echo "RUNNING: apt update..."
|
||||
sudo apt-get -qq --yes update > /dev/null
|
||||
echo "RUNNING: apt dist-upgrade..."
|
||||
sudo apt-get -qq --yes dist-upgrade > /dev/null
|
||||
echo "RUNNING: apt upgrade..."
|
||||
sudo apt-mark hold openssh-server
|
||||
sudo apt-get --yes upgrade
|
||||
sudo apt-get --yes --with-new-pkgs upgrade
|
||||
echo "Installing build tools..."
|
||||
sudo apt-get -qq --yes install build-essential
|
||||
echo "Installing deps for python3"
|
||||
@@ -72,7 +74,8 @@ else
|
||||
echo "running configure with safe and unsafe"
|
||||
./configure $safe_flags $unsafe_flags > /dev/null
|
||||
fi
|
||||
make -j$cpucount PROFILE_TASK="-m test.regrtest --pgo -j$(( $cpucount * 2 ))" -s
|
||||
#make -j$cpucount PROFILE_TASK="-m test.regrtest --pgo -j$(( $cpucount * 2 ))" -s
|
||||
make -j$cpucount -s
|
||||
RESULT=$?
|
||||
echo "First make exited with $RESULT"
|
||||
if [ $RESULT != 0 ]; then
|
||||
@@ -94,9 +97,10 @@ else
|
||||
echo "Installing deps for StaticX..."
|
||||
if [ ! -d patchelf-$PATCHELF_VERSION ]; then
|
||||
echo "Downloading PatchELF $PATCHELF_VERSION"
|
||||
wget https://nixos.org/releases/patchelf/patchelf-$PATCHELF_VERSION/patchelf-$PATCHELF_VERSION.tar.bz2
|
||||
tar xf patchelf-$PATCHELF_VERSION.tar.bz2
|
||||
cd patchelf-$PATCHELF_VERSION
|
||||
wget https://github.com/NixOS/patchelf/archive/$PATCHELF_VERSION.tar.gz
|
||||
tar xf $PATCHELF_VERSION.tar.gz
|
||||
cd patchelf-$PATCHELF_VERSION/
|
||||
./bootstrap.sh
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,8 @@
|
||||
cd src
|
||||
echo "MacOS Version Info According to Python:"
|
||||
python -c "import platform; print(platform.mac_ver())"
|
||||
echo "Xcode versionn:"
|
||||
xcodebuild -version
|
||||
export gampath=dist/gam
|
||||
rm -rf $gampath
|
||||
$python -OO -m PyInstaller --clean --noupx --strip -F --distpath $gampath gam.spec
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
|
||||
cfg = json.load(sys.stdin)
|
||||
cfg['client_secret'] = os.getenv('client_secret')
|
||||
jid = os.getenv('jid')
|
||||
cfg['refresh_token'] = os.getenv('refresh_%s' % jid)
|
||||
gampath = os.getenv('gampath')
|
||||
out_file = os.path.join(gampath, 'oauth2.txt')
|
||||
with open(out_file, 'w') as f:
|
||||
json.dump(cfg, f)
|
||||
Reference in New Issue
Block a user