wire-framework 0.1.4.26 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +201 -339
- data/README.md +16 -0
- data/lib/app.rb +38 -19
- data/lib/app/cache.rb +93 -84
- data/lib/app/db.rb +195 -196
- data/lib/app/file.rb +76 -72
- data/lib/app/history.rb +50 -49
- data/lib/app/history/svn.rb +43 -24
- data/lib/app/login.rb +23 -8
- data/lib/app/render.rb +16 -105
- data/lib/app/render/document.rb +57 -40
- data/lib/app/render/editor.rb +59 -44
- data/lib/app/render/error.rb +55 -35
- data/lib/app/render/instant.rb +71 -60
- data/lib/app/render/page.rb +104 -79
- data/lib/app/render/partial.rb +120 -99
- data/lib/app/render/style.rb +56 -42
- data/lib/app/repo.rb +141 -135
- data/lib/app/repo/svn.rb +203 -177
- data/lib/closet.rb +104 -92
- data/lib/closet/auth.rb +37 -53
- data/lib/closet/config.rb +34 -0
- data/lib/closet/context.rb +142 -98
- data/lib/closet/renderer.rb +44 -41
- data/lib/wire.rb +28 -16
- metadata +11 -40
- data/lib/closet/resource.rb +0 -20
data/lib/closet/auth.rb
CHANGED
@@ -1,57 +1,41 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2017 Bryan T. Meyers
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
##
|
1
16
|
|
2
17
|
module Wire
|
3
|
-
|
4
|
-
|
5
|
-
|
18
|
+
# Auth is a module for handling authorization
|
19
|
+
# @author Bryan T. Meyers
|
20
|
+
module Auth
|
6
21
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
actions = [:create, :read, :readAll, :update, :delete]
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
actions
|
31
|
-
end
|
32
|
-
|
33
|
-
# Setup auth for an App
|
34
|
-
# @param [Symbol] level the type of authz to perform
|
35
|
-
# @param [Proc] block setup for the authz
|
36
|
-
# @return [void]
|
37
|
-
def auth(level, &block)
|
38
|
-
$current_app[:auth] = { level: level }
|
39
|
-
unless (level == :any) || (block.nil?)
|
40
|
-
Docile.dsl_eval(self, &block)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Select handler for :app level of auth
|
45
|
-
# @param [Module] handler the type of authz to perform
|
46
|
-
# @return [void]
|
47
|
-
def handler(handler)
|
48
|
-
$current_app[:auth][:handler] = handler
|
49
|
-
end
|
50
|
-
|
51
|
-
# Select user for :user level of auth
|
52
|
-
# @return [void]
|
53
|
-
def user(user)
|
54
|
-
$current_app[:auth][:user] = user
|
55
|
-
end
|
56
|
-
end
|
22
|
+
# Get the allowed actions for the current URI
|
23
|
+
# @param [Hash] context the context for this request
|
24
|
+
# @return [Array] the allowed actions for this URI
|
25
|
+
def actions_allowed(context)
|
26
|
+
if context.config['auth_read_only']
|
27
|
+
[:read, :readAll]
|
28
|
+
elsif context.config['auth_user']
|
29
|
+
if context.user == context.config['auth_user']
|
30
|
+
[:create, :read, :readAll, :update, :delete]
|
31
|
+
else
|
32
|
+
[]
|
33
|
+
end
|
34
|
+
elsif context.config['auth_handler']
|
35
|
+
context.config['auth_handler'].actions_allowed(context)
|
36
|
+
else
|
37
|
+
[:create, :read, :readAll, :update, :delete]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
57
41
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2017 Bryan T. Meyers
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
##
|
16
|
+
|
17
|
+
require 'yaml'
|
18
|
+
|
19
|
+
module Wire
|
20
|
+
module Config
|
21
|
+
def self.read_config_dir(dir, callback)
|
22
|
+
configs = {}
|
23
|
+
Dir[File.join(dir, '*.yaml')].each do |entry|
|
24
|
+
name = File.basename(entry, '.yaml')
|
25
|
+
config = YAML.load_file(File.join(dir, entry))
|
26
|
+
if callback
|
27
|
+
config = callback.call(config)
|
28
|
+
end
|
29
|
+
configs[name] = config
|
30
|
+
end
|
31
|
+
configs
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/closet/context.rb
CHANGED
@@ -1,106 +1,150 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2017 Bryan T. Meyers
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
##
|
16
|
+
|
17
|
+
require 'json'
|
18
|
+
require 'rest-less'
|
19
|
+
|
1
20
|
module Wire
|
2
|
-
|
3
|
-
|
21
|
+
# Context is a class containing information related to the current request
|
22
|
+
# @author Bryan T. Meyers
|
23
|
+
|
24
|
+
class Context
|
25
|
+
#@!attribute [r] action
|
26
|
+
# @return [Symbol] the action
|
27
|
+
#@!attribute [r] app
|
28
|
+
# @return [Hash] the name of the Wire::App
|
29
|
+
#@!attribute [r] body
|
30
|
+
# @return [String] the unparsed body
|
31
|
+
#@!attribute [r] config
|
32
|
+
# @return [Hash] the Wire::App configuration
|
33
|
+
#@!attribute [r] json
|
34
|
+
# @return [Hash] the JSON parsed body
|
35
|
+
#@!attribute [r] query
|
36
|
+
# @return [Hash] the parsed query string
|
37
|
+
#@!attribute [r] query_string
|
38
|
+
# @return [String] the raw query string
|
39
|
+
#@!attribute [r] rack_env
|
40
|
+
# @return [Hash] the raw Rack environment
|
41
|
+
#@!attribute [r] referer
|
42
|
+
# @return [Array] the referer URI
|
43
|
+
#@!attribute [r] resource
|
44
|
+
# @return [Symbol] the name of the resource
|
45
|
+
#@!attribute [r] uri
|
46
|
+
# @return [Array] the complete URI
|
47
|
+
#@!attribute [r] user
|
48
|
+
# @return [String] the REMOTE_USER
|
49
|
+
#@!attribute [r] verb
|
50
|
+
# @return [Symbol] the HTTP verb
|
51
|
+
|
52
|
+
attr_reader :action, :app, :body, :rack_env, :json, :query,
|
53
|
+
:query_string, :referer, :resource, :type,
|
54
|
+
:uri, :user, :verb
|
55
|
+
attr_writer :referer
|
4
56
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# @return [Hash] the raw Rack environment of the current request
|
14
|
-
#@!attribute [r] json
|
15
|
-
# @return [Hash] the JSON parsed body of the current request
|
16
|
-
#@!attribute [r] query
|
17
|
-
# @return [Hash] the parsed query string of the current request
|
18
|
-
#@!attribute [r] query_string
|
19
|
-
# @return [String] the raw query string of the current request
|
20
|
-
#@!attribute [r] referer
|
21
|
-
# @return [Array] the referer URI of the current request
|
22
|
-
#@!attribute [r] resource
|
23
|
-
# @return [Symbol] the Wire::Resource configuration of the current request
|
24
|
-
#@!attribute [r] type
|
25
|
-
# @return [Module] the Wire::App of the current request
|
26
|
-
#@!attribute [r] uri
|
27
|
-
# @return [Array] the URI of the current request
|
28
|
-
#@!attribute [r] user
|
29
|
-
# @return [String] the REMOTE_USER of the current request
|
30
|
-
#@!attribute [r] verb
|
31
|
-
# @return [Symbol] the HTTP verb of the current request
|
57
|
+
# Maps HTTP verbs to actions
|
58
|
+
HTTP_ACTIONS = {
|
59
|
+
'GET': :read,
|
60
|
+
'HEAD': :read,
|
61
|
+
'POST': :create,
|
62
|
+
'PUT': :update,
|
63
|
+
'DELETE': :delete
|
64
|
+
}
|
32
65
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
66
|
+
# Maps HTTP verbs to Symbols
|
67
|
+
HTTP_VERBS = {
|
68
|
+
'GET': :get,
|
69
|
+
'HEAD': :head,
|
70
|
+
'POST': :post,
|
71
|
+
'PUT': :put,
|
72
|
+
'DELETE': :delete
|
73
|
+
}
|
37
74
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
75
|
+
# Add user info to session
|
76
|
+
# @param [Hash] env the Rack environment
|
77
|
+
# @return [Hash] the updated environment
|
78
|
+
def update_session(env)
|
79
|
+
user = env['HTTP_REMOTE_USER']
|
80
|
+
unless user.nil? or user.empty? or user.eql? 'nobody' or user.eql? '(null)'
|
81
|
+
env['rack.session']['user'] = user
|
82
|
+
end
|
83
|
+
env['REMOTE_USER'] = env['rack.session']['user'] ? env['rack.session']['user'] : nil
|
84
|
+
env
|
85
|
+
end
|
46
86
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
87
|
+
# Builds a new Context
|
88
|
+
# @param [Hash] env the Rack environment
|
89
|
+
# @return [Context] a new Context
|
90
|
+
def initialize(env)
|
91
|
+
@rack_env = update_session(env)
|
92
|
+
@user = env['rack.session']['user']
|
93
|
+
@verb = HTTP_VERBS[env['REQUEST_METHOD']]
|
94
|
+
@action = HTTP_ACTIONS[env['REQUEST_METHOD']]
|
95
|
+
@uri = env['REQUEST_URI'].split('?')[0].split('/')
|
96
|
+
if env['HTTP_REFERER']
|
97
|
+
@referer = env['HTTP_REFERER'].split('/')
|
98
|
+
else
|
99
|
+
@referer = ['http:', '', env['HTTP_HOST']].concat(@uri[1...@uri.length])
|
100
|
+
end
|
101
|
+
@config = $wire_apps[@uri[1]]
|
102
|
+
if @config
|
103
|
+
@app = @uri[1]
|
104
|
+
@resource = @uri[2]
|
105
|
+
@id = context.uri[3...context.uri.length].join('/')
|
106
|
+
else
|
107
|
+
throw Exception.new("App: #{@uri[1]} is Undefined")
|
108
|
+
end
|
109
|
+
@query = {}
|
110
|
+
env['QUERY_STRING'].split('&').each do |q|
|
111
|
+
param = q.split('=')
|
112
|
+
@query[param[0].to_sym] = param[1]
|
113
|
+
end
|
114
|
+
@query_string = env['QUERY_STRING']
|
115
|
+
if env['rack.input']
|
116
|
+
@body = env['rack.input'].read
|
117
|
+
begin
|
118
|
+
@json = JSON.parse_clean(@body)
|
119
|
+
rescue JSON::ParserError
|
120
|
+
$stderr.puts 'Warning: Failed to parse body as JSON'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
55
124
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
env['REMOTE_USER'] = env['rack.session']['user'] ? env['rack.session']['user'] : nil
|
65
|
-
env
|
66
|
-
end
|
125
|
+
CONVERT = {
|
126
|
+
create: :post,
|
127
|
+
read: :get,
|
128
|
+
readAll: :get,
|
129
|
+
update: :put,
|
130
|
+
delete: :delete
|
131
|
+
}
|
67
132
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
@resource = app[:resources][@uri[2]]
|
86
|
-
@type = app[:type]
|
87
|
-
else
|
88
|
-
throw Exception.new("App: #{@uri[1]} is Undefined")
|
89
|
-
end
|
90
|
-
@query = {}
|
91
|
-
env['QUERY_STRING'].split('&').each do |q|
|
92
|
-
param = q.split('=')
|
93
|
-
@query[param[0].to_sym] = param[1]
|
94
|
-
end
|
95
|
-
@query_string = env['QUERY_STRING']
|
96
|
-
if env['rack.input']
|
97
|
-
@body = env['rack.input'].read
|
98
|
-
begin
|
99
|
-
@json = JSON.parse_clean(@body)
|
100
|
-
rescue JSON::ParserError
|
101
|
-
$stderr.puts 'Warning: Failed to parse body as JSON'
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
133
|
+
# Proxy method used when forwarding requests
|
134
|
+
# @param [Symbol] method the action to use when forwarding
|
135
|
+
# @return [Response] a Rack Response triplet, or status code
|
136
|
+
def forward(method)
|
137
|
+
headers = { referer: @referer.join('/'),
|
138
|
+
remote_user: @user }
|
139
|
+
verb = CONVERT[method]
|
140
|
+
uri = "http://#{@app['remote']}/#{@uri[2]}"
|
141
|
+
if [:update, :read, :delete].include?(method)
|
142
|
+
uri += "/#{@uri[3...@uri.length].join('/')}"
|
143
|
+
end
|
144
|
+
uri += '?' + @query_string
|
145
|
+
body = [:create, :update].include?(method) ? @body : nil
|
146
|
+
$stderr.puts "#{verb.upcase}: Forward Request to #{uri}"
|
147
|
+
RL.request verb, uri, headers, body
|
148
|
+
end
|
149
|
+
end
|
106
150
|
end
|
data/lib/closet/renderer.rb
CHANGED
@@ -1,46 +1,49 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
##
|
2
|
+
# Copyright 2017 Bryan T. Meyers
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
##
|
16
|
+
|
17
|
+
require 'tilt'
|
18
|
+
require_relative 'config'
|
5
19
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def renderer(klass, &block)
|
11
|
-
$current_renderer = klass
|
12
|
-
$current_editor = nil
|
13
|
-
Docile.dsl_eval(self, &block)
|
14
|
-
end
|
20
|
+
module Wire
|
21
|
+
# Renderer is a module for mapping mime to rendering templates
|
22
|
+
# @author Bryan T. Meyers
|
23
|
+
module Renderer
|
15
24
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
if $current_editor
|
25
|
-
$editors[mime] = $current_editor
|
26
|
-
end
|
27
|
-
end
|
25
|
+
# Callback for handling partials
|
26
|
+
# @param [Hash] conf the raw configuration
|
27
|
+
# @return [Hash] post-processed configuration
|
28
|
+
def self.configure_partial(conf)
|
29
|
+
conf['partial'] = Tilt.new(conf['partial'], 1, { ugly: true })
|
30
|
+
conf
|
31
|
+
end
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
# Callback for handling templates
|
34
|
+
# @param [Hash] conf the raw configuration
|
35
|
+
# @return [Hash] post-processed configuration
|
36
|
+
def self.configure_template(conf)
|
37
|
+
conf['partial'] = Tilt.new(conf['file'], 1, { ugly: true })
|
38
|
+
conf
|
39
|
+
end
|
35
40
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
41
|
+
# Read all of the configs in './configs/apps'
|
42
|
+
# @return [void]
|
43
|
+
def self.read_configs
|
44
|
+
$wire_editors = Wire::Config.read_config_dir('config/editors', method(:configure_partial))
|
45
|
+
$wire_renderers = Wire::Config.read_config_dir('config/renderers', method(:configure_partial))
|
46
|
+
$wire_templates = Wire::Config.read_config_dir('config/templates', method(:configure_template))
|
47
|
+
end
|
48
|
+
end
|
46
49
|
end
|