Added more tests and a way to send responses to the user without going through hubot
This commit is contained in:
Родитель
a3871df3c9
Коммит
9007425105
|
@ -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
|
||||
|
|
|
@ -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) ->
|
||||
|
||||
# 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...
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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,32 +55,26 @@ 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) =>
|
||||
teamsConnector.fetchMembers activity?.address?.serviceUrl, \
|
||||
activity?.address?.conversation?.id, (err, chatMembers) =>
|
||||
if err
|
||||
console.log("YUP AN 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
|
||||
|
@ -90,6 +85,10 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
|
|||
# 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)
|
||||
|
@ -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]
|
||||
|
@ -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) ->
|
||||
|
@ -271,13 +315,15 @@ 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.
|
||||
# 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
|
||||
|
@ -333,6 +380,9 @@ class MicrosoftTeamsMiddleware extends BaseMiddleware
|
|||
# *** 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
|
||||
activity.text = "#{robot.name} #{activity.text}"
|
||||
|
|
|
@ -4,43 +4,15 @@ 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 is not enabled', ->
|
||||
# Setup
|
||||
process.env.HUBOT_TEAMS_ENABLE_AUTH = 'false'
|
||||
robot = new MockRobot
|
||||
|
||||
# Action
|
||||
expect(() ->
|
||||
adapter = BotFrameworkAdapter.use(robot)
|
||||
).to.not.throw()
|
||||
|
||||
# Assert
|
||||
expect(robot.brain.get("authorizedUsers")).to.be.null
|
||||
|
||||
it 'should set initial admins when auth is enabled', ->
|
||||
# Setup
|
||||
robot = new MockRobot
|
||||
|
||||
# Action
|
||||
expect(() ->
|
||||
adapter = BotFrameworkAdapter.use(robot)
|
||||
).to.not.throw()
|
||||
|
||||
# Assert
|
||||
expect(robot.brain.get("authorizedUsers")).to.eql {
|
||||
'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee': true
|
||||
'eight888-four-4444-fore-twelve121212': true
|
||||
}
|
||||
|
||||
it 'should not set initial admins when auth enable is not set', ->
|
||||
# Setup
|
||||
delete process.env.HUBOT_TEAMS_ENABLE_AUTH
|
||||
|
@ -54,33 +26,56 @@ describe 'Main Adapter', ->
|
|||
# Assert
|
||||
expect(robot.brain.get("authorizedUsers")).to.be.null
|
||||
|
||||
it 'should allow messages from authorized users', ->
|
||||
it 'should not set initial admins when auth is not enabled', ->
|
||||
# Setup
|
||||
process.env.HUBOT_TEAMS_ENABLE_AUTH = 'false'
|
||||
robot = new MockRobot
|
||||
|
||||
# Action
|
||||
expect(() ->
|
||||
adapter = BotFrameworkAdapter.use(robot)
|
||||
).to.not.throw()
|
||||
|
||||
# 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
|
||||
robot = new MockRobot
|
||||
|
||||
# Action
|
||||
expect(() ->
|
||||
adapter = BotFrameworkAdapter.use(robot)
|
||||
).to.not.throw()
|
||||
|
||||
# Assert
|
||||
expect(robot.brain.get("authorizedUsers")).to.eql {
|
||||
'an-1_20@em.ail': true
|
||||
'authorized_user@email.la': true
|
||||
}
|
||||
|
||||
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: '<at>Bot</at> do something <at>Bot</at> and tell <at>User</at> 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: '<at>Bot</at> do something <at>Bot</at> and tell <at>User</at> about it'
|
||||
# agent: 'tests'
|
||||
# source: 'msteams'
|
||||
# entities: [
|
||||
# type: "mention"
|
||||
# text: "<at>Bot</at>"
|
||||
# mentioned:
|
||||
# id: "bot-id"
|
||||
# name: "bot-name"
|
||||
# ,
|
||||
# type: "mention"
|
||||
# text: "<at>User</at>"
|
||||
# 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")
|
||||
|
||||
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
|
||||
|
||||
# # Assert
|
||||
# expect(robot.brain.get("authorizedUsers")).to.eql {
|
||||
# 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee': true
|
||||
# 'eight888-four-4444-fore-twelve121212': true
|
||||
# }
|
|
@ -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
|
||||
|
|
|
@ -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: '<at>Bot</at> do something <at>Bot</at> and tell <at>User</at> 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
|
||||
|
|
Загрузка…
Ссылка в новой задаче