Skip to content

Commit

Permalink
Merge branch 'master' into mattermostGH-200-add-meeting-topic
Browse files Browse the repository at this point in the history
  • Loading branch information
hanzei authored Oct 28, 2021
2 parents fe95408 + 1ab9d0b commit 1a05822
Show file tree
Hide file tree
Showing 16 changed files with 131 additions and 47 deletions.
10 changes: 10 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,22 @@ workflows:
filters:
tags:
only: /^v.*/
- plugin-ci/test:
filters:
tags:
only: /^v.*/
- plugin-ci/coverage:
filters:
tags:
only: /^v.*/
requires:
- plugin-ci/test
- plugin-ci/build:
filters:
tags:
only: /^v.*/
requires:
- plugin-ci/test
- plugin-ci/deploy-ci:
filters:
branches:
Expand All @@ -40,6 +48,7 @@ workflows:
- plugin-ci/lint
- plugin-ci/coverage
- plugin-ci/build
- plugin-ci/test
- plugin-ci/deploy-release-github:
filters:
tags:
Expand All @@ -51,3 +60,4 @@ workflows:
- plugin-ci/lint
- plugin-ci/coverage
- plugin-ci/build
- plugin-ci/test
42 changes: 42 additions & 0 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: "CodeQL"

on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '30 0 * * 0'

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write

strategy:
fail-fast: false
matrix:
language: [ 'go', 'javascript' ]

steps:
- name: Checkout repository
uses: actions/checkout@v2

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ webapp/dist
# Mac
*.swp
.DS_Store

