-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes #110
- Loading branch information
Showing
1 changed file
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
# corobo Enhancement (security, tests and configurability) | ||
|
||
| Metadata | | | ||
| -------- | -------------------------------------------------------- | | ||
| cEP | 15 | | ||
| Version | 1.0 | | ||
| Title | corobo Enhancement (security, tests and configurability) | | ||
| Authors | Nitanshu Vashistha <mailto:[email protected]> | | ||
| Status | Proposed | | ||
| Type | Process | | ||
|
||
## Abstract | ||
|
||
This cEP describes the details of enhancement of | ||
[corobo](https://github.com/coala/corobo) in terms of security, tests, | ||
configurability and the new plugins that are to be added to corobo as part | ||
of the | ||
[GSoC Project](https://summerofcode.withgoogle.com/projects/#6603667076022272). | ||
|
||
## Security | ||
|
||
Security has been one of the major concerns due to some past experiences. | ||
These are the changes that will be implemented to the existing plugins | ||
in order to harden the security. | ||
|
||
1. Give developers the right to invite newcomers to the organization. | ||
This will involve changing the bot command to `corobo invite <@username>`. | ||
|
||
2. Remove the automatic invite due to `Hello World` message. | ||
|
||
3. Remove `invite me` bot command. | ||
|
||
4. Stop newcomers from getting assigned to more than one newcomer issue. | ||
|
||
5. Make all LabHub plugins require being a member of the organization. This | ||
will be done by creating a `members_only` decorator to check if the user is | ||
a member of team or not. The decorator can be used on any bot command which | ||
needs to be under the accessibility of an organization member only. | ||
|
||
```py | ||
def members_only(func): | ||
@wraps(func) | ||
def wrapper(*args, **kwargs): | ||
# plugin instance | ||
instance = args[0] | ||
msg = args[1] | ||
user = msg.frm.nick | ||
for team in instance.team_mapping().values(): | ||
if team.is_member(user): | ||
yield from func(*args, **kwargs) | ||
return | ||
yield ('You need to be a member of this organization' | ||
' to use this command.') | ||
return wrapper | ||
``` | ||
|
||
6. Add callback message to warn users who are spamming the room. | ||
|
||
7. Add bot command to ban a user from all rooms at once. The goal of | ||
this feature is to prevent spamming as someone with many accounts | ||
can't be stopped to join a room. | ||
|
||
## Tests | ||
|
||
The default TestBase provided by Errbot is not enough for testing plugins like | ||
LabHub, which required intensive mocking. | ||
|
||
This will involve making changes upstream in | ||
[Errbot](https://github.com/errbotio/errbot/) and extend the existing testing | ||
infrastructure to implement better testing for plugins like LabHub. | ||
|
||
Errbot provides a pytest fixture `testbot` for testing purposes and to interact | ||
with the message queue. Extending the testbot and adding `inject_mock` method | ||
so that mocked objects can be injected into the plugin by passing a dictionary | ||
containing the `PluginName`, `field_name` and `Mocked Object`. The | ||
`inject_mock` method will inject a property on the plugin object and discard | ||
the assignments from the plugin itself. | ||
|
||
```py | ||
def test_command(testbot): | ||
mocks = {'PluginName': {'field_name1': fakeObj1, | ||
'field_name2': fakeObj2}} | ||
testbot.inject_mock(mocks) | ||
assert 'something' in testbot.exec_command('!my_command') | ||
``` | ||
|
||
## Configurability | ||
|
||
corobo has a potential to be used by other organizations for similar tasks like | ||
onboarding and automation. Currently, it is not configurable and many plugins | ||
are still very coala specific. Making it more configurable will allow other | ||
organizations to adapt corobo to cater their needs. | ||
|
||
Making existing coala specific plugins generic for other organizations and | ||
letting them configure the plugins as per their needs will ensure | ||
configurability. | ||
|
||
Errobot provides [plugin configuration](http://errbot.io/en/latest/user_guide/plugin_development/configuration.html) | ||
through the built-in `!config` command which can be used by other organizations | ||
to configure the plugins as per their needs. | ||
|
||
List of Plugins which are coala specific and can be generalized: | ||
|
||
1. `LabHub` plugin commands are meant to work for a coala specific team names | ||
maintainers, developers, newcomers and coala repositories. | ||
|
||
Issue Link: <https://github.com/coala/corobo/issues/382> | ||
|
||
2. `explain` currently uses hardcoded coala explanations. Configuring | ||
sub-directories `explain/genric` and `explain/<org name>` so that | ||
other organizations can include their culture-specific explanations. | ||
|
||
3. `searchdocs` uses API_DOCS and USER_DOCS links which are constant for coala. | ||
Other organizations will be able to initialize their own documentation | ||
links as per their requirement. | ||
|
||
Issue Link: <https://github.com/coala/corobo/issues/380> | ||
|
||
4. `community_stats` will be moved out from LabHub as a separate plugin since | ||
it can also be used by other organizations for analytics overview. | ||
|
||
Issue Link: <https://github.com/coala/corobo/issues/361> | ||
|
||
5. `wolfram_alpha` plugin currently uses an environment variable, it will be | ||
modified to use the configuration template. | ||
|
||
Issue Link: <https://github.com/coala/corobo/issues/383> | ||
|
||
6. `answer` plugin use `ANSWER_END` environment variable as an endpoint of the | ||
answers microservice. It'll be configured so that other organizations can | ||
also adapt to it. | ||
|
||
Issue Link: <https://github.com/coala/corobo/issues/381> | ||
|
||
### Configuration Sample | ||
|
||
```py | ||
class LabHub(BotPlugin): | ||
def get_configuration_template(self): | ||
return {'GH_TEAM_NAMES': ['newcomers', 'developers', 'maintainers']} | ||
|
||
@botcmd | ||
def mycommand(self, mess, args): | ||
# oh I need my TEAM ! | ||
gh_teams = self.config['GH_TEAM_NAMES'] | ||
``` | ||
|
||
``` | ||
<BOT_PREFIX> plugin config LabHub {'GH_TEAM_NAMES': ['newbies', 'devs', 'admins']} | ||
``` | ||
|
||
Using the configuration templates means that the bot admin will have to | ||
configure the plugin every time the bot starts and since corobo is continuously | ||
deployed on each commit, reconfiguring again and again using the chat via a | ||
admin is not preferable. | ||
|
||
This issue can be handled by using a Mixin to pre-configure the plugins through | ||
`config.py` file. By using `DEFAULT_CONFIGS` in `config.py` file to hold the | ||
configuration dictionaries for the plugins. Eg: | ||
|
||
```py | ||
DEFAULT_CONFIG = { | ||
'LabHub': { | ||
'GH_TOKEN': os.getenv('GITHUB_TOKEN'), | ||
'GL_TOKEN': os.getenv('GITLAB_TOKEN'), | ||
'GH_ORG_NAME': os.getenv('GH_ORG_NAME'), | ||
}, | ||
'WolframAlpha': { | ||
'WA_APP_ID': os.getenv('WA_TOKEN'), | ||
}, | ||
} | ||
``` | ||
|
||
Configuration of the plugins will be done before their activation and if a | ||
plugin is already configured, the `DefaultConfigMixin` will autoconfigure the | ||
plugin from `DEFAULT_CONFIGS`. | ||
|
||
```py | ||
class DefaultConfigMixin: | ||
@property | ||
def _default_config(self) -> Optional[Mapping]: | ||
if (self.bot_config.DEFAULT_CONFIG and self.name | ||
in self.bot_config.DEFAULT_CONFIG): | ||
return self.bot_config.DEFAULT_CONFIG[self.name] | ||
|
||
def __init__(self, bot, name=None) -> None: | ||
super().__init__(bot, name = name) | ||
default_config = self._default_config | ||
if default_config and not self.config: | ||
super().configure(default_config) | ||
|
||
def get_configuration_template(self) -> Mapping: | ||
default_config = self._default_config | ||
if default_config: | ||
return default_config | ||
``` | ||
|
||
```py | ||
class MyPlugin(BotPlugin, DefaultConfigMixin): | ||
... | ||
``` | ||
|
||
Other possible ways to configure corobo were discussed [here](https://github.com/coala/corobo/issues/574). | ||
|
||
### Use of `activate` instead of `__init__` to configure plugins: | ||
|
||
Currently, most of the plugins make use of the `__init__` method for instead of | ||
`activate` for configuration. Since `__init__` is executed at load time its | ||
failure will cause the plugin to not show up. Configuring the plugins through | ||
the `activate` method will remove this possibility. | ||
|
||
Eg: labhub.py | ||
|
||
```py | ||
def __init__(self, bot, name=None): | ||
super().__init__(bot, name) | ||
|
||
teams = dict() | ||
try: | ||
gh = github3.login(token=os.environ.get('GH_TOKEN')) | ||
assert gh is not None | ||
except AssertionError: | ||
self.log.error('Cannot create github object, please check GH_TOKEN') | ||
else: | ||
self.GH3_ORG = gh.organization(self.GH_ORG_NAME) | ||
for team in self.GH3_ORG.teams(): | ||
teams[team.name] = team | ||
|
||
self._teams = teams | ||
|
||
self.IGH = GitHub(GitHubToken(os.environ.get('GH_TOKEN'))) | ||
self.IGL = GitLab(GitLabPrivateToken(os.environ.get('GL_TOKEN'))) | ||
|
||
self.REPOS = dict() | ||
``` | ||
|
||
```py | ||
def activate(self): | ||
|
||
teams = dict() | ||
try: | ||
gh = github3.login(token=os.environ.get('GH_TOKEN')) | ||
assert gh is not None | ||
except AssertionError: | ||
self.log.error('Cannot create github object, please check GH_TOKEN') | ||
else: | ||
self.GH3_ORG = gh.organization(self.GH_ORG_NAME) | ||
for team in self.GH3_ORG.teams(): | ||
teams[team.name] = team | ||
|
||
self._teams = teams | ||
|
||
self.IGH = GitHub(GitHubToken(os.environ.get('GH_TOKEN'))) | ||
self.IGL = GitLab(GitLabPrivateToken(os.environ.get('GL_TOKEN'))) | ||
|
||
super(LabHub, self).activate() | ||
``` |