├── images └── app_block_example.png ├── CONTRIBUTING-ARCHIVED.md ├── README.md ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE.md └── CONTRIBUTING.md ├── LICENSE └── third-party-app-report.gs /images/app_block_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackhq/gsuite-oauth-third-party-app-report/HEAD/images/app_block_example.png -------------------------------------------------------------------------------- /CONTRIBUTING-ARCHIVED.md: -------------------------------------------------------------------------------- 1 | # ARCHIVED 2 | 3 | This project is `Archived` and is no longer actively maintained; 4 | We are not accepting contributions or Pull Requests. 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to Enforce Third Party Apps within G Suite 2 | 3 | Documentation is hosted in the [GitHub Wiki] 4 | 5 | [GitHub Wiki]: https://github.com/slackhq/gsuite-oauth-third-party-app-report/wiki 6 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Summary 2 | 3 | Describe the goal of this PR. Mention any related Issue numbers. 4 | 5 | ### Requirements (place an `x` in each `[ ]`) 6 | 7 | * [ ] I've read and understood the [Contributing Guidelines](https://github.com/slackhq/csp-html-webpack-plugin/blob/master/.github/contributing.md) and have done my best effort to follow them. 8 | * [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct). 9 | * [ ] I've written tests to cover the new code and functionality included in this PR. 10 | * [ ] I've read, agree to, and signed the [Contributor License Agreement (CLA)](https://cla-assistant.io/slackhq/csp-html-webpack-plugin). 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Slack Technologies, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | Describe your issue here. 4 | 5 | ### What type of issue is this? (place an `x` in one of the `[ ]`) 6 | - [ ] bug 7 | - [ ] enhancement (feature request) 8 | - [ ] question 9 | - [ ] documentation related 10 | - [ ] testing related 11 | - [ ] discussion 12 | 13 | ### Requirements (place an `x` in each of the `[ ]`) 14 | * [ ] I've read and understood the [Contributing guidelines](https://github.com/slackhq/csp-html-webpack-plugin/blob/master/.github/CONTRIBUTING.md) and have done my best effort to follow them. 15 | * [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct). 16 | * [ ] I've searched for any related issues and avoided creating a duplicate issue. 17 | 18 | --- 19 | 20 | ### Bug Report 21 | 22 | Filling out the following details about bugs will help us solve your issue sooner. 23 | 24 | #### Reproducible in: 25 | 26 | slackhq/csp-html-webpack-plugin version: 27 | 28 | node version: 29 | 30 | OS version(s): 31 | 32 | #### Steps to reproduce: 33 | 34 | 1. 35 | 2. 36 | 3. 37 | 38 | #### Expected result: 39 | 40 | What you expected to happen 41 | 42 | #### Actual result: 43 | 44 | What actually happened 45 | 46 | #### Attachments: 47 | 48 | Logs, screenshots, screencast, sample project, funny gif, etc. 49 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributors Guide 2 | 3 | Interested in contributing? Awesome! Before you do though, please read our 4 | [Code of Conduct](https://slackhq.github.io/code-of-conduct). We take it very seriously, and expect that you will as 5 | well. 6 | 7 | There are many ways you can contribute! :heart: 8 | 9 | ### Bug Reports and Fixes :bug: 10 | - If you find a bug, please search for it in the [Issues](https://github.com/slackhq/gsuite-oauth-third-party-app-report/issues), and if it isn't already tracked, 11 | [create a new issue](https://github.com/slackhq/gsuite-oauth-third-party-app-report/issues/new). Fill out the "Bug Report" section of the issue template. Even if an Issue is closed, feel free to comment and add details, it will still 12 | be reviewed. 13 | - Issues that have already been identified as a bug (note: able to reproduce) will be labelled `bug`. 14 | - If you'd like to submit a fix for a bug, [send a Pull Request](#creating_a_pull_request) and mention the Issue number. 15 | - Include tests that isolate the bug and verifies that it was fixed. 16 | 17 | ### New Features :bulb: 18 | - If you'd like to add new functionality to this project, describe the problem you want to solve in a [new Issue](https://github.com/slackhq/gsuite-oauth-third-party-app-report/issues/new). 19 | - Issues that have been identified as a feature request will be labelled `enhancement`. 20 | - If you'd like to implement the new feature, please wait for feedback from the project 21 | maintainers before spending too much time writing the code. In some cases, `enhancement`s may 22 | not align well with the project objectives at the time. 23 | 24 | ### Tests :mag:, Documentation :books:, Miscellaneous :sparkles: 25 | - If you'd like to improve the tests, you want to make the documentation clearer, you have an 26 | alternative implementation of something that may have advantages over the way its currently 27 | done, or you have any other change, we would be happy to hear about it! 28 | - If its a trivial change, go ahead and [send a Pull Request](#creating_a_pull_request) with the changes you have in mind. 29 | - If not, [open an Issue](https://github.com/slackhq/gsuite-oauth-third-party-app-report/issues/new) to discuss the idea first. 30 | 31 | If you're new to our project and looking for some way to make your first contribution, look for 32 | Issues labelled `good first contribution`. 33 | 34 | ## Requirements 35 | 36 | For your contribution to be accepted: 37 | 38 | - [x] You must have signed the [Contributor License Agreement (CLA)](https://cla-assistant.io/slackhq/gsuite-oauth-third-party-app-report). 39 | - [x] The test suite must be complete and pass. 40 | - [x] The changes must be approved by code review. 41 | - [x] Commits should be atomic and messages must be descriptive. Related issues should be mentioned by Issue number. 42 | 43 | If the contribution doesn't meet the above criteria, you may fail our automated checks or a maintainer will discuss it with you. You can continue to improve a Pull Request by adding commits to the branch from which the PR was created. 44 | 45 | [Interested in knowing more about about pull requests at Slack?](https://slack.engineering/on-empathy-pull-requests-979e4257d158#.awxtvmb2z) 46 | 47 | ## Creating a Pull Request 48 | 49 | 1. :fork_and_knife: Fork the repository on GitHub. 50 | 2. :runner: Clone/fetch your fork to your local development machine. It's a good idea to run the tests just 51 | to make sure everything is in order. 52 | 3. :herb: Create a new branch and check it out. 53 | 4. :crystal_ball: Make your changes and commit them locally. Magic happens here! 54 | 5. :arrow_heading_up: Push your new branch to your fork. (e.g. `git push username fix-issue-16`). 55 | 6. :inbox_tray: Open a Pull Request on github.com from your new branch on your fork to `master` in this 56 | repository/ 57 | -------------------------------------------------------------------------------- /third-party-app-report.gs: -------------------------------------------------------------------------------- 1 | var HIGH_RISK_ACCESS = [ 2 | "https://mail.google.com", 3 | "https://www.googleapis.com/auth/gmail.compose", 4 | "https://www.googleapis.com/auth/gmail.insert", 5 | "https://www.googleapis.com/auth/gmail.labels", 6 | "https://www.googleapis.com/auth/gmail.modify", 7 | "https://www.googleapis.com/auth/gmail.readonly", 8 | "https://www.googleapis.com/auth/gmail.send", 9 | "https://www.googleapis.com/auth/gmail.settings.basic", 10 | "https://www.googleapis.com/auth/gmail.settings.sharing", 11 | "https://www.googleapis.com/auth/drive", 12 | "https://www.googleapis.com/auth/drive.file", 13 | "https://www.googleapis.com/auth/drive.metadata", 14 | "https://www.googleapis.com/auth/drive.photos.readonly", 15 | "https://www.googleapis.com/auth/drive.readonly", 16 | "https://www.googleapis.com/auth/drive.scripts", 17 | "https://www.googleapis.com/auth/ediscovery", 18 | "https://www.googleapis.com/auth/ediscovery.readonly", 19 | "https://www.googleapis.com/auth/admin.directory.customer", 20 | "https://www.googleapis.com/auth/admin.directory.customer.readonly", 21 | "https://www.googleapis.com/auth/admin.directory.device.chromeos", 22 | "https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly", 23 | "https://www.googleapis.com/auth/admin.directory.device.mobile", 24 | "https://www.googleapis.com/auth/admin.directory.device.mobile.action", 25 | "https://www.googleapis.com/auth/admin.directory.device.mobile.readonly", 26 | "https://www.googleapis.com/auth/admin.directory.domain", 27 | "https://www.googleapis.com/auth/admin.directory.domain.readonly", 28 | "https://www.googleapis.com/auth/admin.directory.group", 29 | "https://www.googleapis.com/auth/admin.directory.group.member", 30 | "https://www.googleapis.com/auth/admin.directory.group.member.readonly", 31 | "https://www.googleapis.com/auth/admin.directory.group.readonly", 32 | "https://www.googleapis.com/auth/admin.directory.notifications", 33 | "https://www.googleapis.com/auth/admin.directory.orgunit", 34 | "https://www.googleapis.com/auth/admin.directory.orgunit.readonly", 35 | "https://www.googleapis.com/auth/admin.directory.resource.calendar", 36 | "https://www.googleapis.com/auth/admin.directory.resource.calendar.readonly", 37 | "https://www.googleapis.com/auth/admin.directory.rolemanagement", 38 | "https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly", 39 | "https://www.googleapis.com/auth/admin.directory.user", 40 | "https://www.googleapis.com/auth/admin.directory.user.alias", 41 | "https://www.googleapis.com/auth/admin.directory.user.alias.readonly", 42 | "https://www.googleapis.com/auth/admin.directory.user.readonly", 43 | "https://www.googleapis.com/auth/admin.directory.user.security", 44 | "https://www.googleapis.com/auth/admin.directory.userschema", 45 | "https://www.googleapis.com/auth/admin.directory.userschema.readonly", 46 | "https://www.googleapis.com/auth/admin.reports.audit.readonly", 47 | "https://www.googleapis.com/auth/admin.reports.usage.readonly" 48 | ]; 49 | 50 | //Get all users. Specify 'domain' to filter search to one domain 51 | function listAllUsers(cb) { 52 | var pageToken, page; 53 | do { 54 | page = AdminDirectory.Users.list({ 55 | domain: '', 56 | orderBy: 'givenName', 57 | maxResults: 500, 58 | pageToken: pageToken 59 | }); 60 | 61 | var users = page.users; 62 | if (users) { 63 | for (var i = 0; i < users.length; i++) { 64 | var user = users[i]; 65 | if (cb) { 66 | cb(user) 67 | } 68 | } 69 | } else { 70 | Logger.log('No users found.'); 71 | } 72 | pageToken = page.nextPageToken; 73 | } while (pageToken); 74 | } 75 | 76 | //Gets all users and tokens 77 | function step1() { 78 | var tokens = [] 79 | tokens.push([ 80 | 'primaryEmail', 81 | 'clientId', 82 | 'displayText', 83 | 'anonymous', 84 | 'nativeApp', 85 | 'userKey', 86 | 'scopes' 87 | ]); 88 | 89 | listAllUsers(function(user) { 90 | try { 91 | if (user.suspended) { 92 | Logger.log('[suspended] %s (%s)', user.name.fullName, user.primaryEmail); 93 | return; 94 | } 95 | 96 | var currentTokens = AdminDirectory.Tokens.list(user.primaryEmail); 97 | if (currentTokens && currentTokens.items && currentTokens.items.length) { 98 | for (var i = 0; i < currentTokens.items.length; i++) { 99 | var tok = currentTokens.items[i]; 100 | if (tok.nativeApp == false) { 101 | tokens.push([ 102 | user.primaryEmail, 103 | tok.clientId, 104 | tok.displayText, 105 | tok.anonymous, 106 | tok.nativeApp, 107 | tok.userKey, 108 | tok.scopes.join(' '), 109 | ]); 110 | } 111 | } 112 | } 113 | } catch (e) { 114 | Logger.log("[error] %s: %s", user.primaryEmail, e); 115 | } 116 | }); 117 | 118 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 119 | var sheet = ss.getSheetByName("OAuth Tokens") 120 | if (sheet == null) { 121 | sheet = ss.insertSheet("OAuth Tokens"); 122 | } else { 123 | sheet.clear(); 124 | } 125 | 126 | Logger.log('Tokens written to Sheet Users: %s', tokens.length); 127 | var dataRange = sheet.getRange(1, 1, tokens.length, tokens[0].length); 128 | dataRange.setValues(tokens); 129 | } 130 | 131 | //Get counts of token usage 132 | function step2() { 133 | var countsRows = []; 134 | countsRows.push([ 135 | "numInstalls", 136 | "displayText", 137 | "clientId", 138 | "highRisk", 139 | "scopes" 140 | ]) 141 | 142 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 143 | var sheetName = ss.getSheets()[0].getSheetName(); 144 | var sheet = ss.getSheetByName(sheetName); 145 | 146 | if (sheet == null || sheetName == 'Sheet1') { 147 | Logger.log('Did not find OAuth Tokens tab. Please run function "step1" or GAM in order to generate the user tokens') 148 | return; 149 | } 150 | 151 | var range = sheet.getDataRange(); 152 | var tokens = range.getValues(); 153 | tokens.shift(); //Remove header 154 | 155 | //Get counts of each token. Format [clientId = count] 156 | Logger.log('Counting tokens...'); 157 | var tokenInstallCount = tokens.reduce(function(sums, entry) { 158 | if(entry[4] == false){ 159 | sums[entry[1]] = (sums[entry[1]] || 0) + 1; 160 | } 161 | return sums; 162 | }, {}); 163 | 164 | Logger.log('Retrieving information associated with clientId...'); 165 | for (tokenRow in tokenInstallCount) { 166 | //Retrieve information associated with clientId 167 | var token = []; 168 | for (var i = 0; i < tokens.length; i++) { 169 | if (tokens[i][1] == tokenRow) { 170 | token = tokens[i]; 171 | break; 172 | } 173 | } 174 | if (token == null) { 175 | Logger.log("Error: token not found"); 176 | return; 177 | } 178 | //Check if scopes appear in HIGH_RISK_ACCESS 179 | var match = false; 180 | oauth_scopes = token[6].split(' '); 181 | if (HIGH_RISK_ACCESS.some(function(element) { 182 | return oauth_scopes.indexOf(element) >= 0; 183 | })) { 184 | match = true; 185 | } 186 | 187 | countsRows.push([ 188 | tokenInstallCount[tokenRow], 189 | token[2], 190 | token[1], 191 | match, 192 | token[6] 193 | ]) 194 | } 195 | 196 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 197 | var sheet = ss.getSheetByName("Counts") 198 | if (sheet == null) { 199 | sheet = ss.insertSheet("Counts"); 200 | } else { 201 | sheet.clear(); 202 | } 203 | 204 | var dataRange = sheet.getRange(1, 1, countsRows.length, countsRows[0].length); 205 | dataRange.setValues(countsRows); 206 | sheet.sort(1, false); 207 | 208 | Logger.log('Finished'); 209 | 210 | } 211 | --------------------------------------------------------------------------------