admission 0.4.5 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/admission.gemspec +7 -7
- data/lib/admission/version.rb +1 -1
- metadata +5 -48
- data/.gitignore +0 -40
- data/.rspec +0 -2
- data/.ruby-version +0 -1
- data/Gemfile +0 -7
- data/LICENSE +0 -674
- data/README.md +0 -24
- data/bin/build +0 -10
- data/bin/rspec +0 -8
- data/spec/integration/action_arbitrating_spec.rb +0 -119
- data/spec/integration/resource_arbitrating_spec.rb +0 -276
- data/spec/rspec_config.rb +0 -103
- data/spec/spec_helper.rb +0 -28
- data/spec/test_context/country.rb +0 -24
- data/spec/test_context/index.rb +0 -5
- data/spec/test_context/person.rb +0 -31
- data/spec/test_context/persons_fixtures.rb +0 -43
- data/spec/test_context/privileges_and_rules.rb +0 -119
- data/spec/unit/arbitration_spec.rb +0 -33
- data/spec/unit/index_spec.rb +0 -144
- data/spec/unit/privilege/order_definer_spec.rb +0 -184
- data/spec/unit/privilege_spec.rb +0 -178
- data/spec/unit/rails/action_admission_spec.rb +0 -188
- data/spec/unit/rails/controller_addon_spec.rb +0 -68
- data/spec/unit/rails/scope_resolver_spec.rb +0 -72
- data/spec/unit/resource_arbitration_spec.rb +0 -76
- data/spec/unit/status_spec.rb +0 -79
- data/visualisation/.babelrc +0 -7
- data/visualisation/actions/index.js +0 -0
- data/visualisation/components/app_container.jsx +0 -78
- data/visualisation/components/input_with_select.jsx +0 -177
- data/visualisation/components/nested_list_row.jsx +0 -48
- data/visualisation/components/privilege_select.jsx +0 -70
- data/visualisation/components/privileges_panel.jsx +0 -73
- data/visualisation/components/rules_panel.jsx +0 -124
- data/visualisation/dist/.gitkeep +0 -0
- data/visualisation/helpers.js +0 -69
- data/visualisation/index.jsx +0 -89
- data/visualisation/package.json +0 -27
- data/visualisation/reducers/index.js +0 -35
- data/visualisation/server.rb +0 -23
- data/visualisation/style.scss +0 -248
- data/visualisation/webpack.config.js +0 -47
- data/visualisation/yarn.lock +0 -3354
@@ -1,76 +0,0 @@
|
|
1
|
-
require_relative '../spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Admission::ResourceArbitration do
|
4
|
-
|
5
|
-
describe '#new' do
|
6
|
-
|
7
|
-
it 'parses simple Symbol scope' do
|
8
|
-
arbitration = Admission::ResourceArbitration.new nil, {scope: -1}, :req, :scope
|
9
|
-
expect(arbitration).to have_inst_vars(
|
10
|
-
person: nil,
|
11
|
-
rules_index: -1,
|
12
|
-
request: :req,
|
13
|
-
resource: nil
|
14
|
-
)
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'parses type scope' do
|
18
|
-
resource = Object.new
|
19
|
-
arbitration = Admission::ResourceArbitration.new nil, {objects: -1}, :req, resource
|
20
|
-
expect(arbitration).to have_inst_vars(
|
21
|
-
person: nil,
|
22
|
-
rules_index: -1,
|
23
|
-
request: :req,
|
24
|
-
resource: resource
|
25
|
-
)
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'parses nested type scope' do
|
29
|
-
resource = Object.new
|
30
|
-
arbitration = Admission::ResourceArbitration.new nil, {:'objects:vars' => -1}, :req, [resource, :vars]
|
31
|
-
expect(arbitration).to have_inst_vars(
|
32
|
-
person: nil,
|
33
|
-
rules_index: -1,
|
34
|
-
request: :req,
|
35
|
-
resource: resource
|
36
|
-
)
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
describe 'RulesBuilder' do
|
42
|
-
|
43
|
-
describe 'fails when given reserved action name' do
|
44
|
-
|
45
|
-
let(:builder){
|
46
|
-
builder = Admission::ResourceArbitration::RulesBuilder.new nil
|
47
|
-
builder.instance_variable_set '@privilege', 'privilege'
|
48
|
-
builder
|
49
|
-
}
|
50
|
-
|
51
|
-
it '#allow' do
|
52
|
-
expect{ builder.allow '', :allow }.not_to raise_exception
|
53
|
-
expect{ builder.allow '', Admission::ALL_ACTION }.to(
|
54
|
-
raise_exception("reserved action name #{Admission::ALL_ACTION}")
|
55
|
-
)
|
56
|
-
end
|
57
|
-
|
58
|
-
it '#forbid' do
|
59
|
-
expect{ builder.forbid '', :forbid }.not_to raise_exception
|
60
|
-
expect{ builder.forbid '', Admission::ALL_ACTION }.to(
|
61
|
-
raise_exception("reserved action name #{Admission::ALL_ACTION}")
|
62
|
-
)
|
63
|
-
end
|
64
|
-
|
65
|
-
it '#allow_resource' do
|
66
|
-
expect{ builder.allow_resource :'', :allow_resource, &->{} }.not_to raise_exception
|
67
|
-
expect{ builder.allow_resource :'', Admission::ALL_ACTION, &->{} }.to(
|
68
|
-
raise_exception("reserved action name #{Admission::ALL_ACTION}")
|
69
|
-
)
|
70
|
-
end
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
data/spec/unit/status_spec.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
require_relative '../spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Admission::Status do
|
4
|
-
|
5
|
-
def privilege context
|
6
|
-
@fake_privilege_klass ||= Struct.new(:context, :inherited)
|
7
|
-
@fake_privilege_klass.new context
|
8
|
-
end
|
9
|
-
|
10
|
-
describe '#new' do
|
11
|
-
|
12
|
-
it 'sets privileges to nil' do
|
13
|
-
instance = Admission::Status.new :person, nil, :rules, :arbiter
|
14
|
-
expect(instance).to have_inst_vars(
|
15
|
-
person: :person,
|
16
|
-
privileges: nil,
|
17
|
-
rules: :rules,
|
18
|
-
arbiter: :arbiter
|
19
|
-
)
|
20
|
-
|
21
|
-
instance = Admission::Status.new :person, [], :rules, :arbiter
|
22
|
-
expect(instance).to have_inst_vars(
|
23
|
-
person: :person,
|
24
|
-
privileges: nil,
|
25
|
-
rules: :rules,
|
26
|
-
arbiter: :arbiter
|
27
|
-
)
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'sets privileges and freezes them' do
|
31
|
-
instance = Admission::Status.new :person, [:czech], :rules, :arbiter
|
32
|
-
expect(instance).to have_inst_vars(
|
33
|
-
person: :person,
|
34
|
-
privileges: [:czech],
|
35
|
-
rules: :rules,
|
36
|
-
arbiter: :arbiter
|
37
|
-
)
|
38
|
-
expect(instance.privileges).to be_frozen
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'sorts privileges by context' do
|
42
|
-
instance = Admission::Status.new :person, [
|
43
|
-
privilege(nil),
|
44
|
-
privilege(:czech),
|
45
|
-
privilege(15),
|
46
|
-
privilege(:czech),
|
47
|
-
privilege({a: 15}),
|
48
|
-
privilege({a: {f: 1}}),
|
49
|
-
privilege(nil),
|
50
|
-
privilege({a: 15}),
|
51
|
-
], :rules, :arbiter
|
52
|
-
expect(instance.privileges.map(&:context)).to eq([
|
53
|
-
nil, nil, :czech, :czech, 15, {:a=>15}, {:a=>15}, {:a=>{:f=>1}}
|
54
|
-
])
|
55
|
-
expect(instance.privileges).to be_frozen
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
describe '#allowed_in_contexts' do
|
61
|
-
|
62
|
-
it 'returns empty list for blank privileges' do
|
63
|
-
instance = Admission::Status.new :person, nil, :rules, :arbiter
|
64
|
-
expect(instance.allowed_in_contexts).to eq([])
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'lists only context for which any privilege allows it' do
|
68
|
-
priv1 = privilege text: '1'
|
69
|
-
priv2 = privilege text: '2'
|
70
|
-
rules = {can: {priv1 => true}}
|
71
|
-
instance = Admission::Status.new nil, [priv1, priv2], rules, Admission::Arbitration
|
72
|
-
|
73
|
-
list = instance.allowed_in_contexts :can
|
74
|
-
expect(list).to eq([priv1.context])
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
data/visualisation/.babelrc
DELETED
File without changes
|
@@ -1,78 +0,0 @@
|
|
1
|
-
import preact from 'preact';
|
2
|
-
import classnames from 'classnames';
|
3
|
-
|
4
|
-
import PrivilegesPanel from './privileges_panel';
|
5
|
-
import RulesPanel from "./rules_panel";
|
6
|
-
|
7
|
-
export default class AppContainer extends preact.Component {
|
8
|
-
|
9
|
-
constructor (props) {
|
10
|
-
super(props);
|
11
|
-
|
12
|
-
this.switchToPrivileges = this.changePanel.bind(this, 'privileges');
|
13
|
-
this.switchToRules = this.changePanel.bind(this, 'rules');
|
14
|
-
}
|
15
|
-
|
16
|
-
render ({app}, {loaded, load_fail, panel}) {
|
17
|
-
if (!loaded) return <div className="splash-message">
|
18
|
-
<code>... loading admission data ...</code>
|
19
|
-
</div>;
|
20
|
-
|
21
|
-
if (load_fail) return <div className="splash-message">
|
22
|
-
<h4>failed to load admission data</h4>
|
23
|
-
<code>{load_fail}</code>
|
24
|
-
</div>;
|
25
|
-
|
26
|
-
return <div className="admission-app-container">
|
27
|
-
<ul className="panels-list">
|
28
|
-
|
29
|
-
<li
|
30
|
-
onClick={this.switchToPrivileges}
|
31
|
-
className={classnames({'active': panel === 'privileges'})}>
|
32
|
-
Privileges Order
|
33
|
-
</li>
|
34
|
-
|
35
|
-
<li
|
36
|
-
onClick={this.switchToRules}
|
37
|
-
className={classnames({'active': panel === 'rules'})}>
|
38
|
-
Rules Listing
|
39
|
-
</li>
|
40
|
-
|
41
|
-
</ul>
|
42
|
-
|
43
|
-
{this.renderPanel()}
|
44
|
-
</div>;
|
45
|
-
}
|
46
|
-
|
47
|
-
componentDidMount () {
|
48
|
-
const store = this.props.app.store;
|
49
|
-
|
50
|
-
this.store_unsibscribe = store.subscribe(() => {
|
51
|
-
const state = store.getState();
|
52
|
-
this.setState({loaded: state.loaded, panel: state.panel});
|
53
|
-
});
|
54
|
-
|
55
|
-
setTimeout(this.props.onMounted, 0);
|
56
|
-
}
|
57
|
-
|
58
|
-
componentWillUnmount () {
|
59
|
-
this.store_unsibscribe();
|
60
|
-
}
|
61
|
-
|
62
|
-
changePanel(panel) {
|
63
|
-
this.props.app.store.dispatch({type: 'PANEL_CHANGE', panel: panel});
|
64
|
-
}
|
65
|
-
|
66
|
-
renderPanel () {
|
67
|
-
const app = this.props.app;
|
68
|
-
switch (this.state.panel) {
|
69
|
-
case 'privileges':
|
70
|
-
return <PrivilegesPanel app={app} />;
|
71
|
-
break;
|
72
|
-
|
73
|
-
case 'rules':
|
74
|
-
return <RulesPanel app={app}/>;
|
75
|
-
break;
|
76
|
-
}
|
77
|
-
}
|
78
|
-
}
|
@@ -1,177 +0,0 @@
|
|
1
|
-
import preact from 'preact';
|
2
|
-
import classnames from 'classnames';
|
3
|
-
import helpers from '../helpers';
|
4
|
-
|
5
|
-
export default class InputWithSelect extends preact.Component {
|
6
|
-
|
7
|
-
constructor (props) {
|
8
|
-
super(props);
|
9
|
-
|
10
|
-
this.state = {
|
11
|
-
text: props.defaultText || '',
|
12
|
-
matching: null,
|
13
|
-
};
|
14
|
-
|
15
|
-
this.setParentRef = ref => this.element = ref;
|
16
|
-
this.setListRef = ref => this.list = ref;
|
17
|
-
this.onKeyDown = this.onKeyDown.bind(this);
|
18
|
-
this.onTextChange = helpers.debounce(this.onTextChange.bind(this), 400);
|
19
|
-
this.toggleList = helpers.debounce(this.toggleList.bind(this), 400, true);
|
20
|
-
this.closeList = this.closeList.bind(this);
|
21
|
-
this.onSelected = this.onSelected.bind(this);
|
22
|
-
}
|
23
|
-
|
24
|
-
render ({placeholder}, {text, matching}) {
|
25
|
-
if (!matching) this.list = null;
|
26
|
-
|
27
|
-
return <div
|
28
|
-
ref={this.setParentRef}
|
29
|
-
className="select_box">
|
30
|
-
|
31
|
-
<div className="_inputs">
|
32
|
-
<input
|
33
|
-
type="text"
|
34
|
-
className="input_text"
|
35
|
-
placeholder={placeholder}
|
36
|
-
onKeyDown={this.onKeyDown}
|
37
|
-
value={text}/>
|
38
|
-
|
39
|
-
<button
|
40
|
-
type="button"
|
41
|
-
tabIndex="-1"
|
42
|
-
className="button"
|
43
|
-
onClick={this.toggleList}>
|
44
|
-
⌄
|
45
|
-
</button>
|
46
|
-
</div>
|
47
|
-
|
48
|
-
{matching && <DropdownList
|
49
|
-
ref={this.setListRef}
|
50
|
-
items={matching}
|
51
|
-
toSelect={this.onSelected}
|
52
|
-
toClose={this.closeList}
|
53
|
-
/>}
|
54
|
-
|
55
|
-
</div>;
|
56
|
-
}
|
57
|
-
|
58
|
-
componentDidMount () {
|
59
|
-
this._outside_click_listener = e => {
|
60
|
-
if (!this.element.contains(e.target) && this.list) {
|
61
|
-
this.closeList();
|
62
|
-
}
|
63
|
-
};
|
64
|
-
document.addEventListener('click', this._outside_click_listener);
|
65
|
-
}
|
66
|
-
|
67
|
-
componentWillUnmount () {
|
68
|
-
document.removeEventListener('click', this._outside_click_listener);
|
69
|
-
}
|
70
|
-
|
71
|
-
componentWillReceiveProps (new_props) {
|
72
|
-
if (new_props.defaultText !== this.props.defaultText) this.setState({text: new_props.defaultText});
|
73
|
-
}
|
74
|
-
|
75
|
-
onKeyDown (e) {
|
76
|
-
if (this.list && this.list.onKeyDown(e)) return;
|
77
|
-
if (e.keyCode === 13 && this.props.enterable) {
|
78
|
-
this.onSelected(e.target.value.trim());
|
79
|
-
return;
|
80
|
-
}
|
81
|
-
this.onTextChange(e);
|
82
|
-
}
|
83
|
-
|
84
|
-
onTextChange (e) {
|
85
|
-
const text = e.target.value.trim();
|
86
|
-
let matching = null;
|
87
|
-
if ((text && text !==this.state.text) || e.keyCode === 40) {
|
88
|
-
matching = this.filtered_items(text);
|
89
|
-
}
|
90
|
-
this.setState({matching, text});
|
91
|
-
}
|
92
|
-
|
93
|
-
toggleList () {
|
94
|
-
if (this.list) {
|
95
|
-
this.closeList();
|
96
|
-
|
97
|
-
} else {
|
98
|
-
this.setState({matching: this.filtered_items()});
|
99
|
-
}
|
100
|
-
}
|
101
|
-
|
102
|
-
closeList () {
|
103
|
-
this.setState({matching: null});
|
104
|
-
}
|
105
|
-
|
106
|
-
onSelected (value) {
|
107
|
-
this.setState({text: value, matching: null});
|
108
|
-
this.props.onSelect(value);
|
109
|
-
}
|
110
|
-
|
111
|
-
filtered_items (text) {
|
112
|
-
let items = this.props.all_items;
|
113
|
-
if (text) items = items.filter(value => value.startsWith(text));
|
114
|
-
if (this.props.nullable) items.unshift(null);
|
115
|
-
|
116
|
-
return items.length === 0 ? null : items;
|
117
|
-
}
|
118
|
-
|
119
|
-
}
|
120
|
-
|
121
|
-
class DropdownList extends preact.Component {
|
122
|
-
|
123
|
-
constructor (props) {
|
124
|
-
super(props);
|
125
|
-
this.state = {selected: -1};
|
126
|
-
}
|
127
|
-
|
128
|
-
render ({items, toSelect}, {selected}) {
|
129
|
-
return <div
|
130
|
-
className="_dropdown">
|
131
|
-
<ul>
|
132
|
-
{items.map((name, i) => <li
|
133
|
-
className={classnames({'selected': selected === i})}
|
134
|
-
onClick={() => toSelect(name)}>
|
135
|
-
{name === null ? '\u00A0' : name}
|
136
|
-
</li>)}
|
137
|
-
</ul>
|
138
|
-
</div>;
|
139
|
-
}
|
140
|
-
|
141
|
-
onKeyDown (e) {
|
142
|
-
switch (e.keyCode) {
|
143
|
-
case 40: // down
|
144
|
-
this.changeSelection(1);
|
145
|
-
return true;
|
146
|
-
break;
|
147
|
-
|
148
|
-
case 38: // up
|
149
|
-
this.changeSelection(-1);
|
150
|
-
return true;
|
151
|
-
break;
|
152
|
-
|
153
|
-
case 13: // enter
|
154
|
-
const selected = this.state.selected;
|
155
|
-
if (selected !== -1) {
|
156
|
-
this.props.toSelect(this.props.items[selected]);
|
157
|
-
return true;
|
158
|
-
}
|
159
|
-
break;
|
160
|
-
|
161
|
-
case 27: // escape
|
162
|
-
this.props.toClose();
|
163
|
-
return true;
|
164
|
-
break;
|
165
|
-
|
166
|
-
}
|
167
|
-
return false;
|
168
|
-
}
|
169
|
-
|
170
|
-
changeSelection (value) {
|
171
|
-
let selected = this.state.selected;
|
172
|
-
selected += value;
|
173
|
-
if (selected < 0) selected = 0;
|
174
|
-
if (selected >= this.props.items.length) selected = this.props.items.length -1;
|
175
|
-
this.setState({selected});
|
176
|
-
}
|
177
|
-
}
|
@@ -1,48 +0,0 @@
|
|
1
|
-
import preact from 'preact';
|
2
|
-
|
3
|
-
export default class NestedListRow extends preact.Component {
|
4
|
-
|
5
|
-
constructor (props) {
|
6
|
-
super(props);
|
7
|
-
|
8
|
-
this.state = {
|
9
|
-
unrolled: !!props.defaultUnrolled
|
10
|
-
};
|
11
|
-
|
12
|
-
this.toggleRollOut = this.toggleRollOut.bind(this);
|
13
|
-
}
|
14
|
-
|
15
|
-
render ({app, content, nestedRows}, {unrolled}) {
|
16
|
-
return <li>
|
17
|
-
<div onClick={this.toggleRollOut} className="nested-list-content">
|
18
|
-
{nestedRows && <span className="icon">
|
19
|
-
{unrolled ? '\u25B6' : '\u25BC'}
|
20
|
-
</span>
|
21
|
-
}
|
22
|
-
<span className="content">{content}</span>
|
23
|
-
</div>
|
24
|
-
{unrolled && nestedRows && <ul className="nested-list">{
|
25
|
-
nestedRows.map(row =>
|
26
|
-
<NestedListRow
|
27
|
-
app={app}
|
28
|
-
content={row.content}
|
29
|
-
nestedRows={row.nested_rows}
|
30
|
-
defaultUnrolled={this.props.defaultUnrolled}
|
31
|
-
/>
|
32
|
-
)
|
33
|
-
}</ul>}
|
34
|
-
</li>;
|
35
|
-
}
|
36
|
-
|
37
|
-
componentWillReceiveProps ({defaultUnrolled}) {
|
38
|
-
if (defaultUnrolled !== this.props.defaultUnrolled) {
|
39
|
-
this.setState({unrolled: !!defaultUnrolled});
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
toggleRollOut () {
|
44
|
-
this.setState({unrolled: !this.state.unrolled});
|
45
|
-
}
|
46
|
-
|
47
|
-
}
|
48
|
-
|