Split out accessbot module
Change-Id: Ic1c894d3567e9590bd450e9fe942f7dc0c6701d6
This commit is contained in:
parent
f522fda83e
commit
bfee17bb52
@ -56,6 +56,7 @@ SOURCE_MODULES["https://github.com/puppet-community/puppet-module-puppetboard"]=
|
||||
|
||||
|
||||
# Add modules that should be part of the openstack-infra integration test here
|
||||
INTEGRATION_MODULES["https://git.openstack.org/openstack-infra/puppet-accessbot"]="origin/master"
|
||||
INTEGRATION_MODULES["https://git.openstack.org/openstack-infra/puppet-ansible"]="origin/master"
|
||||
INTEGRATION_MODULES["https://git.openstack.org/openstack-infra/puppet-asterisk"]="origin/master"
|
||||
INTEGRATION_MODULES["https://git.openstack.org/openstack-infra/puppet-bup"]="origin/master"
|
||||
|
@ -1,214 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# Copyright 2011, 2013-2014 OpenStack Foundation
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import ConfigParser
|
||||
import argparse
|
||||
import irc.client
|
||||
import logging
|
||||
import ssl
|
||||
import sys
|
||||
import time
|
||||
import yaml
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
|
||||
class SetAccess(irc.client.SimpleIRCClient):
|
||||
log = logging.getLogger("setaccess")
|
||||
|
||||
def __init__(self, config, noop, nick, password, server, port):
|
||||
irc.client.SimpleIRCClient.__init__(self)
|
||||
self.identify_msg_cap = False
|
||||
self.config = config
|
||||
self.nick = nick
|
||||
self.password = password
|
||||
self.server = server
|
||||
self.port = int(port)
|
||||
self.noop = noop
|
||||
self.channels = [x['name'] for x in self.config['channels']]
|
||||
self.current_channel = None
|
||||
self.current_list = []
|
||||
self.changes = []
|
||||
self.identified = False
|
||||
if self.port == 6697:
|
||||
factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
|
||||
self.connect(self.server, self.port, self.nick,
|
||||
connect_factory=factory)
|
||||
else:
|
||||
self.connect(self.server, self.port, self.nick)
|
||||
|
||||
def on_disconnect(self, connection, event):
|
||||
sys.exit(0)
|
||||
|
||||
def on_welcome(self, c, e):
|
||||
self.identify_msg_cap = False
|
||||
self.log.debug("Requesting identify-msg capability")
|
||||
c.cap('REQ', 'identify-msg')
|
||||
c.cap('END')
|
||||
|
||||
def on_cap(self, c, e):
|
||||
self.log.debug("Received cap response %s" % repr(e.arguments))
|
||||
if e.arguments[0] == 'ACK' and 'identify-msg' in e.arguments[1]:
|
||||
self.log.debug("identify-msg cap acked")
|
||||
self.identify_msg_cap = True
|
||||
self.log.debug("Identifying to nickserv")
|
||||
c.privmsg("nickserv", "identify %s " % self.password)
|
||||
|
||||
def on_privnotice(self, c, e):
|
||||
if not self.identify_msg_cap:
|
||||
self.log.debug("Ignoring message because identify-msg "
|
||||
"cap not enabled")
|
||||
return
|
||||
nick = e.source.split('!')[0]
|
||||
auth = e.arguments[0][0]
|
||||
msg = e.arguments[0][1:]
|
||||
if auth == '+' and nick == 'NickServ' and not self.identified:
|
||||
if msg.startswith('You are now identified'):
|
||||
self.identified = True
|
||||
self.advance()
|
||||
return
|
||||
if auth != '+' or nick != 'ChanServ':
|
||||
self.log.debug("Ignoring message from unauthenticated "
|
||||
"user %s" % nick)
|
||||
return
|
||||
self.failed = False
|
||||
self.advance(msg)
|
||||
|
||||
def _get_access_list(self, channel_name):
|
||||
ret = {}
|
||||
channel = None
|
||||
for c in self.config['channels']:
|
||||
if c['name'] == channel_name:
|
||||
channel = c
|
||||
if channel is None:
|
||||
raise Exception("Unknown channel %s" % (channel_name,))
|
||||
mask = ''
|
||||
for access, nicks in (self.config['global'].items() +
|
||||
channel.items()):
|
||||
if access == 'mask':
|
||||
mask = self.config['access'].get(nicks)
|
||||
continue
|
||||
flags = self.config['access'].get(access)
|
||||
if flags is None:
|
||||
continue
|
||||
for nick in nicks:
|
||||
ret[nick] = flags
|
||||
return mask, ret
|
||||
|
||||
def _get_access_change(self, current, target, mask):
|
||||
remove = ''
|
||||
add = ''
|
||||
change = ''
|
||||
for x in current:
|
||||
if x in '+-':
|
||||
continue
|
||||
if target:
|
||||
if x not in target:
|
||||
remove += x
|
||||
else:
|
||||
if x not in mask:
|
||||
remove += x
|
||||
for x in target:
|
||||
if x in '+-':
|
||||
continue
|
||||
if x not in current:
|
||||
add += x
|
||||
if remove:
|
||||
change += '-' + remove
|
||||
if add:
|
||||
change += '+' + add
|
||||
return change
|
||||
|
||||
def _get_access_changes(self):
|
||||
mask, target = self._get_access_list(self.current_channel)
|
||||
self.log.debug("Mask for %s: %s" % (self.current_channel, mask))
|
||||
self.log.debug("Target for %s: %s" % (self.current_channel, target))
|
||||
all_nicks = set()
|
||||
current = {}
|
||||
changes = []
|
||||
for nick, flags, msg in self.current_list:
|
||||
all_nicks.add(nick)
|
||||
current[nick] = flags
|
||||
for nick in target.keys():
|
||||
all_nicks.add(nick)
|
||||
for nick in all_nicks:
|
||||
change = self._get_access_change(current.get(nick, ''),
|
||||
target.get(nick, ''), mask)
|
||||
if change:
|
||||
changes.append('access #%s add %s %s' % (self.current_channel,
|
||||
nick, change))
|
||||
return changes
|
||||
|
||||
def advance(self, msg=None):
|
||||
if self.changes:
|
||||
if self.noop:
|
||||
for change in self.changes:
|
||||
self.log.info('NOOP: ' + change)
|
||||
self.changes = []
|
||||
else:
|
||||
change = self.changes.pop()
|
||||
self.log.info(change)
|
||||
self.connection.privmsg('chanserv', change)
|
||||
time.sleep(1)
|
||||
return
|
||||
if not self.current_channel:
|
||||
if not self.channels:
|
||||
self.connection.quit()
|
||||
return
|
||||
self.current_channel = self.channels.pop()
|
||||
self.current_list = []
|
||||
self.connection.privmsg('chanserv', 'access list #%s' %
|
||||
self.current_channel)
|
||||
time.sleep(1)
|
||||
return
|
||||
if msg.startswith('End of'):
|
||||
self.changes = self._get_access_changes()
|
||||
self.current_channel = None
|
||||
self.advance()
|
||||
return
|
||||
parts = msg.split()
|
||||
if parts[2].startswith('+'):
|
||||
self.current_list.append((parts[1], parts[2], msg))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='IRC channel access check')
|
||||
parser.add_argument('-c', dest='config', nargs=1,
|
||||
help='specify the config file')
|
||||
parser.add_argument('-l', dest='channels',
|
||||
default='/etc/irc/channels.yaml',
|
||||
help='path to the channel config')
|
||||
parser.add_argument('--noop', dest='noop',
|
||||
action='store_true',
|
||||
help="Don't make any changes")
|
||||
args = parser.parse_args()
|
||||
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(args.config)
|
||||
|
||||
channels = yaml.load(open(args.channels))
|
||||
|
||||
a = SetAccess(channels, args.noop,
|
||||
config.get('ircbot', 'nick'),
|
||||
config.get('ircbot', 'pass'),
|
||||
config.get('ircbot', 'server'),
|
||||
config.get('ircbot', 'port'))
|
||||
a.start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,154 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# Copyright 2011, 2013-2014 OpenStack Foundation
|
||||
# Copyright 2012 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import argparse
|
||||
import irc.client
|
||||
import logging
|
||||
import random
|
||||
import string
|
||||
import ssl
|
||||
import sys
|
||||
import time
|
||||
import yaml
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
class CheckAccess(irc.client.SimpleIRCClient):
|
||||
log = logging.getLogger("checkaccess")
|
||||
|
||||
def __init__(self, channels, nick, flags):
|
||||
irc.client.SimpleIRCClient.__init__(self)
|
||||
self.identify_msg_cap = False
|
||||
self.channels = channels
|
||||
self.nick = nick
|
||||
self.flags = flags
|
||||
self.current_channel = None
|
||||
self.current_list = []
|
||||
self.failed = True
|
||||
|
||||
def on_disconnect(self, connection, event):
|
||||
if self.failed:
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
def on_welcome(self, c, e):
|
||||
self.identify_msg_cap = False
|
||||
self.log.debug("Requesting identify-msg capability")
|
||||
c.cap('REQ', 'identify-msg')
|
||||
c.cap('END')
|
||||
|
||||
def on_cap(self, c, e):
|
||||
self.log.debug("Received cap response %s" % repr(e.arguments))
|
||||
if e.arguments[0] == 'ACK' and 'identify-msg' in e.arguments[1]:
|
||||
self.log.debug("identify-msg cap acked")
|
||||
self.identify_msg_cap = True
|
||||
self.advance()
|
||||
|
||||
def on_privnotice(self, c, e):
|
||||
if not self.identify_msg_cap:
|
||||
self.log.debug("Ignoring message because identify-msg "
|
||||
"cap not enabled")
|
||||
return
|
||||
nick = e.source.split('!')[0]
|
||||
auth = e.arguments[0][0]
|
||||
msg = e.arguments[0][1:]
|
||||
if auth != '+' or nick != 'ChanServ':
|
||||
self.log.debug("Ignoring message from unauthenticated "
|
||||
"user %s" % nick)
|
||||
return
|
||||
self.failed = False
|
||||
self.advance(msg)
|
||||
|
||||
def advance(self, msg=None):
|
||||
if not self.current_channel:
|
||||
if not self.channels:
|
||||
self.connection.quit()
|
||||
return
|
||||
self.current_channel = self.channels.pop()
|
||||
self.current_list = []
|
||||
self.connection.privmsg('chanserv', 'access list %s' %
|
||||
self.current_channel)
|
||||
time.sleep(1)
|
||||
return
|
||||
if msg.startswith('End of'):
|
||||
found = False
|
||||
for nick, flags, msg in self.current_list:
|
||||
if nick == self.nick and flags == self.flags:
|
||||
self.log.info('%s access ok on %s' %
|
||||
(self.nick, self.current_channel))
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
self.failed = True
|
||||
print ("%s does not have permissions on %s:" %
|
||||
(self.nick, self.current_channel))
|
||||
for nick, flags, msg in self.current_list:
|
||||
print msg
|
||||
print
|
||||
self.current_channel = None
|
||||
self.advance()
|
||||
return
|
||||
parts = msg.split()
|
||||
self.current_list.append((parts[1], parts[2], msg))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='IRC channel access check')
|
||||
parser.add_argument('-l', dest='config',
|
||||
default='/etc/accessbot/channels.yaml',
|
||||
help='path to the config file')
|
||||
parser.add_argument('-s', dest='server',
|
||||
default='chat.freenode.net',
|
||||
help='IRC server')
|
||||
parser.add_argument('-p', dest='port',
|
||||
default=6697,
|
||||
help='IRC port')
|
||||
parser.add_argument('nick',
|
||||
help='the nick for which access should be validated')
|
||||
args = parser.parse_args()
|
||||
|
||||
config = yaml.load(open(args.config))
|
||||
channels = []
|
||||
for channel in config['channels']:
|
||||
channels.append('#' + channel['name'])
|
||||
|
||||
access_level = None
|
||||
for level, names in config['global'].items():
|
||||
if args.nick in names:
|
||||
access_level = level
|
||||
if access_level is None:
|
||||
raise Exception("Unable to determine global access level for %s" %
|
||||
args.nick)
|
||||
flags = config['access'][access_level]
|
||||
|
||||
a = CheckAccess(channels, args.nick, flags)
|
||||
mynick = ''.join(random.choice(string.ascii_uppercase)
|
||||
for x in range(16))
|
||||
port = int(args.port)
|
||||
if port == 6697:
|
||||
factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
|
||||
a.connect(args.server, int(args.port), mynick,
|
||||
connect_factory=factory)
|
||||
else:
|
||||
a.connect(args.server, int(args.port), mynick)
|
||||
a.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,82 +0,0 @@
|
||||
# == Class: accessbot
|
||||
#
|
||||
class accessbot(
|
||||
$nick = '',
|
||||
$password = '',
|
||||
$server = '',
|
||||
$channel_file = '',
|
||||
) {
|
||||
|
||||
user { 'accessbot':
|
||||
ensure => present,
|
||||
home => '/home/accessbot',
|
||||
shell => '/bin/bash',
|
||||
gid => 'accessbot',
|
||||
managehome => true,
|
||||
require => Group['accessbot'],
|
||||
}
|
||||
|
||||
group { 'accessbot':
|
||||
ensure => present,
|
||||
}
|
||||
|
||||
# A lot of things need yaml, be conservative requiring this package to avoid
|
||||
# conflicts with other modules.
|
||||
if ! defined(Package['python-yaml']) {
|
||||
package { 'python-yaml':
|
||||
ensure => present,
|
||||
}
|
||||
}
|
||||
|
||||
exec { 'run_accessbot' :
|
||||
command => '/usr/local/bin/accessbot -c /etc/accessbot/accessbot.config -l /etc/accessbot/channels.yaml >> /var/log/accessbot/accessbot.log 2>&1',
|
||||
path => '/usr/local/bin:/usr/bin:/bin/',
|
||||
user => 'accessbot',
|
||||
refreshonly => true,
|
||||
subscribe => File['/etc/accessbot/channels.yaml'],
|
||||
require => [File['/etc/accessbot/channels.yaml'],
|
||||
File['/etc/accessbot/accessbot.config'],
|
||||
File['/usr/local/bin/accessbot']],
|
||||
}
|
||||
|
||||
file { '/etc/accessbot':
|
||||
ensure => directory,
|
||||
}
|
||||
|
||||
file { '/var/log/accessbot':
|
||||
ensure => directory,
|
||||
owner => 'accessbot',
|
||||
group => 'accessbot',
|
||||
mode => '0775',
|
||||
require => User['accessbot'],
|
||||
}
|
||||
|
||||
file { '/etc/accessbot/accessbot.config':
|
||||
ensure => present,
|
||||
content => template('accessbot/accessbot.config.erb'),
|
||||
group => 'accessbot',
|
||||
mode => '0440',
|
||||
owner => 'root',
|
||||
replace => true,
|
||||
require => User['accessbot'],
|
||||
}
|
||||
|
||||
file { '/etc/accessbot/channels.yaml':
|
||||
ensure => present,
|
||||
source => $channel_file,
|
||||
group => 'accessbot',
|
||||
mode => '0440',
|
||||
owner => 'root',
|
||||
replace => true,
|
||||
require => User['accessbot'],
|
||||
}
|
||||
|
||||
file { '/usr/local/bin/accessbot':
|
||||
ensure => present,
|
||||
source => 'puppet:///modules/accessbot/accessbot.py',
|
||||
mode => '0555',
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
replace => true,
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
[ircbot]
|
||||
nick=<%= nick %>
|
||||
pass=<%= password %>
|
||||
server=<%= server %>
|
||||
port=6697
|
Loading…
x
Reference in New Issue
Block a user