diff --git a/src/adapter-middleware.coffee b/src/adapter-middleware.coffee
index 9a1ce94..a14e5f0 100644
--- a/src/adapter-middleware.coffee
+++ b/src/adapter-middleware.coffee
@@ -43,6 +43,15 @@ class TextMiddleware extends BaseMiddleware
return message
+ # Constructs a text message response to indicate an error to the user in the
+ # message channel they are using
+ constructErrorResponse: (activity, text) ->
+ payload =
+ type: 'message'
+ text: "#{text}"
+ address: activity?.address
+ return payload
+
# Indicates that the authorization isn't supported for this middleware
supportsAuth: () ->
return false
diff --git a/src/adapter.coffee b/src/adapter.coffee
index 063d3ff..76f2d12 100644
--- a/src/adapter.coffee
+++ b/src/adapter.coffee
@@ -23,7 +23,7 @@ class BotFrameworkAdapter extends Adapter
constructor: (robot) ->
super robot
@appId = process.env.BOTBUILDER_APP_ID
- @appPassword = process.env.BOTBUILDER_APP_PASSWORD
+ @appPassword = process.env.BOTBUILDER_APP_PASSWORD
@endpoint = process.env.BOTBUILDER_ENDPOINT || "/api/messages"
@enableAuth = process.env.HUBOT_TEAMS_ENABLE_AUTH || 'false'
robot.logger.info "#{LogPrefix} Adapter loaded. Using appId #{@appId}"
@@ -49,12 +49,21 @@ class BotFrameworkAdapter extends Adapter
@connector.onEvent (events, cb) => @onBotEvents events, cb
- @connector.onInvoke (events, cb) => @sendTextToHubot events, cb
+ @connector.onInvoke (events, cb) => @menuCardInvoke events, cb
- sendTextToHubot: (invokeEvent, cb) ->
- invokeEvent.text = invokeEvent.value.hubotMessage
- delete invokeEvent.value
- @handleActivity(invokeEvent)
+
+ # If the command for the invoke doesn't need user input, handle the command
+ # normally. If it does need user input, return a prompt for user input.
+ menuCardInvoke: (invokeEvent, cb) ->
+ middleware = @using(invokeEvent.source)
+ payload = middleware.maybeConstructUserInputPrompt(invokeEvent)
+ if payload == null
+ invokeEvent.text = invokeEvent.value.hubotMessage
+ delete invokeEvent.value
+ @handleActivity(invokeEvent)
+ else
+ @sendPayload(@robot, payload)
+ return
using: (name) ->
MiddlewareClass = Middleware.middlewareFor(name)
@@ -68,7 +77,8 @@ class BotFrameworkAdapter extends Adapter
handleActivity: (activity) ->
console.log("handle activity")
console.log(activity)
- @robot.logger.info "#{LogPrefix} Handling activity Channel: #{activity.source}; type: #{activity.type}"
+ @robot.logger.info "#{LogPrefix} Handling activity Channel:
+ #{activity.source}; type: #{activity.type}"
# Construct the middleware
middleware = @using(activity.source)
@@ -79,17 +89,32 @@ class BotFrameworkAdapter extends Adapter
# the text middleware, otherwise use the Teams middleware
if not middleware.supportsAuth()
if @enableAuth == 'true'
- @robot.logger.info "#{LogPrefix} Message source doesn't support authorization"
- activity.text = "hubot return source authorization not supported error"
- # This redundant section is included if we do the short circuit sendPayload thing
- event = middleware.toReceivable activity
- if event?
- @robot.receive event
+ # @robot.logger.info "#{LogPrefix} Message source doesn't support authorization"
+ # activity.text = "hubot return source authorization not supported error"
+ # # This redundant section is included if we do the short circuit sendPayload thing
+ # event = middleware.toReceivable activity
+ # if event?
+ # @robot.receive event
+ # A payload has at least type and address
+ @robot.logger.info "#{LogPrefix} Authorization isn\'t supported for the channel"
+ text = "Authorization isn't supported for the channel"
+ payload = middleware.constructErrorResponse(activity, text)
+ @sendPayload(@robot, payload)
+ return
else
event = middleware.toReceivable activity
if event?
@robot.receive event
else
+ # Check for clicks from the dropdown menu
+ if activity?.value?.needsUserInput == 'true'
+ payload = middleware.maybeConstructUserInputPrompt(activity)
+ if payload != null
+ @sendPayload(@robot, payload)
+ return
+ activity.text = activity.value.hubotMessage
+ delete activity.value
+
# Construct a TeamsChatConnector to pass to toReceivable
teamsConnector = new BotBuilderTeams.TeamsChatConnector {
appId: @robot.adapter.appId
@@ -99,43 +124,18 @@ class BotFrameworkAdapter extends Adapter
if event?
console.log("********************************")
console.log(event)
- # If unauthorized error occurred, overwrite the text
+
if unauthorizedError
- event.text = "hubot return unauthorized user error"
+ @robot.logger.info "#{LogPrefix} Unauthorized user, sending error"
+
+ text = "You are not authorized to send commands to hubot.
+ To gain access, talk to your admins:"
+ payload = middleware.constructErrorResponse(activity, text, true)
+ @sendPayload(@robot, payload)
+ return
+
@robot.receive event
- # # Return an error to the user if authorization is enabled and the message
- # # is either from a channel that doesn't support auth or if the user who sent the
- # # message isn't authorized
- # authorizedUsers = @robot.brain.get("authorizedUsers")
- # aadObjectId = activity?.address?.user?.aadObjectId
- # if @enableAuth == 'true'
- # if middleware.supportsAuth()
- # if aadObjectId is undefined or authorizedUsers[aadObjectId] is undefined
- # @robot.logger.info "#{LogPrefix} Unauthorized user; returning error"
- # activity.text = "hubot return unauthorized user error"
- # # Change this to make a call to a middleware function that returns
- # # a payload with the error text to return
- # else
- # @robot.logger.info "#{LogPrefix} Message source doesn't support authorization"
- # activity.text = "hubot return source authorization not supported error"
-
- # # If authorization isn't supported by the activity source, use
- # # the text middleware, otherwise use the Teams middleware
- # if not middleware.supportsAuth()
- # event = middleware.toReceivable activity
- # if event?
- # @robot.receive event
- # else
- # # Construct a TeamsChatConnector to pass to toReceivable
- # teamsConnector = new BotBuilderTeams.TeamsChatConnector {
- # appId: @robot.adapter.appId
- # appPassword: @robot.adapter.appPassword
- # }
- # middleware.toReceivable activity, teamsConnector, (event) =>
- # if event?
- # @robot.receive event
-
send: (context, messages...) ->
@robot.logger.info "#{LogPrefix} send"
@reply context, messages...
diff --git a/src/hubot-response-cards.coffee b/src/hubot-response-cards.coffee
index 7edae24..83a6e9a 100644
--- a/src/hubot-response-cards.coffee
+++ b/src/hubot-response-cards.coffee
@@ -3,12 +3,12 @@
HubotQueryParts = require './hubot-query-parts'
-maybeConstructCard = (response, query) ->
+maybeConstructResponseCard = (response, query) ->
# Check if the response is from a list commands follow up button press.
# If so, construct the needed input card and return it
- index = query.search("generate input card")
- if (index != -1)
- return constructMenuInputCard(query.replace("generate input card", ""), response.text)
+ # index = query.search("generate input card")
+ # if (index != -1)
+ # return maybeConstructMenuInputCard(response.text)
# Check if response.text matches one of the reg exps in the LUT
for regex of HubotResponseCards
@@ -20,10 +20,19 @@ maybeConstructCard = (response, query) ->
return card
return null
-# Constructs an input card
-constructMenuInputCard = (query, text) ->
- card = initializeAdaptiveCard(query)
- queryParts = HubotQueryParts[text]
+# Constructs an input card if needed or returns null if the
+# query doesn't need user input
+maybeConstructMenuInputCard = (query) ->
+ queryParts = HubotQueryParts[query]
+
+ # Check if the query needs a user input card
+ console.log(queryParts.inputParts is undefined)
+ console.log(queryParts.inputParts == undefined)
+ if queryParts.inputParts is undefined
+ return null
+
+ shortQuery = constructShortQuery(query)
+ card = initializeAdaptiveCard(shortQuery)
# Create the input fields of the sub card
for i in [0 ... queryParts.inputParts.length]
@@ -38,7 +47,8 @@ constructMenuInputCard = (query, text) ->
# Create selector
if index != -1
- card.content.body.push(addSelector(query, inputPart.substring(index + 1), query + " - input" + "#{i}"))
+ card.content.body.push(addSelector(query, inputPart.substring(index + 1),
+ query + " - input" + "#{i}"))
# Create text input
else
card.content.body.push(addTextInput(query + " - input" + "#{i}", inputPart))
@@ -126,13 +136,7 @@ addTextInput = (id, inputPart) ->
getFollowUpButtons = (query, regex) ->
actions = []
for followUpQuery in HubotResponseCards[regex]
-
- # Create a short version of the command by including only the
- # start of the command to the first user input marked by ( or <
- shortQueryEnd = followUpQuery.search(new RegExp("[(<]"))
- if shortQueryEnd == -1
- shortQueryEnd = followUpQuery.length
- shortQuery = followUpQuery.substring(0, shortQueryEnd)
+ shortQuery = constructShortQuery(followUpQuery)
action = {
'title': shortQuery
}
@@ -246,6 +250,15 @@ appendCardActions = (card1, card2) ->
return card1
+# Create a short version of the command by including only the
+# start of the command to the first user input marked by ( or <
+constructShortQuery = (query) ->
+ shortQueryEnd = query.search(new RegExp("[(<]"))
+ if shortQueryEnd == -1
+ shortQueryEnd = query.length
+ shortQuery = query.substring(0, shortQueryEnd)
+ return shortQuery.trim()
+
# HubotResponseCards maps from regex's of hubot queries to an array of follow up hubot
# queries stored as strings
HubotResponseCards = {
@@ -285,7 +298,8 @@ HubotResponseCards = {
}
module.exports = {
- maybeConstructCard,
+ maybeConstructResponseCard,
+ maybeConstructMenuInputCard,
appendCardBody,
appendCardActions
}
\ No newline at end of file
diff --git a/src/msteams-middleware.coffee b/src/msteams-middleware.coffee
index 40e5690..7504e89 100644
--- a/src/msteams-middleware.coffee
+++ b/src/msteams-middleware.coffee
@@ -25,7 +25,7 @@
BotBuilder = require 'botbuilder'
BotBuilderTeams = require 'botbuilder-teams'
HubotResponseCards = require './hubot-response-cards'
-#MicrosoftGraph = require '@microsoft/microsoft-graph-client'
+HubotQueryParts = require './hubot-query-parts'
{ Robot, TextMessage, Message, User } = require 'hubot'
{ BaseMiddleware, registerMiddleware } = require './adapter-middleware'
LogPrefix = "hubot-msteams:"
@@ -41,7 +41,8 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
@allowedTenants = []
if process.env.HUBOT_OFFICE365_TENANT_FILTER?
@allowedTenants = process.env.HUBOT_OFFICE365_TENANT_FILTER.split(",")
- @robot.logger.info("#{LogPrefix} Restricting tenants to #{JSON.stringify(@allowedTenants)}")
+ @robot.logger.info("#{LogPrefix} Restricting tenants to \
+ #{JSON.stringify(@allowedTenants)}")
toReceivable: (activity, teamsConnector, authEnabled, cb) ->
@robot.logger.info "#{LogPrefix} toReceivable"
@@ -54,45 +55,43 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
# Get the user
user = getUser(activity)
user = @robot.brain.userForId(user.id, user)
- console.log("++++++++++++++++++++++++++++++++++++++++++++++")
- console.log(user)
- # We don't want to save the activity or room in the brain since its something that changes per chat.
+ # We don't want to save the activity or room in the brain since its
+ # something that changes per chat.
user.activity = activity
user.room = getRoomId(activity)
- if activity.type != 'message' && activity.type != 'invoke'
- return new Message(user)
- else
- # Fetch the roster of members to do authorization based on UPN
- teamsConnector.fetchMembers activity?.address?.serviceUrl, activity?.address?.conversation?.id, (err, chatMembers) =>
- if err
- return
- console.log("==========================================")
- console.log(chatMembers)
- # Set the unauthorizedError to true if auth is enabled and the user who sent
- # the message is not authorized
- unauthorizedError = false
- if authEnabled
- authorizedUsers = @robot.brain.get("authorizedUsers")
- console.log("---------------")
- console.log(chatMembers[0].userPrincipalName)
- # Get the sender's UPN
- senderUPN = getSenderUPN(user, chatMembers)
- console.log(senderUPN)
- if senderUPN is undefined or authorizedUsers[senderUPN] is undefined
- @robot.logger.info "#{LogPrefix} Unauthorized user; returning error"
- unauthorizedError = true
- # activity.text = "hubot return unauthorized user error"
- # Change this to make a call to a middleware function that returns
- # a payload with the error text to return
-
- # Add the sender's UPN to user
- user.userPrincipalName = senderUPN
+ # Fetch the roster of members to do authorization based on UPN
+ teamsConnector.fetchMembers activity?.address?.serviceUrl, \
+ activity?.address?.conversation?.id, (err, chatMembers) =>
+ if err
+ console.log("YUP AN ERR")
+ return
- activity = fixActivityForHubot(activity, @robot, chatMembers)
- message = new TextMessage(user, activity.text, activity.address.id)
- cb(message, unauthorizedError)
+ # Set the unauthorizedError to true if auth is enabled and the user who sent
+ # the message is not authorized
+ unauthorizedError = false
+ if authEnabled
+ authorizedUsers = @robot.brain.get("authorizedUsers")
+ # Get the sender's UPN
+ senderUPN = getSenderUPN(user, chatMembers)
+ if senderUPN is undefined or authorizedUsers[senderUPN] is undefined
+ @robot.logger.info "#{LogPrefix} Unauthorized user; returning error"
+ unauthorizedError = true
+ # activity.text = "hubot return unauthorized user error"
+ # Change this to make a call to a middleware function that returns
+ # a payload with the error text to return
+
+ # Add the sender's UPN to user
+ user.userPrincipalName = senderUPN
+
+ # Return a generic message if the activity isn't a message or invoke
+ if activity.type != 'message' && activity.type != 'invoke'
+ cb(new Message(user), unauthorizedError)
+
+ activity = fixActivityForHubot(activity, @robot, chatMembers)
+ message = new TextMessage(user, activity.text, activity.address.id)
+ cb(message, unauthorizedError)
toSendable: (context, message) ->
@robot.logger.info "#{LogPrefix} toSendable"
@@ -109,7 +108,7 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
# If the query sent by the user should trigger a card,
# construct the card to attach to the response
# and remove sentQuery from the brain
- card = HubotResponseCards.maybeConstructCard(response, activity.text)
+ card = HubotResponseCards.maybeConstructResponseCard(response, activity.text)
if card != null
delete response.text
response.attachments = [card]
@@ -156,7 +155,7 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
return true
# Combines the text and attachments of multiple hubot messages sent in succession.
- # Most of the first received response is kept, and the text and attachments of
+ # Most of the first received response is kept, and the text and attachments of
# subsequent responses received within 500ms of the first are combined into the
# first response.
combineResponses: (storedPayload, newPayload) ->
@@ -197,9 +196,53 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
if attachment.contentType != "application/vnd.microsoft.card.adaptive"
storedMessage.attachments.push(attachment)
else
- storedCard = HubotResponseCards.appendCardBody(storedCard, attachment)
- storedCard = HubotResponseCards.appendCardActions(storedCard, attachment)
+ storedCard = HubotResponseCards.appendCardBody(storedCard, \
+ attachment)
+ storedCard = HubotResponseCards.appendCardActions(storedCard, \
+ attachment)
+ # Constructs a text message response to indicate an error to the user in the
+ # message channel they are using
+ constructErrorResponse: (activity, text, appendAdmins) ->
+ if appendAdmins
+ authorizedUsers = @robot.brain.get("authorizedUsers")
+ for userKey, isAdmin of authorizedUsers
+ if isAdmin
+ text = """#{text}
+ #{userKey}"""
+ typing =
+ type: 'typing'
+ address: activity?.address
+
+ payload =
+ type: 'message'
+ text: "#{text}"
+ address: activity?.address
+
+ return [typing, payload]
+
+ # Constructs a response containing a card for user input if needed or null
+ # if user input is not needed
+ maybeConstructUserInputPrompt: (event) ->
+ query = event.value.hubotMessage
+
+ # Remove the hubot prefix, if there is one
+ query = query.replace("hubot ", "")
+ console.log(query)
+
+ card = HubotResponseCards.maybeConstructMenuInputCard(query)
+ if card is null
+ console.log("CARD IS NULL")
+ return null
+
+ response =
+ type: 'message'
+ address: event?.address
+ attachments: [
+ card
+ ]
+
+ return response
#############################################################################
# Helper methods for generating richer messages
@@ -256,7 +299,8 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
entities = activity?.entities || []
if not Array.isArray(entities)
entities = [entities]
- return entities.filter((entity) -> entity.type == "mention" && (not userId? || userId == entity.mentioned?.id))
+ return entities.filter((entity) -> entity.type == "mention" && \
+ (not userId? || userId == entity.mentioned?.id))
# Returns the provided user's userPrincipalName (UPN) or null if one cannot be found
getSenderUPN = (user, chatMembers) ->
@@ -270,14 +314,16 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
# Fixes the activity to have the proper information for Hubot
# 1. Constructs the text command to send to hubot if the event is from a
- # submit on an adaptive card (containing the value property).
- # 2. Replaces all occurrences of the channel's bot at mention name with the configured name in hubot.
+ # submit on an adaptive card (containing the value property).
+ # 2. Replaces all occurrences of the channel's bot at mention name with the configured
+ # name in hubot.
# The hubot's configured name might not be the same name that is sent from the chat service in
# the activity's text.
# 3. Replaces all occurrences of @ mentions to users with their aad object id if the user is
# on the roster of chanenl members from Teams. If a mentioned user is not in the chat roster,
# the mention is replaced with their name.
- # 4. Prepends hubot's name to the message for personal messages if it's not already
+ # 4. Trims all whitespace and newlines from the beginning and end of the text.
+ # 5. Prepends hubot's name to the message for personal messages if it's not already
# there
fixActivityForHubot = (activity, robot, chatMembers) ->
# If activity.value exists, the command is from a card and the correct
@@ -309,10 +355,11 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
# Set the constructed query as the text of the activity
activity.text = text
- return activity
+ #return activity
if not activity?.text? || typeof activity.text isnt 'string'
return activity
+
myChatId = activity?.address?.bot?.id
if not myChatId?
return activity
@@ -332,6 +379,9 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
replacement = member.userPrincipalName
# *** replacement = member.objectId
activity.text = activity.text.replace(mentionTextRegExp, replacement)
+
+ # Remove leading/trailing whitespace and newlines
+ activity.text = activity.text.trim()
# prepends the robot's name for direct messages if it's not already there
if getConversationType(activity) == 'personal' && activity.text.search(robot.name) != 0
diff --git a/test/adapter.test.coffee b/test/adapter.test.coffee
index 3d1b82f..fdf5a14 100644
--- a/test/adapter.test.coffee
+++ b/test/adapter.test.coffee
@@ -4,15 +4,28 @@ expect = chai.expect
BotFrameworkAdapter = require '../src/adapter'
MockRobot = require './mock-robot'
+
describe 'Main Adapter', ->
- describe 'Test Auth', ->
+ describe 'Test Authorization Setup', ->
beforeEach ->
- process.env.HUBOT_TEAMS_INITIAL_ADMINS = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee,eight888-four-4444-fore-twelve121212'
- process.env.BOTBUILDER_APP_ID = 'botbuilder-app-id'
- process.env.BOTBUILDER_APP_PASSWORD = 'botbuilder-app-password'
- process.env.HUBOT_DEBUG_LEVEL = 'error'
+ process.env.HUBOT_TEAMS_INITIAL_ADMINS = 'an-1_20@em.ail,authorized_user@email.la'
+ # process.env.BOTBUILDER_APP_ID = 'botbuilder-app-id'
+ # process.env.BOTBUILDER_APP_PASSWORD = 'botbuilder-app-password'
process.env.HUBOT_TEAMS_ENABLE_AUTH = 'true'
+ it 'should not set initial admins when auth enable is not set', ->
+ # Setup
+ delete process.env.HUBOT_TEAMS_ENABLE_AUTH
+ robot = new MockRobot
+
+ # Action
+ expect(() ->
+ adapter = BotFrameworkAdapter.use(robot)
+ ).to.not.throw()
+
+ # Assert
+ expect(robot.brain.get("authorizedUsers")).to.be.null
+
it 'should not set initial admins when auth is not enabled', ->
# Setup
process.env.HUBOT_TEAMS_ENABLE_AUTH = 'false'
@@ -25,6 +38,16 @@ describe 'Main Adapter', ->
# Assert
expect(robot.brain.get("authorizedUsers")).to.be.null
+
+ it 'should throw error when auth is enabled and initial admins', ->
+ # Setup
+ delete process.env.HUBOT_TEAMS_INITIAL_ADMINS
+ robot = new MockRobot
+
+ # Action and Assert
+ expect(() ->
+ adapter = BotFrameworkAdapter.use(robot)
+ ).to.throw()
it 'should set initial admins when auth is enabled', ->
# Setup
@@ -37,50 +60,22 @@ describe 'Main Adapter', ->
# Assert
expect(robot.brain.get("authorizedUsers")).to.eql {
- 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee': true
- 'eight888-four-4444-fore-twelve121212': true
+ 'an-1_20@em.ail': true
+ 'authorized_user@email.la': true
}
-
- it 'should not set initial admins when auth enable is not set', ->
- # Setup
- delete process.env.HUBOT_TEAMS_ENABLE_AUTH
- robot = new MockRobot
-
- # Action
- expect(() ->
- adapter = BotFrameworkAdapter.use(robot)
- ).to.not.throw()
-
- # Assert
- expect(robot.brain.get("authorizedUsers")).to.be.null
- it 'should allow messages from authorized users', ->
- # Setup
+ describe 'Test Authorization Support for Teams Channel', ->
+ robot = null
+ adapter = null
+ event = null
+ beforeEach ->
+ process.env.HUBOT_TEAMS_INITIAL_ADMINS = 'an-1_20@em.ail,authorized_user@email.la'
+ process.env.BOTBUILDER_APP_ID = 'botbuilder-app-id'
+ process.env.BOTBUILDER_APP_PASSWORD = 'botbuilder-app-password'
+ process.env.HUBOT_TEAMS_ENABLE_AUTH = 'true'
robot = new MockRobot
adapter = BotFrameworkAdapter.use(robot)
robot.adapter = adapter
- adapter.connector.fetchMembers = (serviceUrl, teamId, callback) ->
- members = [
- {
- id: 'id-1'
- objectId: 'aad-object-id-1'
- name: 'user1 one'
- givenName: 'user1'
- surname: 'one'
- email: 'one@user.one'
- userPrincipalName: 'one@user.one'
- },
- {
- id: 'user-id'
- objectId: 'eight888-four-4444-fore-twelve121212'
- name: 'user-name'
- givenName: 'user-'
- surname: 'name'
- email: 'em@ai.l'
- userPrincipalName: 'em@ai.l'
- }
- ]
- callback false, members
event =
type: 'message'
text: 'Bot do something Bot and tell User about it'
@@ -112,88 +107,44 @@ describe 'Main Adapter', ->
id: "user-id"
name: "user-name"
aadObjectId: "eight888-four-4444-fore-twelve121212"
+ userPrincipalName: "user-UPN"
serviceUrl: 'url-serviceUrl/a-url'
+ it 'should return authorization not supported error for non-Teams channels', ->
+ # Setup
+ event.source = 'authorization-not-supported-source'
+
# Action
expect(() ->
- result = adapter.handleActivity(event)
+ adapter.handleActivity(event)
).to.not.throw()
# Assert
- expect(robot.brain.get("authorizedUsers")).to.eql {
- 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee': true
- 'eight888-four-4444-fore-twelve121212': true
- }
+ result = robot.brain.get("event")
+ expect(result.text).to.eql "hubot return source authorization not supported error"
- # it 'should overwrite the hubot command text to return an error message to unauthorized users', ->
- # # Setup
- # robot = new Robot('../../hubot-botframework', 'botframework', false, 'hubot')
- # adapter = BotFrameworkAdapter.use(robot)
- # adapter.connector.fetchMembers = (serviceUrl, teamId, callback) ->
- # members = [
- # {
- # id: 'id-1'
- # objectId: 'aad-object-id-1'
- # name: 'user1 one'
- # givenName: 'user1'
- # surname: 'one'
- # email: 'one@user.one'
- # userPrincipalName: 'one@user.one'
- # },
- # {
- # id: 'user-id'
- # objectId: 'eight888-four-4444-fore-twelve121212'
- # name: 'user-name'
- # givenName: 'user-'
- # surname: 'name'
- # email: 'em@ai.l'
- # userPrincipalName: 'em@ai.l'
- # }
- # ]
- # callback false, members
- # event =
- # type: 'message'
- # text: 'Bot do something Bot and tell User about it'
- # agent: 'tests'
- # source: 'msteams'
- # entities: [
- # type: "mention"
- # text: "Bot"
- # mentioned:
- # id: "bot-id"
- # name: "bot-name"
- # ,
- # type: "mention"
- # text: "User"
- # mentioned:
- # id: "user-id"
- # name: "user-name"
- # ]
- # attachments: []
- # sourceEvent:
- # tenant:
- # id: "tenant-id"
- # address:
- # conversation:
- # id: "19:conversation-id"
- # bot:
- # id: "bot-id"
- # user:
- # id: "id-1"
- # name: "user1 one"
- # aadObjectId: "aad-object-id-1"
- # serviceUrl: 'url-serviceUrl/a-url'
+ it 'should work when authorization is enabled and message is from Teams', ->
+ # Setup
- # # Action
- # expect(() ->
- # result = adapter.handleActivity(event)
- # ).to.not.throw()
+ # Action
+ expect(() ->
+ adapter.handleActivity(event)
+ ).to.not.throw()
- # console.log("=======================================")
- # console.log(adapter)
+ # Assert
+ result = robot.brain.get("event")
- # # Assert
- # expect(robot.brain.get("authorizedUsers")).to.eql {
- # 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee': true
- # 'eight888-four-4444-fore-twelve121212': true
- # }
\ No newline at end of file
+ it 'should work when message is from invoke', ->
+ # Setup
+ event.type = 'invoke'
+ event.value =
+ hubotMessage: 'hubot do something'
+ delete event.text
+
+ # Action
+ expect(() ->
+ adapter.sendTextToHubot(event)
+ ).to.not.throw()
+
+ # Assert
+
\ No newline at end of file
diff --git a/test/mock-robot.coffee b/test/mock-robot.coffee
index 9686c12..702598f 100644
--- a/test/mock-robot.coffee
+++ b/test/mock-robot.coffee
@@ -9,7 +9,19 @@ class MockRobot
"hubot b - does something b"
]
@brain =
- userForId: -> {}
+ data: {}
+ userForId: (id, options) ->
+ user = {
+ id: "#{id}"
+ name: "a-hubot-user-name"
+ }
+ if options is null
+ return user
+ else
+ for key of options
+ user[key] = options[key]
+ return user
+
users: -> []
get: (key) ->
if @data is undefined
@@ -18,16 +30,9 @@ class MockRobot
if key == storedKey
return @data[storedKey]
return null
+ set: (key, value) ->
+ @data[key] = value
- if process.env.HUBOT_TEAMS_ENABLE_AUTH == 'true'
- if process.env.HUBOT_TEAMS_INITIAL_ADMINS
- @brain.data = {}
- authorizedUsers = {}
- for admin in process.env.HUBOT_TEAMS_INITIAL_ADMINS.split(",")
- authorizedUsers[admin] = true
- @brain.data["authorizedUsers"] = authorizedUsers
-
- else
- throw new Error("HUBOT_TEAMS_INITIAL_ADMINS is required")
- receive: -> {}
+ receive: (event) ->
+ @brain.data["event"] = event
module.exports = MockRobot
diff --git a/test/msteams-middleware.test.coffee b/test/msteams-middleware.test.coffee
index 3339057..8e854ab 100644
--- a/test/msteams-middleware.test.coffee
+++ b/test/msteams-middleware.test.coffee
@@ -11,15 +11,21 @@ describe 'MicrosoftTeamsMiddleware', ->
describe 'toReceivable', ->
robot = null
event = null
- chatMembers = null
- cb = -> {}
+ teamsChatConnector = null
+ authEnabled = false
+ cb = ->
+ robot.receive event
beforeEach ->
delete process.env.HUBOT_OFFICE365_TENANT_FILTER
-
robot = new MockRobot
- adapter = BotFrameworkAdapter.use(robot)
- robot.adapter = adapter
+ options = {
+ appId: 'botframework-app-id'
+ appPassword: 'botframework-app-password'
+ }
+ teamsChatConnector = new MockTeamsChatConnector(options)
+
+ authEnabled = false
event =
type: 'message'
text: 'Bot do something Bot and tell User about it'
@@ -52,97 +58,135 @@ describe 'MicrosoftTeamsMiddleware', ->
name: "user-name"
aadObjectId: 'eight888-four-4444-fore-twelve121212'
- it 'should allow messages without tenant id when tenant filter is empty', ->
+
+ it 'should allow messages when auth is not enabled', ->
# Setup
- console.log("**************************************")
- console.log(MicrosoftTeamsMiddleware.toString())
delete event.sourceEvent
teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
- options = {
- appId: 'botframework-app-id'
- appPassword: 'botframework-app-password'
- }
- teamsChatConnector = new MockTeamsChatConnector(options)
-
# Action
- receivable = null
expect(() ->
- receivable = teamsMiddleware.toReceivable(event, teamsChatConnector, cb)
+ teamsMiddleware.toReceivable(event, teamsChatConnector, authEnabled, cb)
).to.not.throw()
# Assert
- expect(receivable).to.be.a('Object')
+ result = robot.brain.get("event")
+ expect(result).to.be.a('Object')
- # it 'should allow messages with tenant id when tenant filter is empty', ->
- # # Setup
- # teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
+ it 'should set unauthorized error for message when user isn\'t authorized', ->
+ # Setup
+ robot.brain.data["authorizedUsers"] =
+ 'an-1_20@em.ail': true
+ 'authorized_user@email.la': false
+ teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
+ authEnabled = true
+ cb = (event, unauthorizedError) ->
+ robot.brain.data["unauthError"] = unauthorizedError
+ robot.receive event
- # # Action
- # receivable = null
- # expect(() ->
- # receivable = teamsMiddleware.toReceivable(event, cb)
- # ).to.not.throw()
+ # Action
+ expect(() ->
+ teamsMiddleware.toReceivable(event, teamsChatConnector, authEnabled, cb)
+ ).to.not.throw()
- # # Assert
- # expect(receivable).to.be.a('Object')
+ # Assert
+ result = robot.brain.get("event")
+ expect(result).to.be.a('Object')
+ expect(robot.brain.get("unauthError")).to.be.true
- # it 'should allow messages from allowed tenant ids', ->
- # # Setup
- # process.env.HUBOT_OFFICE365_TENANT_FILTER = event.sourceEvent.tenant.id
- # teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
+ it 'should allow messages without tenant id when tenant filter is empty', ->
+ # Setup
+ delete event.sourceEvent
+ teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
- # # Action
- # receivable = null
- # expect(() ->
- # receivable = teamsMiddleware.toReceivable(event, cb)
- # ).to.not.throw()
+ # Action
+ expect(() ->
+ teamsMiddleware.toReceivable(event, teamsChatConnector, authEnabled, cb)
+ ).to.not.throw()
- # # Assert
- # expect(receivable).to.be.a('Object')
+ # Assert
+ result = robot.brain.get("event")
+ expect(result).to.be.a('Object')
- # it 'should block messages from unallowed tenant ids', ->
- # # Setup
- # process.env.HUBOT_OFFICE365_TENANT_FILTER = event.sourceEvent.tenant.id
- # event.sourceEvent.tenant.id = "different-tenant-id"
- # teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
+ it 'should allow messages with tenant id when tenant filter is empty', ->
+ # Setup
+ teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
- # # Action
- # receivable = null
- # expect(() ->
- # receivable = teamsMiddleware.toReceivable(event, cb)
- # ).to.not.throw()
+ # Action
+ expect(() ->
+ teamsMiddleware.toReceivable(event, teamsChatConnector, authEnabled, cb)
+ ).to.not.throw()
- # # Assert
- # expect(receivable).to.be.null
+ # Assert
+ result = robot.brain.get("event")
+ expect(result).to.be.a('Object')
- # it 'return generic message when appropriate type is not found', ->
- # # Setup
- # event.type = 'typing'
- # teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
+ it 'should allow messages from allowed tenant ids', ->
+ # Setup
+ process.env.HUBOT_OFFICE365_TENANT_FILTER = event.sourceEvent.tenant.id
+ teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
- # # Action
- # receivable = null
- # expect(() ->
- # receivable = teamsMiddleware.toReceivable(event, cb)
- # ).to.not.throw()
+ # Action
+ expect(() ->
+ teamsMiddleware.toReceivable(event, teamsChatConnector, authEnabled, cb)
+ ).to.not.throw()
- # # Assert
- # expect(receivable).to.be.not.null
+ # Assert
+ result = robot.brain.get("event")
+ expect(result).to.be.a('Object')
- # it 'should work when activity text is an object', ->
- # # Setup
- # event.text = event
- # teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
+ it 'should block messages from unallowed tenant ids', ->
+ # Setup
+ process.env.HUBOT_OFFICE365_TENANT_FILTER = event.sourceEvent.tenant.id
+ event.sourceEvent.tenant.id = "different-tenant-id"
+ teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
- # # Action
- # receivable = null
- # expect(() ->
- # receivable = teamsMiddleware.toReceivable(event, cb)
- # ).to.not.throw()
+ # Action
+ expect(() ->
+ teamsMiddleware.toReceivable(event, teamsChatConnector, authEnabled, cb)
+ ).to.not.throw()
- # # Assert
- # expect(receivable.text).to.equal(event)
+ # Assert
+ result = robot.brain.get("event")
+ expect(result).to.be.null
+
+ it 'return generic message when appropriate type is not found', ->
+ # Setup
+ event.type = 'typing'
+ teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
+
+ # Action
+ expect(() ->
+ teamsMiddleware.toReceivable(event, teamsChatConnector, authEnabled, cb)
+ ).to.not.throw()
+
+ # Assert
+ result = robot.brain.get("event")
+ expect(result).to.be.not.null
+
+ # Test when message is from follow up button
+ it 'should work when activity value contains query parts', ->
+ # Setup
+
+
+ # Action
+
+ # Assert
+
+
+ it 'should work when activity text is an object', ->
+ # Setup
+ event.text = event
+ teamsMiddleware = new MicrosoftTeamsMiddleware(robot)
+
+ # Action
+ expect(() ->
+ teamsMiddleware.toReceivable(event, teamsChatConnector, authEnabled, cb)
+ ).to.not.throw()
+
+ # Assert
+ result = robot.brain.get("event")
+ expect(result.text).to.equal(event)
# it 'should work when mentions not provided', ->
# # Setup