#IDEs
/.idea
/.vscode
25 changes: 2 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,11 @@
[![Release](https://img.shields.io/github/v/release/mattermost/mattermost-plugin-zoom)](https://github.com/mattermost/mattermost-plugin-zoom/releases/latest)
[![HW](https://img.shields.io/github/issues/mattermost/mattermost-plugin-zoom/Up%20For%20Grabs?color=dark%20green&label=Help%20Wanted)](https://github.com/mattermost/mattermost-plugin-zoom/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22Up+For+Grabs%22+label%3A%22Help+Wanted%22)


**Maintainer:** [@larkox](https://github.com/larkox)
**Co-Maintainer:** [@mickmister](https://github.com/mickmister)

Start and join voice calls, video calls and use screen sharing with your team members via Zoom.

## Usage & Setup Guide
### Commands
* **`/zoom start`** — Start a zoom meeting.
* **`/zoom disconnect`** — Disconnect from zoom (if OAuth is enabled).
* **`/zoom help`** — Display help message.

Check our [GitBook](https://mattermost.gitbook.io/plugin-zoom/) for further documentation about this plugin.
Start and join voice calls, video calls and use screen sharing with your team members via Zoom. Check our [GitBook](https://mattermost.gitbook.io/plugin-zoom/) for further documentation about this plugin.

## Development

This plugin contains both a server and web app portion.

Use `make dist` to build distributions of the plugin that you can upload to a Mattermost server for testing.

Use `make check-style` to check the style for the whole plugin.

### Server

Inside the `/server` directory, you will find the Go files that make up the server-side of the plugin. Within there, build the plugin like you would any other Go application.

### Web App

Inside the `/webapp` directory, you will find the JS and React files that make up the client-side of the plugin. Within there, modify files and components as necessary. Test your syntax by running `npm run build`.
This plugin contains both a server and web app portion. Read our documentation about the [Developer Workflow](https://developers.mattermost.com/extend/plugins/developer-workflow/) and [Developer Setup](https://developers.mattermost.com/extend/plugins/developer-setup/) for more information about developing and extending plugins.
2 changes: 1 addition & 1 deletion docs/installation/zoom-configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Zoom version 1.5 supports one authentication method for users to connect Matterm

* There are two types of OAuth Zoom Apps you can create. You can use either one with this Zoom plugin depending on your organization's security and UX preferences. \(**Account** or **User** Level Apps\)
* **Account-Level App**
* Users do not need to authorize their Zoom accounts before starting their first Zoom meeting. The only requirement is that their Mattermost account users the same email address as their Zoom account.
* Users do not need to authorize their Zoom accounts before starting their first Zoom meeting. The only requirement is that their Mattermost account uses the same email address as their Zoom account.
* Users cannot connect their Mattermost/Zoom accounts if their emails do not match.
* **User Level App**
* Each user will need to connect their Zoom account with their Mattermost account before they can use the integration. When they try to create a meeting for the first time, they'll receive a message to connect their account, and will need to click **Approve** on the pop-up confirmation notice.
Expand Down
5 changes: 2 additions & 3 deletions docs/installation/zoom-configuration/zoom-setup-oauth.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ description: Steps to configure your Zoom account to work with Mattermost

# Zoom Setup \(Account Level App\)

You can set the **OAuth ClientID** and **OAuth Secret**, generated by Zoom, and use it to create meetings and pull user data.
You can set the **OAuth ClientID** and **OAuth Secret**, generated by Zoom, and use it to create meetings and pull user data. Note that this requires admin permissions on your Zoom account.

**Account-level Apps** require **an admin to authorize access to all users accounts within the Zoom account**. Individual users in Mattermost are verified by checking their Mattermost email and requesting their Personal Meeting ID via the Zoom API. The user's emails in Zoom and Mattermost accounts should match up. If you prefer for each end user to authorize individually, you should create a Zoom [User-Level App](zoom-setup-user-level-app.md).
**Account-level apps** require **an admin to authorize access to all users accounts within the Zoom account**. Individual users in Mattermost are verified by checking their Mattermost email and requesting their Personal Meeting ID via the Zoom API. The user's emails in Zoom and Mattermost accounts should match up. If you prefer for each end user to authorize individually, you should create a Zoom [User-Level App](zoom-setup-user-level-app.md).

## Create an app for Mattermost

Expand Down Expand Up @@ -56,4 +56,3 @@ This plugin allows users to be deauthorized directly from Zoom, in order to comp
## Finish setting up Mattermost server

Follow the instructions for [Mattermost Setup](../mattermost-setup.md)

4 changes: 2 additions & 2 deletions plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"description": "Zoom audio and video conferencing plugin for Mattermost 5.2+.",
"homepage_url": "https://github.com/mattermost/mattermost-plugin-zoom",
"support_url": "https://github.com/mattermost/mattermost-plugin-zoom/issues",
"release_notes_url": "https://github.com/mattermost/mattermost-plugin-zoom/releases/tag/v1.5.0",
"release_notes_url": "https://github.com/mattermost/mattermost-plugin-zoom/releases/tag/v1.5.1",
"icon_path": "assets/profile.svg",
"version": "1.5.0",
"version": "1.5.1",
"min_server_version": "5.12.0",
"server": {
"executables": {
Expand Down
8 changes: 5 additions & 3 deletions server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,15 @@ func (p *Plugin) runDisconnectCommand(user *model.User) (string, error) {
if p.configuration.AccountLevelApp {
err := p.removeSuperUserToken()
if err != nil {
return "Could not disconnect, err=" + err.Error(), nil
return "Error disconnecting, " + err.Error(), nil
}
return "Successfully disconnected from Zoom.", nil
}

if err := p.disconnectOAuthUser(user.Id); err != nil {
return fmt.Sprintf("Failed to disconnect the user: %s", err.Error()), nil
err := p.disconnectOAuthUser(user.Id)

if err != nil {
return "Could not disconnect OAuth from zoom, " + err.Error(), nil
}

p.trackDisconnect(user.Id)
Expand Down
4 changes: 2 additions & 2 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (p *Plugin) completeUserOAuthToZoom(w http.ResponseWriter, r *http.Request)
}
}

client := zoom.NewOAuthClient(token, conf, p.siteURL, p.getZoomAPIURL(), p.configuration.AccountLevelApp)
client := zoom.NewOAuthClient(token, conf, p.siteURL, p.getZoomAPIURL(), p.configuration.AccountLevelApp, p)
user, appErr := p.API.GetUser(userID)
if appErr != nil {
http.Error(w, appErr.Error(), http.StatusInternalServerError)
Expand Down Expand Up @@ -274,7 +274,7 @@ func (p *Plugin) handleMeetingEnded(w http.ResponseWriter, r *http.Request, webh
),
}

post.Message = "I have ended the meeting."
post.Message = "The meeting has ended."
post.Props["meeting_status"] = zoom.WebhookStatusEnded
post.Props["attachments"] = []*model.SlackAttachment{&slackAttachment}

Expand Down
2 changes: 1 addition & 1 deletion server/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ var manifest = struct {
Version string
}{
ID: "zoom",
Version: "1.5.0",
Version: "1.5.1",
}
28 changes: 21 additions & 7 deletions server/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func (p *Plugin) getActiveClient(user *model.User) (Client, string, error) {
if token == nil {
return nil, message, errors.New("zoom app not connected")
}
return zoom.NewOAuthClient(token, p.getOAuthConfig(), p.siteURL, p.getZoomAPIURL(), true), "", nil
return zoom.NewOAuthClient(token, p.getOAuthConfig(), p.siteURL, p.getZoomAPIURL(), true, p), "", nil
}

// Oauth User Level
Expand All @@ -172,7 +172,7 @@ func (p *Plugin) getActiveClient(user *model.User) (Client, string, error) {

info.OAuthToken.AccessToken = plainToken
conf := p.getOAuthConfig()
return zoom.NewOAuthClient(info.OAuthToken, conf, p.siteURL, p.getZoomAPIURL(), false), "", nil
return zoom.NewOAuthClient(info.OAuthToken, conf, p.siteURL, p.getZoomAPIURL(), false, p), "", nil
}

// getOAuthConfig returns the Zoom OAuth2 flow configuration.
Expand All @@ -188,11 +188,6 @@ func (p *Plugin) getOAuthConfig() *oauth2.Config {
TokenURL: fmt.Sprintf("%v/oauth/token", zoomURL),
},
RedirectURL: fmt.Sprintf("%s/plugins/zoom/oauth2/complete", p.siteURL),
Scopes: []string{
"user:read",
"meeting:write",
"webinar:write",
"recording:write"},
}
}

Expand Down Expand Up @@ -226,3 +221,22 @@ func (p *Plugin) sendDirectMessage(userID string, message string) error {
_, err = p.API.CreatePost(post)
return err
}

func (p *Plugin) GetZoomSuperUserToken() (*oauth2.Token, error) {
token, err := p.getSuperuserToken()
if err != nil {
return nil, errors.Wrap(err, "could not get token")
}
if token == nil {
return nil, errors.New("zoom app not connected")
}
return token, nil
}

func (p *Plugin) SetZoomSuperUserToken(token *oauth2.Token) error {
err := p.setSuperUserToken(token)
if err != nil {
return errors.Wrap(err, "could not set token")
}
return nil
}
5 changes: 5 additions & 0 deletions server/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,15 @@ func (p *Plugin) fetchOAuthUserInfo(tokenKey, userID string) (*zoom.OAuthUserInf
}

func (p *Plugin) disconnectOAuthUser(userID string) error {
// according to the definition encoded would be nil
encoded, err := p.API.KVGet(zoomUserByMMID + userID)

if err != nil {
return errors.Wrap(err, "could not find OAuth user info")
}
if encoded == nil {
return errors.New("you are not connected to Zoom yet")
}

var info zoom.OAuthUserInfo
if err := json.Unmarshal(encoded, &info); err != nil {
Expand Down
6 changes: 6 additions & 0 deletions server/zoom/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/mattermost/mattermost-server/v5/model"
"github.com/pkg/errors"
"golang.org/x/oauth2"
)

// AuthError represents a Zoom authentication error
Expand All @@ -27,3 +28,8 @@ type Client interface {
GetMeeting(meetingID int) (*Meeting, error)
GetUser(user *model.User) (*User, *AuthError)
}

type PluginAPI interface {
GetZoomSuperUserToken() (*oauth2.Token, error)
SetZoomSuperUserToken(*oauth2.Token) error
}
29 changes: 26 additions & 3 deletions server/zoom/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ type OAuthClient struct {
siteURL string
apiURL string
isAccountLevel bool
api PluginAPI
}

// NewOAuthClient creates a new Zoom OAuthClient instance.
func NewOAuthClient(token *oauth2.Token, config *oauth2.Config, siteURL, apiURL string, isAccountLevel bool) Client {
return &OAuthClient{token, config, siteURL, apiURL, isAccountLevel}
func NewOAuthClient(token *oauth2.Token, config *oauth2.Config, siteURL, apiURL string, isAccountLevel bool, api PluginAPI) Client {
return &OAuthClient{token, config, siteURL, apiURL, isAccountLevel, api}
}

// GetUser returns the Zoom user via OAuth.
Expand Down Expand Up @@ -95,15 +96,37 @@ func (c *OAuthClient) GetMeeting(meetingID int) (*Meeting, error) {
}

func (c *OAuthClient) getUserViaOAuth(user *model.User) (*User, error) {
client := c.config.Client(context.Background(), c.token)
url := fmt.Sprintf("%s/users/me", c.apiURL)
if c.isAccountLevel {
url = fmt.Sprintf("%s/users/%s", c.apiURL, user.Email)
currentToken, err := c.api.GetZoomSuperUserToken()
if err != nil {
return nil, errors.Wrap(err, "error getting zoom super user token")
}

tokenSource := c.config.TokenSource(context.Background(), currentToken)
updatedToken, err := tokenSource.Token()
if err != nil {
return nil, errors.Wrap(err, "error getting token from token source")
}

if updatedToken.AccessToken != currentToken.AccessToken {
kvErr := c.api.SetZoomSuperUserToken(updatedToken)
if kvErr != nil {
return nil, errors.Wrap(kvErr, "error setting new token")
}
}

c.token = updatedToken
}

client := c.config.Client(context.Background(), c.token)

res, err := client.Get(url)
if err != nil {
return nil, errors.Wrap(err, "error fetching zoom user")
}

defer res.Body.Close()

if res.StatusCode == http.StatusNotFound {
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/components/post_type_zoom/post_type_zoom.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export default class PostTypeZoom extends React.PureComponent {
);
}
} else if (props.meeting_status === 'ENDED') {
preText = 'I have ended the meeting';
preText = 'The meeting has ended';
if (this.props.fromBot) {
preText = `${this.props.creatorName} has ended the meeting`;
}
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/manifest.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This file is automatically generated. Do not modify it manually.

export const id = 'zoom';
export const version = '1.5.0';
export const version = '1.5.1';

0 comments on commit 1a05822

Please sign in to comment.