autumn 3.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +11 -0
- data/CHANGELOG +567 -0
- data/MANIFEST +110 -0
- data/README +1114 -0
- data/README.textile +1153 -0
- data/Rakefile +75 -0
- data/autumn.gemspec +44 -0
- data/bin/autumn +11 -0
- data/lib/autumn.rb +8 -0
- data/lib/autumn/authentication.rb +238 -0
- data/lib/autumn/channel_leaf.rb +107 -0
- data/lib/autumn/coder.rb +166 -0
- data/lib/autumn/console_boot.rb +10 -0
- data/lib/autumn/ctcp.rb +250 -0
- data/lib/autumn/daemon.rb +207 -0
- data/lib/autumn/datamapper_hacks.rb +290 -0
- data/lib/autumn/foliater.rb +231 -0
- data/lib/autumn/formatting.rb +236 -0
- data/lib/autumn/generator.rb +231 -0
- data/lib/autumn/genesis.rb +190 -0
- data/lib/autumn/inheritable_attributes.rb +162 -0
- data/lib/autumn/leaf.rb +738 -0
- data/lib/autumn/log_facade.rb +49 -0
- data/lib/autumn/misc.rb +87 -0
- data/lib/autumn/resources/daemons/Anothernet.yml +3 -0
- data/lib/autumn/resources/daemons/AustHex.yml +29 -0
- data/lib/autumn/resources/daemons/Bahamut.yml +67 -0
- data/lib/autumn/resources/daemons/Dancer.yml +3 -0
- data/lib/autumn/resources/daemons/GameSurge.yml +3 -0
- data/lib/autumn/resources/daemons/IRCnet.yml +3 -0
- data/lib/autumn/resources/daemons/Ithildin.yml +7 -0
- data/lib/autumn/resources/daemons/KineIRCd.yml +56 -0
- data/lib/autumn/resources/daemons/PTlink.yml +6 -0
- data/lib/autumn/resources/daemons/QuakeNet.yml +20 -0
- data/lib/autumn/resources/daemons/RFC1459.yml +158 -0
- data/lib/autumn/resources/daemons/RFC2811.yml +16 -0
- data/lib/autumn/resources/daemons/RFC2812.yml +36 -0
- data/lib/autumn/resources/daemons/RatBox.yml +25 -0
- data/lib/autumn/resources/daemons/Ultimate.yml +24 -0
- data/lib/autumn/resources/daemons/Undernet.yml +6 -0
- data/lib/autumn/resources/daemons/Unreal.yml +110 -0
- data/lib/autumn/resources/daemons/_Other.yml +7 -0
- data/lib/autumn/resources/daemons/aircd.yml +33 -0
- data/lib/autumn/resources/daemons/bdq-ircd.yml +3 -0
- data/lib/autumn/resources/daemons/hybrid.yml +38 -0
- data/lib/autumn/resources/daemons/ircu.yml +67 -0
- data/lib/autumn/resources/daemons/tr-ircd.yml +8 -0
- data/lib/autumn/script.rb +74 -0
- data/lib/autumn/speciator.rb +165 -0
- data/lib/autumn/stem.rb +919 -0
- data/lib/autumn/stem_facade.rb +176 -0
- data/lib/autumn/tool/bin.rb +301 -0
- data/lib/autumn/tool/create.rb +48 -0
- data/lib/autumn/tool/project_creator.rb +110 -0
- data/lib/autumn/version.rb +3 -0
- data/lib/skel/Rakefile +163 -0
- data/lib/skel/config/global.yml +2 -0
- data/lib/skel/config/seasons/testing/database.yml +4 -0
- data/lib/skel/config/seasons/testing/leaves.yml +9 -0
- data/lib/skel/config/seasons/testing/season.yml +2 -0
- data/lib/skel/config/seasons/testing/stems.yml +10 -0
- data/lib/skel/leaves/administrator/README +20 -0
- data/lib/skel/leaves/administrator/controller.rb +67 -0
- data/lib/skel/leaves/administrator/views/autumn.txt.erb +1 -0
- data/lib/skel/leaves/administrator/views/reload.txt.erb +11 -0
- data/lib/skel/leaves/insulter/README +17 -0
- data/lib/skel/leaves/insulter/controller.rb +65 -0
- data/lib/skel/leaves/insulter/views/about.txt.erb +1 -0
- data/lib/skel/leaves/insulter/views/help.txt.erb +1 -0
- data/lib/skel/leaves/insulter/views/insult.txt.erb +1 -0
- data/lib/skel/leaves/scorekeeper/README +34 -0
- data/lib/skel/leaves/scorekeeper/config.yml +2 -0
- data/lib/skel/leaves/scorekeeper/controller.rb +104 -0
- data/lib/skel/leaves/scorekeeper/helpers/general.rb +64 -0
- data/lib/skel/leaves/scorekeeper/models/channel.rb +12 -0
- data/lib/skel/leaves/scorekeeper/models/person.rb +14 -0
- data/lib/skel/leaves/scorekeeper/models/pseudonym.rb +11 -0
- data/lib/skel/leaves/scorekeeper/models/score.rb +14 -0
- data/lib/skel/leaves/scorekeeper/tasks/stats.rake +17 -0
- data/lib/skel/leaves/scorekeeper/views/about.txt.erb +1 -0
- data/lib/skel/leaves/scorekeeper/views/change.txt.erb +5 -0
- data/lib/skel/leaves/scorekeeper/views/history.txt.erb +11 -0
- data/lib/skel/leaves/scorekeeper/views/points.txt.erb +5 -0
- data/lib/skel/leaves/scorekeeper/views/usage.txt.erb +1 -0
- data/lib/skel/log/README +1 -0
- data/lib/skel/script/console +28 -0
- data/lib/skel/script/destroy +48 -0
- data/lib/skel/script/generate +48 -0
- data/lib/skel/shared/README +1 -0
- data/lib/skel/tmp/README +1 -0
- data/spec/authentication_spec.rb +328 -0
- data/spec/channel_leaf_spec.rb +142 -0
- data/spec/coder_spec.rb +146 -0
- data/spec/ctcp_spec.rb +222 -0
- data/spec/daemon_spec.rb +202 -0
- data/spec/datamapper_hacks_spec.rb +164 -0
- data/tasks/authors.rake +30 -0
- data/tasks/changelog.rake +18 -0
- data/tasks/copyright.rake +21 -0
- data/tasks/doc.rake +7 -0
- data/tasks/gem.rake +23 -0
- data/tasks/gem_installer.rake +76 -0
- data/tasks/install_dependencies.rake +6 -0
- data/tasks/manifest.rake +4 -0
- data/tasks/rcov.rake +23 -0
- data/tasks/release.rake +52 -0
- data/tasks/reversion.rake +8 -0
- data/tasks/setup.rake +24 -0
- data/tasks/spec.rake +7 -0
- data/tasks/yard.rake +4 -0
- metadata +188 -0
data/Rakefile
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
begin; require 'rubygems'; rescue LoadError; end
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/clean'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require 'time'
|
7
|
+
require 'date'
|
8
|
+
|
9
|
+
PROJECT_SPECS = FileList[
|
10
|
+
'spec/*.rb',
|
11
|
+
'lib/skel/spec/*.rb'
|
12
|
+
]
|
13
|
+
|
14
|
+
PROJECT_MODULE = 'Autumn'
|
15
|
+
PROJECT_README = 'README.markdown'
|
16
|
+
#PROJECT_RUBYFORGE_GROUP_ID = 3034
|
17
|
+
PROJECT_COPYRIGHT = [
|
18
|
+
"# Copyright (c) #{Time.now.year} Tim Morgan riscfuture@gmail.com",
|
19
|
+
"# Distributed under the same terms as the Ruby license.",
|
20
|
+
"# Portions of this code are copyright ©2004 David Heinemeier Hansson; ",
|
21
|
+
"# please see libs/inheritable_attributes.rb for more information.",
|
22
|
+
"# Autumn's package tasks are copyright 2009 Michael Fellinger m.fellinger@gmail.com;",
|
23
|
+
"# Distributed under the terms of the Ramaze license"
|
24
|
+
]
|
25
|
+
|
26
|
+
# To release the monthly version do:
|
27
|
+
# $ PROJECT_VERSION=2009.03 rake release
|
28
|
+
IGNORE_FILES = [/\.gitignore/]
|
29
|
+
|
30
|
+
GEMSPEC = Gem::Specification.new{|s|
|
31
|
+
s.name = 'autumn'
|
32
|
+
s.author = "Tim 'riscfuture' Morgan"
|
33
|
+
s.summary = "Autumn is a simple and modular irc framework"
|
34
|
+
s.description = s.summary
|
35
|
+
s.email = 'bougy.man@gmail.com'
|
36
|
+
s.homepage = 'http://github.com/bougyman/autumn'
|
37
|
+
s.platform = Gem::Platform::RUBY
|
38
|
+
s.version = (ENV['PROJECT_VERSION'] || Date.today.strftime("%Y.%m.%d"))
|
39
|
+
s.files = `git ls-files`.split("\n").sort.reject { |f| IGNORE_FILES.detect { |exp| f.match(exp) } }
|
40
|
+
s.has_rdoc = true
|
41
|
+
s.require_path = 'lib'
|
42
|
+
s.bindir = "bin"
|
43
|
+
s.executables = ["autumn"]
|
44
|
+
s.rubyforge_project = "pastr"
|
45
|
+
|
46
|
+
s.add_dependency('facets')
|
47
|
+
s.add_dependency('anise')
|
48
|
+
|
49
|
+
s.post_install_message = <<MESSAGE.strip
|
50
|
+
============================================================
|
51
|
+
|
52
|
+
Thank you for installing Autumn!
|
53
|
+
You can now create a new bot:
|
54
|
+
# autumn create yourbot
|
55
|
+
|
56
|
+
============================================================
|
57
|
+
MESSAGE
|
58
|
+
}
|
59
|
+
|
60
|
+
Dir['tasks/*.rake'].each{|f| import(f) }
|
61
|
+
|
62
|
+
task :default => [:spec]
|
63
|
+
|
64
|
+
CLEAN.include %w[
|
65
|
+
**/.*.sw?
|
66
|
+
*.gem
|
67
|
+
.config
|
68
|
+
**/*~
|
69
|
+
**/{data.db,cache.yaml}
|
70
|
+
*.yaml
|
71
|
+
pkg
|
72
|
+
rdoc
|
73
|
+
ydoc
|
74
|
+
*coverage*
|
75
|
+
]
|
data/autumn.gemspec
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{autumn}
|
5
|
+
s.version = "3.1.8"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Tim 'riscfuture' Morgan"]
|
9
|
+
s.date = %q{2009-05-03}
|
10
|
+
s.default_executable = %q{autumn}
|
11
|
+
s.description = %q{Autumn is a simple and modular irc framework}
|
12
|
+
s.email = %q{bougy.man@gmail.com}
|
13
|
+
s.executables = ["autumn"]
|
14
|
+
s.files = ["AUTHORS", "CHANGELOG", "MANIFEST", "README", "README.textile", "Rakefile", "autumn.gemspec", "bin/autumn", "lib/autumn.rb", "lib/autumn/authentication.rb", "lib/autumn/channel_leaf.rb", "lib/autumn/coder.rb", "lib/autumn/console_boot.rb", "lib/autumn/ctcp.rb", "lib/autumn/daemon.rb", "lib/autumn/datamapper_hacks.rb", "lib/autumn/foliater.rb", "lib/autumn/formatting.rb", "lib/autumn/generator.rb", "lib/autumn/genesis.rb", "lib/autumn/inheritable_attributes.rb", "lib/autumn/leaf.rb", "lib/autumn/log_facade.rb", "lib/autumn/misc.rb", "lib/autumn/resources/daemons/Anothernet.yml", "lib/autumn/resources/daemons/AustHex.yml", "lib/autumn/resources/daemons/Bahamut.yml", "lib/autumn/resources/daemons/Dancer.yml", "lib/autumn/resources/daemons/GameSurge.yml", "lib/autumn/resources/daemons/IRCnet.yml", "lib/autumn/resources/daemons/Ithildin.yml", "lib/autumn/resources/daemons/KineIRCd.yml", "lib/autumn/resources/daemons/PTlink.yml", "lib/autumn/resources/daemons/QuakeNet.yml", "lib/autumn/resources/daemons/RFC1459.yml", "lib/autumn/resources/daemons/RFC2811.yml", "lib/autumn/resources/daemons/RFC2812.yml", "lib/autumn/resources/daemons/RatBox.yml", "lib/autumn/resources/daemons/Ultimate.yml", "lib/autumn/resources/daemons/Undernet.yml", "lib/autumn/resources/daemons/Unreal.yml", "lib/autumn/resources/daemons/_Other.yml", "lib/autumn/resources/daemons/aircd.yml", "lib/autumn/resources/daemons/bdq-ircd.yml", "lib/autumn/resources/daemons/hybrid.yml", "lib/autumn/resources/daemons/ircu.yml", "lib/autumn/resources/daemons/tr-ircd.yml", "lib/autumn/script.rb", "lib/autumn/speciator.rb", "lib/autumn/stem.rb", "lib/autumn/stem_facade.rb", "lib/autumn/tool/bin.rb", "lib/autumn/tool/create.rb", "lib/autumn/tool/project_creator.rb", "lib/autumn/version.rb", "lib/skel/Rakefile", "lib/skel/config/global.yml", "lib/skel/config/seasons/testing/database.yml", "lib/skel/config/seasons/testing/leaves.yml", "lib/skel/config/seasons/testing/season.yml", "lib/skel/config/seasons/testing/stems.yml", "lib/skel/leaves/administrator/README", "lib/skel/leaves/administrator/controller.rb", "lib/skel/leaves/administrator/views/autumn.txt.erb", "lib/skel/leaves/administrator/views/reload.txt.erb", "lib/skel/leaves/insulter/README", "lib/skel/leaves/insulter/controller.rb", "lib/skel/leaves/insulter/views/about.txt.erb", "lib/skel/leaves/insulter/views/help.txt.erb", "lib/skel/leaves/insulter/views/insult.txt.erb", "lib/skel/leaves/scorekeeper/README", "lib/skel/leaves/scorekeeper/config.yml", "lib/skel/leaves/scorekeeper/controller.rb", "lib/skel/leaves/scorekeeper/helpers/general.rb", "lib/skel/leaves/scorekeeper/models/channel.rb", "lib/skel/leaves/scorekeeper/models/person.rb", "lib/skel/leaves/scorekeeper/models/pseudonym.rb", "lib/skel/leaves/scorekeeper/models/score.rb", "lib/skel/leaves/scorekeeper/tasks/stats.rake", "lib/skel/leaves/scorekeeper/views/about.txt.erb", "lib/skel/leaves/scorekeeper/views/change.txt.erb", "lib/skel/leaves/scorekeeper/views/history.txt.erb", "lib/skel/leaves/scorekeeper/views/points.txt.erb", "lib/skel/leaves/scorekeeper/views/usage.txt.erb", "lib/skel/log/README", "lib/skel/script/console", "lib/skel/script/destroy", "lib/skel/script/generate", "lib/skel/shared/README", "lib/skel/tmp/README", "spec/authentication_spec.rb", "spec/channel_leaf_spec.rb", "spec/coder_spec.rb", "spec/ctcp_spec.rb", "spec/daemon_spec.rb", "spec/datamapper_hacks_spec.rb", "tasks/authors.rake", "tasks/changelog.rake", "tasks/copyright.rake", "tasks/doc.rake", "tasks/gem.rake", "tasks/gem_installer.rake", "tasks/install_dependencies.rake", "tasks/manifest.rake", "tasks/rcov.rake", "tasks/release.rake", "tasks/reversion.rake", "tasks/setup.rake", "tasks/spec.rake", "tasks/yard.rake"]
|
15
|
+
s.has_rdoc = true
|
16
|
+
s.homepage = %q{http://github.com/bougyman/autumn}
|
17
|
+
s.post_install_message = %q{============================================================
|
18
|
+
|
19
|
+
Thank you for installing Autumn!
|
20
|
+
You can now create a new bot:
|
21
|
+
# autumn create yourbot
|
22
|
+
|
23
|
+
============================================================}
|
24
|
+
s.require_paths = ["lib"]
|
25
|
+
s.rubyforge_project = %q{pastr}
|
26
|
+
s.rubygems_version = %q{1.3.1}
|
27
|
+
s.summary = %q{Autumn is a simple and modular irc framework}
|
28
|
+
|
29
|
+
if s.respond_to? :specification_version then
|
30
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
31
|
+
s.specification_version = 2
|
32
|
+
|
33
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
34
|
+
s.add_runtime_dependency(%q<facets>, [">= 0"])
|
35
|
+
s.add_runtime_dependency(%q<anise>, [">= 0"])
|
36
|
+
else
|
37
|
+
s.add_dependency(%q<facets>, [">= 0"])
|
38
|
+
s.add_dependency(%q<anise>, [">= 0"])
|
39
|
+
end
|
40
|
+
else
|
41
|
+
s.add_dependency(%q<facets>, [">= 0"])
|
42
|
+
s.add_dependency(%q<anise>, [">= 0"])
|
43
|
+
end
|
44
|
+
end
|
data/bin/autumn
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
begin
|
3
|
+
require "pathname"
|
4
|
+
require Pathname.new(__FILE__).expand_path.dirname.join("..", "lib", "autumn")
|
5
|
+
require "autumn/tool/bin"
|
6
|
+
rescue LoadError
|
7
|
+
require "rubygems"
|
8
|
+
require "autumn"
|
9
|
+
require "autumn/tool/bin"
|
10
|
+
end
|
11
|
+
Autumn::Tool::Bin::Cmd.run(ARGV)
|
data/lib/autumn.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
# Defines the Autumn::Authentication class, which includes different
|
2
|
+
# authentication strategies available to leaves.
|
3
|
+
|
4
|
+
module Autumn
|
5
|
+
|
6
|
+
# Defines classes which each encapsulate a different strategy for
|
7
|
+
# authentication. When the +authentication+ option is specified (see the
|
8
|
+
# Autumn::Leaf class), the options given are used to choose the correct class
|
9
|
+
# within this module to serve as the authenticator for that leaf.
|
10
|
+
#
|
11
|
+
# These authentication strategies are used to ensure only authorized users
|
12
|
+
# have access to protected commands. Leaf authors can designate certain
|
13
|
+
# commands as protected.
|
14
|
+
#
|
15
|
+
# = Writing Your Own Authenticators
|
16
|
+
#
|
17
|
+
# When the Autumn::Leaf#authenticate method is called, it converts the symbol
|
18
|
+
# from snake_case to CamelCase, and looks for a class in this model. Thus, a
|
19
|
+
# call to <tt>authenticate :hostname</tt> would look for a class
|
20
|
+
# Autumn::Authentication::Hostname.
|
21
|
+
#
|
22
|
+
# To define your own authenticator, subclass Autumn::Authentication::Base as a
|
23
|
+
# new class in the Autumn::Authentication module. Implement the methods
|
24
|
+
# defined in the Autumn::Authentication::Base class docs, then adjust your
|
25
|
+
# configuration to use your new authenticator.
|
26
|
+
|
27
|
+
module Authentication
|
28
|
+
|
29
|
+
# The basic subclass for all authenticators. If you wish to write your own
|
30
|
+
# authenticator, you must subclass this class. You must at a minimum
|
31
|
+
# override the authenticate method. You should also override the initialize
|
32
|
+
# method if you need to store any options or other data for later use.
|
33
|
+
#
|
34
|
+
# The authentication module will become a stem listener, so see
|
35
|
+
# Autumn::Stem#add_listener for information on other methods you can
|
36
|
+
# implement.
|
37
|
+
|
38
|
+
class Base
|
39
|
+
|
40
|
+
# Stores the options for this authenticator and configures it for use.
|
41
|
+
|
42
|
+
def initialize(options={})
|
43
|
+
raise "You can only instantiate subclasses of this class."
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns true if the user is authorized, false if not. +sender+ is a
|
47
|
+
# sender hash as defined in the Autumn::Stem docs.
|
48
|
+
|
49
|
+
def authenticate(stem, channel, sender, leaf)
|
50
|
+
raise "Subclasses must override the Autumn::Authentication::Base#authenticate method."
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns a string to be displayed to a user who is not authorized to
|
54
|
+
# perform a command. Override this method to provide more specific hints
|
55
|
+
# to a user on what he can do to authorize himself (e.g., "Tell me your
|
56
|
+
# password").
|
57
|
+
|
58
|
+
def unauthorized
|
59
|
+
"You must be an administrator for this bot to do that."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Authenticates users by their privilege level in the channel they ran the
|
64
|
+
# command in.
|
65
|
+
#
|
66
|
+
# This is a quick, configuration-free way of protecting your leaf, so long
|
67
|
+
# as you trust the ops in your channel.
|
68
|
+
|
69
|
+
class Op < Base
|
70
|
+
|
71
|
+
# Creates a new authenticator. Pass a list of allowed privileges (as
|
72
|
+
# symbols) for the +privileges+ option. By default this class accepts ops,
|
73
|
+
# admins, and channel owners/founders as authorized.
|
74
|
+
|
75
|
+
def initialize(options={})
|
76
|
+
@privileges = options[:privileges]
|
77
|
+
@privileges ||= [ :operator, :oper, :op, :admin, :founder, :channel_owner ]
|
78
|
+
end
|
79
|
+
|
80
|
+
def authenticate(stem, channel, sender, leaf) # :nodoc:
|
81
|
+
# Returns true if the sender has any of the privileges listed below
|
82
|
+
not (@privileges & [ stem.privilege(channel, sender) ].flatten).empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
def unauthorized # :nodoc:
|
86
|
+
"You must be an op to do that."
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Authenticates by IRC nick. A list of allowed nicks is built on
|
91
|
+
# initialization, and anyone with that nick is allowed to use restricted
|
92
|
+
# commands.
|
93
|
+
#
|
94
|
+
# This is the most obvious approach to authentication, but it is hardly
|
95
|
+
# secure. Anyone can change their nick to an admin's nick once that admin
|
96
|
+
# logs out.
|
97
|
+
|
98
|
+
class Nick < Base
|
99
|
+
|
100
|
+
# Creates a new authenticator. Pass a single nick for the +nick+ option or
|
101
|
+
# an array of allowed nicks for the +nicks+ option. If neither option is
|
102
|
+
# set, an exception is raised.
|
103
|
+
|
104
|
+
def initialize(options={})
|
105
|
+
@nicks = options[:nick]
|
106
|
+
@nicks ||= options[:nicks]
|
107
|
+
raise "You must give the nick of an administrator to use nick-based authentication." if @nicks.nil?
|
108
|
+
@nicks = [ @nicks ] if @nicks.kind_of? String
|
109
|
+
end
|
110
|
+
|
111
|
+
def authenticate(stem, channel, sender, leaf) # :nodoc:
|
112
|
+
@nicks.include? sender[:nick]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Authenticates by the host portion of an IRC message. A hostmask is used to
|
117
|
+
# match the relevant portion of the address with a whitelist of accepted
|
118
|
+
# host addresses.
|
119
|
+
#
|
120
|
+
# This method can be a secure way of preventing unauthorized access if you
|
121
|
+
# choose an appropriately narrow hostmask. However, you must configure in
|
122
|
+
# advance the computers you may want to administrate your leaves from.
|
123
|
+
|
124
|
+
class Hostname < Base
|
125
|
+
|
126
|
+
# Creates a new authenticator. You provide a hostmask via the +hostmask+
|
127
|
+
# option -- either a Regexp with one capture (that captures the portion of
|
128
|
+
# the hostmask you are interested in), or a Proc, which takes a host as an
|
129
|
+
# argument and returns true if the host is authorized, false if not. If
|
130
|
+
# the +hostmask+ option is not provided, a standard hostmask regexp will
|
131
|
+
# be used. This regexp strips everything left of the first period; for the
|
132
|
+
# example hostmask "wsd1.ca.widgetcom.net", it would return
|
133
|
+
# "ca.widgetcom.net" to be used for comparison.
|
134
|
+
#
|
135
|
+
# You also provide an authorized host with the +host+ option, or a list of
|
136
|
+
# such hosts with the +hosts+ option. If neither is given, an exception is
|
137
|
+
# raised.
|
138
|
+
|
139
|
+
def initialize(options={})
|
140
|
+
@hostmask = options[:hostmask]
|
141
|
+
@hostmask ||= /^.+?\.(.+)$/
|
142
|
+
@hostmask = @hostmask.to_rx(false) if @hostmask.kind_of? String
|
143
|
+
if @hostmask.kind_of? Regexp then
|
144
|
+
mask = @hostmask
|
145
|
+
@hostmask = lambda do |host|
|
146
|
+
if matches = host.match(mask) then matches[1] else nil end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
@hosts = options[:host]
|
151
|
+
@hosts ||= options[:hosts]
|
152
|
+
raise "You must give the host address of an administrator to use nick-based authentication." unless @hosts
|
153
|
+
@hosts = [ @hosts ] unless @hosts.kind_of? Array
|
154
|
+
end
|
155
|
+
|
156
|
+
def authenticate(stem, channel, sender, leaf) # :nodoc:
|
157
|
+
@hosts.include? @hostmask.call(sender[:host])
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Authenticates by a password provided in secret. When a user PRIVMSG's the
|
162
|
+
# leaf the correct password, the leaf adds that user's nick to a list of
|
163
|
+
# authorized nicks. These credentials expire when the person changes his
|
164
|
+
# nick, logs out, leaves the channel, etc. They also expire if a certain
|
165
|
+
# amount of time passes without running any protected commands.
|
166
|
+
|
167
|
+
class Password < Base
|
168
|
+
# The default period of time that must occur with no use of protected
|
169
|
+
# commands after which a user's credentials expire.
|
170
|
+
DEFAULT_EXPIRE_TIME = 5*60
|
171
|
+
|
172
|
+
# Creates a new authenticator. You provide a valid password with the
|
173
|
+
# +password+ option. If that option is not provided, an exception is
|
174
|
+
# raised. You can pass a number of seconds to the +expire_time+ option;
|
175
|
+
# this is the amount of time that must pass with no protected commands for
|
176
|
+
# a nick's authorization to expire. If the +expire_time+ option is not
|
177
|
+
# given, a default value of five minutes is used.
|
178
|
+
|
179
|
+
def initialize(options={})
|
180
|
+
@password = options[:password]
|
181
|
+
@expire_time = options[:expire_time]
|
182
|
+
@expire_time ||= DEFAULT_EXPIRE_TIME
|
183
|
+
raise "You must provide a password to use password-based authentication" unless @password
|
184
|
+
@authorized_nicks = Hash.new { |hsh, key| hsh[key] = Set.new }
|
185
|
+
@last_protected_action = Hash.new { |hsh, key| hsh[key] = Hash.new(Time.at(0)) }
|
186
|
+
@an_lock = Mutex.new
|
187
|
+
end
|
188
|
+
|
189
|
+
def irc_privmsg_event(stem, sender, arguments) # :nodoc:
|
190
|
+
if arguments[:recipient] and arguments[:message] == @password then
|
191
|
+
@an_lock.synchronize do
|
192
|
+
@authorized_nicks[stem] << sender[:nick]
|
193
|
+
@last_protected_action[stem][sender[:nick]] = Time.now
|
194
|
+
#TODO values are not always deleted; this hash has the possibility to slowly grow and consume more memory
|
195
|
+
end
|
196
|
+
stem.message "Your password has been accepted, and you are now authorized.", sender[:nick]
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def irc_nick_event(stem, sender, arguments) # :nodoc:
|
201
|
+
@an_lock.synchronize do
|
202
|
+
revoke stem, sender[:nick]
|
203
|
+
revoke stem, arguments[:nick]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def irc_kick_event(stem, sender, arguments) # :nodoc:
|
208
|
+
@an_lock.synchronize { revoke stem, arguments[:nick] }
|
209
|
+
end
|
210
|
+
|
211
|
+
def irc_quit_event(stem, sender, arguments) # :nodoc:
|
212
|
+
@an_lock.synchronize { revoke stem, sender[:nick] }
|
213
|
+
end
|
214
|
+
|
215
|
+
def authenticate(stem, channel, sender, leaf) # :nodoc:
|
216
|
+
@an_lock.synchronize do
|
217
|
+
if Time.now - @last_protected_action[stem][sender[:nick]] > @expire_time then
|
218
|
+
revoke stem, sender[:nick]
|
219
|
+
else
|
220
|
+
@last_protected_action[stem][sender[:nick]] = Time.now
|
221
|
+
end
|
222
|
+
@authorized_nicks[stem].include? sender[:nick]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def unauthorized # :nodoc:
|
227
|
+
"You must authenticate with an administrator password to do that."
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
|
232
|
+
def revoke(stem, nick)
|
233
|
+
@authorized_nicks[stem].delete nick
|
234
|
+
@last_protected_action[stem].delete nick
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# Defines the Autumn::ChannelLeaf class, a subclass of Autumn::Leaf that
|
2
|
+
# selectively ignores channels.
|
3
|
+
|
4
|
+
module Autumn
|
5
|
+
|
6
|
+
# A special kind of leaf that only responds to messages sent to certain
|
7
|
+
# channels. Leaves that subclass ChannelLeaf can, in their config, specify a
|
8
|
+
# +channels+ option that narrows down which channels the leaf listens to. The
|
9
|
+
# leaf will not invoke the hook methods nor the <tt>*_command</tt> methods for
|
10
|
+
# IRC events that are not associated with those channels. It will respond to
|
11
|
+
# global, non-channel-specific events as well.
|
12
|
+
#
|
13
|
+
# You can combine multiple ChannelLeaf subclasses in one Stem to allow you to
|
14
|
+
# run two leaves off of one nick, but have the nick running different leaves
|
15
|
+
# in different channels.
|
16
|
+
#
|
17
|
+
# The +channels+ option should be a list of stems, and for each stem, a valid
|
18
|
+
# channel. For example, if you ran your leaf on two servers, your stems.yml
|
19
|
+
# file might look like:
|
20
|
+
#
|
21
|
+
# GamingServer:
|
22
|
+
# channels: fishinggames, games, drivinggames
|
23
|
+
# [...]
|
24
|
+
# FishingServer:
|
25
|
+
# channels: fishinggames, flyfishing
|
26
|
+
# [...]
|
27
|
+
#
|
28
|
+
# Now let's say you had a trivia leaf that asked questions about fishing
|
29
|
+
# games. You'd want to run that leaf on the "#fishinggames" channel of each
|
30
|
+
# server, and the "#games" channel of the GamingServer, but not the other
|
31
|
+
# channels. (Perhaps your Stem was also running other leaves relevant to those
|
32
|
+
# channels.) You'd set up your leaves.yml file like so:
|
33
|
+
#
|
34
|
+
# FishingGamesTrivia:
|
35
|
+
# channels:
|
36
|
+
# GamingServer:
|
37
|
+
# - fishinggames
|
38
|
+
# - games
|
39
|
+
# FishingServer: fishinggames
|
40
|
+
# [...]
|
41
|
+
#
|
42
|
+
# Now your leaf will only respond to messages relevant to the specified server
|
43
|
+
# channels (as well as global messages).
|
44
|
+
#
|
45
|
+
# Interception and filtering of messages is done at the _leaf_ level, not the
|
46
|
+
# _stem_ level. Therefore, for instance, if you override
|
47
|
+
# +someone_did_join_channel+, it will only be called for the appropriate
|
48
|
+
# channels; however, if you implement +irc_join_event+, it will still be
|
49
|
+
# called for all channels the stem is in.
|
50
|
+
|
51
|
+
class ChannelLeaf < Leaf
|
52
|
+
# The IRC channels that this leaf is responding to, mapped to server names.
|
53
|
+
attr :channels
|
54
|
+
|
55
|
+
# Creates a new instance. (See the Leaf class for more information.)
|
56
|
+
|
57
|
+
def will_start_up
|
58
|
+
@channels = Hash.new
|
59
|
+
@options[:channels] ||= Hash.new
|
60
|
+
@options[:channels].each do |server, chans|
|
61
|
+
stem = Foliater.instance.stems[server]
|
62
|
+
raise "Unknown stem #{server}" unless stem
|
63
|
+
chans = [ chans ] if chans.kind_of? String
|
64
|
+
@channels[stem] = chans.map { |chan| stem.normalized_channel_name chan }
|
65
|
+
end
|
66
|
+
super
|
67
|
+
end
|
68
|
+
|
69
|
+
def irc_privmsg_event(stem, sender, arguments) # :nodoc:
|
70
|
+
super if arguments[:channel].nil? or listening?(stem, arguments[:channel])
|
71
|
+
end
|
72
|
+
|
73
|
+
def irc_join_event(stem, sender, arguments) # :nodoc:
|
74
|
+
super if listening?(stem, arguments[:channel])
|
75
|
+
end
|
76
|
+
|
77
|
+
def irc_part_event(stem, sender, arguments) # :nodoc:
|
78
|
+
super if listening?(stem, arguments[:channel])
|
79
|
+
end
|
80
|
+
|
81
|
+
def irc_mode_event(stem, sender, arguments) # :nodoc:
|
82
|
+
super if arguments[:channel].nil? or listening?(stem, arguments[:channel])
|
83
|
+
end
|
84
|
+
|
85
|
+
def irc_topic_event(stem, sender, arguments) # :nodoc:
|
86
|
+
super if listening?(stem, arguments[:channel])
|
87
|
+
end
|
88
|
+
|
89
|
+
def irc_invite_event(stem, sender, arguments) # :nodoc:
|
90
|
+
super if listening?(stem, arguments[:channel]) or not stem.channels.include? arguments[:channel]
|
91
|
+
end
|
92
|
+
|
93
|
+
def irc_kick_event(stem, sender, arguments) # :nodoc:
|
94
|
+
super if listening?(stem, arguments[:channel])
|
95
|
+
end
|
96
|
+
|
97
|
+
def irc_notice_event(stem, sender, arguments) # :nodoc:
|
98
|
+
super if arguments[:channel].nil? or listening?(stem, arguments[:channel])
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def listening?(stem, channel)
|
104
|
+
@channels.include? stem and @channels[stem].include? channel
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|