Created
August 10, 2016 01:48
-
-
Save nickmalcolm/0d3479c52ea77893c3f325368268b0a3 to your computer and use it in GitHub Desktop.
Simple ruby script to pull events from Okta's API, and push them to ThisData
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'this_data' | |
require 'httparty' | |
# A simple proof of concept which will pull a page of events from Okta, and push | |
# them to ThisData. This enables ThisData to detect behavioural anomalies, and | |
# keep a third-party access log. | |
# | |
# Requires the ThisData and HTTParty ruby gems. | |
# | |
# Usage: | |
# OktaSync.new( | |
# okta_org: "yourorg", | |
# okta_token: "abc", | |
# thisdata_api_key: "def" | |
# ).sync | |
# | |
# Improvements: add pagination, better event transformation when user is not | |
# in the users field | |
class OktaSync | |
def initialize(okta_org: nil, okta_token: nil, thisdata_api_key: nil) | |
@okta_org = okta_org | |
@okta_token = okta_token | |
# Set up ThisData | |
ThisData.setup do |config| | |
config.api_key = thisdata_api_key | |
config.logger = Logger.new($stdout) | |
config.async = false | |
end | |
end | |
def sync | |
okta_events = fetch_events_from_okta | |
okta_events.each do |okta_event| | |
if event = transform_okta_to_thisdata(okta_event) | |
ThisData.track(event) | |
end | |
end | |
end | |
# Fetch a single page of events from Okta | |
def fetch_events_from_okta | |
url = "https://#{@okta_org}.oktapreview.com/api/v1/events" | |
response = HTTParty.get(url, headers: {"Authorization" => "SSWS #{@okta_token}"}) | |
JSON.parse(response.body) | |
end | |
# Some Okta events have equivalents in ThisData, which we should use. | |
def event_verb_from_okta_object_type(object_type) | |
case object_type | |
when "core.user_auth.login_success" | |
ThisData::Verbs::LOG_IN | |
when "core.user_auth.logout_success" | |
ThisData::Verbs::LOG_OUT | |
when "core.user_auth.login_failed" | |
ThisData::Verbs::LOG_IN_DENIED | |
when "core.user.sms.message_sent.factor" | |
ThisData::Verbs::LOG_IN_CHALLENGE | |
when "core.user.email.message_sent.self_service.password_reset" | |
ThisData::Verbs::PASSWORD_RESET | |
when "core.user.config.password_update.success" | |
ThisData::Verbs::PASSWORD_UPDATE | |
else | |
# No-op | |
object_type | |
end | |
end | |
# Takes an event from Okta, and turns it into an event ThisData can understand | |
def transform_okta_to_thisdata(event) | |
# Where is Okta keeping the user who performed this event? | |
if ["core.user.email.message_sent.self_service.password_reset", "core.user_auth.login_failed"].include?(event["action"]["objectType"]) | |
actor = event["targets"].find {|a| a["objectType"] == "User" } | |
else | |
actor = event["actors"].find {|a| a["objectType"] == "User" } | |
end | |
client = event["actors"].find {|a| a["objectType"] == "Client" } | |
# Skip system events | |
return nil unless !actor.nil? && !client.nil? | |
ip = client["ipAddress"] | |
user_agent = client["id"] | |
{ | |
verb: event_verb_from_okta_object_type(event["action"]["objectType"]), | |
ip: ip, | |
user_agent: user_agent, | |
user: { | |
id: actor["id"], | |
email: actor["login"], | |
name: actor["displayName"] | |
}, | |
original: event | |
} | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment