├── CHANGELOG.md ├── LICENSE ├── README-CN.md ├── README.md ├── app-babel.js ├── app-english.js ├── app.js ├── css ├── react-tagsinput.css └── style.css ├── english.html ├── highlight ├── README.md ├── highlight.pack.js └── styles │ ├── agate.css │ ├── androidstudio.css │ ├── arduino-light.css │ ├── arta.css │ ├── ascetic.css │ ├── atelier-cave-dark.css │ ├── atelier-cave-light.css │ ├── atelier-dune-dark.css │ ├── atelier-dune-light.css │ ├── atelier-estuary-dark.css │ ├── atelier-estuary-light.css │ ├── atelier-forest-dark.css │ ├── atelier-forest-light.css │ ├── atelier-heath-dark.css │ ├── atelier-heath-light.css │ ├── atelier-lakeside-dark.css │ ├── atelier-lakeside-light.css │ ├── atelier-plateau-dark.css │ ├── atelier-plateau-light.css │ ├── atelier-savanna-dark.css │ ├── atelier-savanna-light.css │ ├── atelier-seaside-dark.css │ ├── atelier-seaside-light.css │ ├── atelier-sulphurpool-dark.css │ ├── atelier-sulphurpool-light.css │ ├── atom-one-dark.css │ ├── atom-one-light.css │ ├── brown-paper.css │ ├── brown-papersq.png │ ├── codepen-embed.css │ ├── color-brewer.css │ ├── darcula.css │ ├── dark.css │ ├── darkula.css │ ├── default.css │ ├── docco.css │ ├── dracula.css │ ├── far.css │ ├── foundation.css │ ├── github-gist.css │ ├── github.css │ ├── googlecode.css │ ├── grayscale.css │ ├── gruvbox-dark.css │ ├── gruvbox-light.css │ ├── hopscotch.css │ ├── hybrid.css │ ├── idea.css │ ├── ir-black.css │ ├── kimbie.dark.css │ ├── kimbie.light.css │ ├── magula.css │ ├── mono-blue.css │ ├── monokai-sublime.css │ ├── monokai.css │ ├── obsidian.css │ ├── ocean.css │ ├── paraiso-dark.css │ ├── paraiso-light.css │ ├── pojoaque.css │ ├── pojoaque.jpg │ ├── purebasic.css │ ├── qtcreator_dark.css │ ├── qtcreator_light.css │ ├── railscasts.css │ ├── rainbow.css │ ├── routeros.css │ ├── school-book.css │ ├── school-book.png │ ├── solarized-dark.css │ ├── solarized-light.css │ ├── sunburst.css │ ├── tomorrow-night-blue.css │ ├── tomorrow-night-bright.css │ ├── tomorrow-night-eighties.css │ ├── tomorrow-night.css │ ├── tomorrow.css │ ├── vs.css │ ├── vs2015.css │ ├── xcode.css │ ├── xt256.css │ └── zenburn.css ├── index.html └── package.json /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.2.0 / 2018-05-15 2 | ================== 3 | 4 | * Add actions filter 5 | * Refine json style 6 | 7 | 1.1.1 / 2018-04-24 8 | ================== 9 | 10 | * Refine button style 11 | 12 | 1.1.0 / 2018-04-02 13 | ================== 14 | 15 | * Add oss:RestoreObject 16 | * Globalize 17 | 18 | 1.0.5 / 2016-05-11 19 | ================== 20 | 21 | * Add oss:ListBuckets 22 | 23 | 1.0.4 / 2016-04-15 24 | ================== 25 | 26 | * Fix a bug in enable path 27 | 28 | 1.0.3 / 2016-04-15 29 | ================== 30 | 31 | * Allow multiple values in condition 32 | 33 | 1.0.2 / 2016-04-15 34 | ================== 35 | 36 | * Add examples in README 37 | * Allow list objects under the last dir 38 | 39 | 1.0.1 / 2016-04-14 40 | ================== 41 | 42 | * Change to bootcdn 43 | * Add app version in page 44 | 45 | 1.0.0 / 2016-04-13 46 | ================== 47 | 48 | * First release 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 aliyun.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 6 | permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 9 | Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 12 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 14 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | # RAM Policy Editor 2 | 3 | ## [README of English](https://github.com/aliyun/ram-policy-editor/blob/master/README.md) 4 | 5 | ## 关于 6 | 7 | 可视化的OSS授权策略生成器。 8 | 9 | ## 示例 10 | 11 | [http://gosspublic.alicdn.com/ram-policy-editor/index.html](http://gosspublic.alicdn.com/ram-policy-editor/index.html) 12 | 13 | ## 用法 14 | 15 | ### Resources 16 | 17 | Resources有如下几种格式: 18 | 19 | - 表示某个bucket: `my-bucket` (此时对bucket下的文件没有权限) 20 | - 表示某个bucket下面所有文件: `my-bucket/*` (此时对bucket本身没有权限,例如ListObjects) 21 | - 表示某个bucket下某个目录: `my-bucket/dir` (此时对dir/下面的文件没有权限) 22 | - 表示某个bucket下某个目录下面所有文件: `my-bucket/dir/*` (此时对dir没有权限,例如ListObjects) 23 | - 填写完整的资源路径:`acs:oss:*:1234:my-bucket/dir`,其中`1234`为用户的User ID(在控制台查看) 24 | 25 | ### EnablePath 26 | 27 | 当用户需要对某个目录授权时,往往还需要保证对上一层目录也有List权限,例 28 | 如用户对`my-bucket/users/dir/*`赋予读写权限,为了在控制台(或其他工具) 29 | 能够查看这个目录,用户还需要以下权限: 30 | 31 | ``` 32 | ListObjects my-bucket 33 | ListObjects my-bucket/users 34 | ListObjects my-bucket/users/dir 35 | ``` 36 | 37 | 勾选EnablePath选项时,上面这些权限会自动添加。 38 | 39 | ### Examples 40 | 41 | #### Full access to a bucket 42 | 43 | 对某个bucket(例如`my-bucket`)完全授权: 44 | 45 | 添加一条规则: 46 | 47 | - Effect设置为`Allow` 48 | - Action选择`oss:*` 49 | - Resource填写为`my-bucket`和`my-bucket/*` 50 | - EnablePath不勾选 51 | 52 | 注意: 53 | 54 | - 如果是只读权限,把`oss:*`换成`oss:Get*`; 55 | - 如果是只写权限,把`oss:*`换成`oss:Put*`; 56 | 57 | #### Full access to a dir 58 | 59 | 对`my-bucket`下的`my-dir`完全授权: 60 | 61 | 添加一条规则: 62 | 63 | - Effect设置为`Allow` 64 | - Action选择`oss:*` 65 | - Resource填写为`my-bucket/my-dir/*` 66 | - EnablePath勾选 67 | 68 | 注意: 69 | 70 | - 如果是只读权限,把`oss:*`换成`oss:Get*`; 71 | - 如果是只写权限,把`oss:*`换成`oss:Put*`; 72 | 73 | #### Allow only specified IP 74 | 75 | 只允许特定的IP来访问`my-bucket`下面的`my-dir`目录。 76 | 77 | 添加一条规则: 78 | 79 | - Effect设置为`Allow` 80 | - Action选择`oss:Get*` 81 | - Resource填写为`my-bucket/my-dir/*` 82 | - EnablePath不勾选 83 | - 添加条件 84 | - Key选择为`acs:SourceIp` 85 | - Operator选择为`IpAddress` 86 | - Value填具体的IP,如`40.32.9.125` 87 | 88 | 注意: 89 | 90 | - 如果是只读权限,把`oss:*`换成`oss:Get*`; 91 | - 如果是只写权限,把`oss:*`换成`oss:Put*`; 92 | 93 | #### Web console 94 | 95 | 如果需要在阿里云的控制台用子账号访问,需要有`oss:ListBuckets`权限。 96 | 97 | 需要额外添加一条规则: 98 | 99 | - Effect设置为`Allow` 100 | - Action选择`oss:ListBuckets` 101 | - Resource填写为`*` 102 | - EnablePath不勾选 103 | 104 | ### Build 105 | 106 | ``` 107 | npm install -g browserify 108 | npm install 109 | npm run build 110 | ``` 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RAM Policy Editor 2 | 3 | ## [README of Chinese](https://github.com/aliyun/ram-policy-editor/blob/master/README-CN.md) 4 | 5 | ## About 6 | 7 | Visual RAM Policy Editor for OSS. 8 | 9 | ## Demo 10 | 11 | [http://gosspublic.alicdn.com/ram-policy-editor/index.html](http://gosspublic.alicdn.com/ram-policy-editor/index.html) 12 | 13 | ## Usage 14 | 15 | ### Resources 16 | 17 | Resources can be represented in the following formats: 18 | 19 | - A bucket: `my-bucket` (with no permission on objects in the bucket) 20 | - All objects in a bucket: `my-bucket/*` (with no permission on the bucket itself, such as ListObjects) 21 | - A directory in a bucket: `my-bucket/dir` (with no permission on objects under dir/) 22 | - All objects under a directory in a bucket: `my-bucket/dir/*` (with no permission on dir, such as ListObjects) 23 | - Complete resource path: `acs:oss:*:1234:my-bucket/dir`, where `1234` is the User ID (view the User ID in the console). 24 | 25 | ### EnablePath 26 | 27 | When you want to grant permissions to a directory, you also need to grant the List permission on its upper level directory. For example, if you want to grant read and write permissions to `my-bucket/users/dir/*`, you also need to grant the following permissions to 28 | view this directory in the console (or in other tools): 29 | 30 | ``` 31 | ListObjects my-bucket 32 | ListObjects my-bucket/users 33 | ListObjects my-bucket/users/dir 34 | ``` 35 | 36 | When the **EnablePath** option is selected, these permissions are granted automatically. 37 | 38 | ### Examples 39 | 40 | #### Full access to a bucket 41 | 42 | To grant all permissions to a bucket (such as `my-bucket`), add a rule as follows: 43 | 44 | - **Effect**: Select `Allow`. 45 | - **Actions**: Select `oss:*`. 46 | - **Resources**: Enter `my-bucket` and `my-bucket/*`. 47 | - **EnablePath**: Unselected. 48 | 49 | > **Note:** 50 | - For read-only permission, replace `oss:*` with `oss:Get*`. 51 | - For write-only permission, replace `oss:*` with `oss:Put*`. 52 | 53 | #### Full access to a directory 54 | 55 | To grant all permissions to `my-dir` in `my-bucket`, add a rule as follows: 56 | 57 | - **Effect**: Select `Allow`. 58 | - **Actions**: Select `oss:*`. 59 | - **Resources**: Enter `my-bucket/my-dir/*`. 60 | - **EnablePath**: Selected. 61 | 62 | > **Note:** 63 | - For read-only permission, replace `oss:*` with `oss:Get*`. 64 | - For write-only permission, replace `oss:*` with `oss:Put*`. 65 | 66 | #### Allow only specified IP 67 | 68 | To allow only specified IP addresses to access the `my-dir` directory in `my-bucket`, add a rule as follows: 69 | 70 | - **Effect**: Select `Allow`. 71 | - **Actions**: Select `oss:Get*`. 72 | - **Resources**: Enter `my-bucket/my-dir/*`. 73 | - **EnablePath**: Unselected. 74 | - **Conditions**: Click **Show** and add conditions as follows: 75 | - **Key**: Select `acs:SourceIp`. 76 | - **Operator**: Select `IpAddress`. 77 | - **Value**: Enter the IP address, such as `40.32.9.125`. 78 | 79 | > **Note:** 80 | - For read-only permission, replace `oss:*` with `oss:Get*`. 81 | - For write-only permission, replace `oss:*` with `oss:Put*`. 82 | 83 | #### Web Console 84 | 85 | To allow a RAM user to access the Alibaba Cloud console, you must grant the `oss:ListBuckets` permission by adding a rule as follows: 86 | 87 | - **Effect**: Select `Allow`. 88 | - **Actions**: Select `oss:ListBuckets`. 89 | - **Resources**: Enter `*`. 90 | - **EnablePath**: Unselected. 91 | 92 | ### Build 93 | 94 | ``` 95 | npm install -g browserify 96 | npm install 97 | npm run build 98 | ``` 99 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | var ReactDOM = require('react-dom'); 5 | var MultiSelect = require('react-bootstrap-multiselect'); 6 | var TagsInput = require('react-tagsinput'); 7 | 8 | var EffectList = [ 9 | 'Allow', 10 | 'Deny' 11 | ]; 12 | 13 | var ActionList = [ 14 | { 15 | label: 'General', 16 | children: [ 17 | 'oss:*', 18 | 'oss:Get*', 19 | 'oss:Put*', 20 | 'oss:List*' 21 | ], 22 | }, 23 | { 24 | label: 'Object', 25 | children: [ 26 | 'oss:GetObject', 27 | 'oss:PutObject', 28 | 'oss:DeleteObject', 29 | 'oss:GetObjectAcl', 30 | 'oss:PutObjectAcl', 31 | 'oss:ListParts', 32 | 'oss:AbortMultipartUpload', 33 | 'oss:ListObjects', 34 | ] 35 | }, 36 | { 37 | label: 'Bucket', 38 | children: [ 39 | 'oss:ListBuckets', 40 | 'oss:PutBucket', 41 | 'oss:DeleteBucket', 42 | 'oss:GetBucketLocation', 43 | 'oss:ListMultipartUploads', 44 | 'oss:PutBucketAcl', 45 | 'oss:GetBucketAcl', 46 | 'oss:PutBucketReferer', 47 | 'oss:GetBucketReferer', 48 | 'oss:PutBucketLogging', 49 | 'oss:GetBucketLogging', 50 | 'oss:DeleteBucketLogging', 51 | 'oss:PutBucketWebsite', 52 | 'oss:GetBucketWebsite', 53 | 'oss:DeleteBucketWebsite', 54 | 'oss:PutBucketLifecycle', 55 | 'oss:GetBucketLifecycle', 56 | 'oss:DeleteBucketLifecycle', 57 | 'oss:PutBucketCors', 58 | 'oss:GetBucketCors', 59 | 'oss:DeleteBucketCors', 60 | 'oss:PutBucketReplication', 61 | 'oss:GetBucketReplication', 62 | 'oss:DeleteBucketReplication', 63 | 'oss:GetBucketReplicationLocation', 64 | 'oss:GetBucketReplicationProgress' 65 | ]} 66 | ]; 67 | 68 | var ConditionList = [ 69 | 'acs:SourceIp', 70 | 'acs:UserAgent', 71 | 'acs:CurrentTime', 72 | 'acs:SecureTransport', 73 | 'oss:Prefix', 74 | 'oss:Delimiter' 75 | ]; 76 | 77 | var ConditionOpList = [ 78 | 'StringEquals', 79 | 'StringNotEquals', 80 | 'StringEqualsIgnoreCase', 81 | 'StringNotEqualsIgnoreCase', 82 | 'StringLike', 83 | 'StringNotLike', 84 | 'NumericEquals', 85 | 'NumericNotEquals', 86 | 'NumericLessThan', 87 | 'NumericLessThanEquals', 88 | 'NumericGreaterThan', 89 | 'NumericGreaterThanEquals', 90 | 'DateEquals', 91 | 'DateNotEquals', 92 | 'DateLessThan', 93 | 'DateLessThanEquals', 94 | 'DateGreaterThan', 95 | 'DateGreaterThanEquals', 96 | 'Bool', 97 | 'IpAddress', 98 | 'NotIpAddress' 99 | ]; 100 | 101 | var RuleEditor = React.createClass({ 102 | getInitialState: function () { 103 | return { 104 | Effect: 'Allow', 105 | Action: [], 106 | Resource: [], 107 | Condition: [], 108 | EnablePath: false, 109 | ShowCondEditor: false, 110 | Notice: '' 111 | }; 112 | }, 113 | handleEffectChange: function (e) { 114 | this.setState({Effect: e.target.value}); 115 | }, 116 | handleActionChange: function (e) { 117 | var actions = new Set(this.state.Action); 118 | if (e[0].selected) { 119 | actions.add(e[0].value); 120 | } else { 121 | actions.delete(e[0].value); 122 | } 123 | this.setState({Action: Array.from(actions)}); 124 | }, 125 | handleResourceChange: function (e) { 126 | this.setState({Resource: e}); 127 | }, 128 | handleEnablePathChange: function (e) { 129 | this.setState({EnablePath: e.target.checked}); 130 | }, 131 | handleConditionSubmit: function (e) { 132 | if (!e.condValue) { 133 | console.error('Invalid condition: %j', e); 134 | this.setState({Notice: 'ERROR: Condition value is empty!'}); 135 | return false; 136 | } 137 | 138 | var conds = this.state.Condition; 139 | var cond = { 140 | condId: Math.random(), 141 | condOp: e.condOp, 142 | condKey: e.condKey, 143 | condValue: e.condValue 144 | }; 145 | conds = conds.concat([cond]); 146 | this.setState({Condition: conds, Notice: ''}); 147 | return true; 148 | }, 149 | handleConditionRemove: function (id) { 150 | var conds = this.state.Condition.filter(function (x) { 151 | return x.condId != id; 152 | }); 153 | this.setState({Condition: conds}); 154 | }, 155 | showCondEditor: function (e) { 156 | e.preventDefault(); 157 | if (this.state.ShowCondEditor) { 158 | this.setState({ShowCondEditor: false}); 159 | } else { 160 | this.setState({ShowCondEditor: true}); 161 | } 162 | }, 163 | handleSubmit: function (e) { 164 | e.preventDefault(); 165 | 166 | var r = this.props.onRuleSubmit(this.state); 167 | if (!r) { 168 | this.setState({ 169 | Effect: EffectList[0], 170 | Action: [], 171 | Resource: [], 172 | Condition: [], 173 | Notice: '', 174 | EnablePath: false 175 | }); 176 | } else { 177 | this.setState({Notice: 'ERROR: ' + r}); 178 | } 179 | }, 180 | 181 | render: function () { 182 | var self = this; 183 | var selectEffect = EffectList.map(function (x) { 184 | return (); 185 | }); 186 | 187 | var selectAction = ActionList.map(function (group) { 188 | var selected = new Set(self.state.Action); 189 | return ({ 190 | label: group.label, 191 | children: group.children.map(function (x) { 192 | return ( 193 | {value: x, selected: selected.has(x)} 194 | ) 195 | }) 196 | }); 197 | }); 198 | 199 | return ( 200 |
201 |

添加规则

202 |
203 |
204 | 205 |
206 | 212 |
213 |
214 | 215 |
216 | 217 |
218 | 224 |
225 |
226 | 227 |
228 | 229 |
230 | 235 |
236 |
    237 |
  • 每添加一个Resource后按回车确认
  • 238 |
  • {'例子: my-bucket, my-bucket/dir/*'}
  • 239 |
  • 240 | 241 | More... 242 | 243 |
  • 244 |
245 |
246 |
247 |
248 | 249 |
250 | 251 |
252 |
253 | 262 |
263 |
264 |
265 | 266 |
267 | 268 |
269 | 274 | 279 |
280 |
281 | 282 |
283 | {this.state.Notice} 284 |
285 | 286 |
287 |
288 | 289 |
290 |
291 |
292 |
293 | ); 294 | } 295 | }); 296 | 297 | var Rule = React.createClass({ 298 | handleRuleRemove: function (e) { 299 | e.preventDefault(); 300 | this.props.onRuleRemove(this.props.ruleId); 301 | }, 302 | 303 | render: function () { 304 | var actions = this.props.actions.map(function (x) { 305 | return (
{x}
); 306 | }); 307 | 308 | var resources = this.props.resources.map(function (x) { 309 | return (
{x}
); 310 | }); 311 | 312 | var conds = this.props.conditions; 313 | var conditions = Object.keys(conds).map(function (k) { 314 | return ( 315 |
316 | {conds[k].condOp} 317 | ({conds[k].condKey} : {JSON.stringify(conds[k].condValue)}) 318 |
319 | ); 320 | }); 321 | return ( 322 | 323 | {this.props.effect} 324 | {actions} 325 | {resources} 326 | {conditions} 327 | 328 | 329 | 330 | 331 | 332 | 333 | ); 334 | } 335 | }); 336 | 337 | var RuleList = React.createClass({ 338 | render: function () { 339 | var self = this; 340 | var rules = self.props.data.Statement.map(function(r) { 341 | return ( 342 | 351 | ); 352 | }); 353 | return ( 354 |
355 |

规则列表

356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | {rules} 366 | 367 |
EffectActionsResourcesConditions
368 |
369 | ); 370 | } 371 | }); 372 | 373 | var PolicyView = React.createClass({ 374 | handleChange: function () { 375 | }, 376 | 377 | render: function () { 378 | var policy = {}; 379 | policy.Version = this.props.data.Version; 380 | policy.Statement = this.props.data.Statement.map(function (x) { 381 | var conds = {}; 382 | x.Condition.map(function (cond) { 383 | var value = conds[cond.condOp] || {}; 384 | value[cond.condKey] = cond.condValue; 385 | conds[cond.condOp] = value; 386 | }); 387 | 388 | return ({ 389 | Effect: x.Effect, 390 | Action: x.Action, 391 | Resource: x.Resource, 392 | Condition: conds 393 | }); 394 | }); 395 | 396 | return ( 397 |
398 |

授权策略

399 |