Skip to content

Commit

Permalink
cEP-0015.md: corobo enhancement
Browse files Browse the repository at this point in the history
Closes #110
  • Loading branch information
nvzard committed Jul 29, 2018
1 parent 1d1a8a9 commit 9530c0c
Showing 1 changed file with 257 additions and 0 deletions.
257 changes: 257 additions & 0 deletions cEP-0015.md
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()
```

0 comments on commit 9530c0c

Please sign in to comment.