class Object

Constants

DEFAULT_BASE_RETRY_DELAY
DEFAULT_MAX_RETRIES
DEFAULT_MAX_RETRY_DELAY
OKTA_CLIENT_ORGURL
OKTA_CLIENT_TOKEN
SDM_API_ACCESS_KEY
SDM_API_SECRET_KEY

Public Instance Methods

main() click to toggle source

panicButton.rb suspends all users except for one admin, in the fake use case of a critical break in or something usage: ruby panicButton.rb adminuser@email.com to revert back to pre-panic state: ruby panicButton.rb revert

# File examples/panicButton.rb, line 25
def main
  access_key = ENV["SDM_API_ACCESS_KEY"]
  secret_key = ENV["SDM_API_SECRET_KEY"]
  if access_key == nil or secret_key == nil
    puts "SDM_API_ACCESS_KEY and SDM_API_SECRET_KEY must be provided"
    return
  end
  client = SDM::Client.new(access_key, secret_key)

  if ARGV.size == 1 and ARGV[0] == "revert"
    state_file = File.open("state.json")
    state = JSON.load(state_file)

    reinstated_count = 0

    users = client.accounts.list("")
    users.each { |user|
      if user.suspended
        reinstated_count += 1
        user.suspended = false
        client.accounts.update(user)
      end
    }
    state["attachments"].each { |attachment|
      begin
        a = SDM::AccountAttachment.new()
        a.account_id = attachment["account_id"]
        a.role_id = attachment["role_id"]
        client.account_attachments.create(a)
      rescue SDM::AlreadyExistsError
      rescue => ex
        puts "skipping creation of attachment due to error: " + ex.to_s
      end
    }
    state["grants"].each { |attachment|
      begin
        g = SDM::AccountGrant.new()
        g.account_id = attachment["account_id"]
        g.resource_id = attachment["resource_id"]
        client.account_grants.create(g)
      rescue SDM::AlreadyExistsError
      rescue => ex
        puts "skipping creation of grant due to error: " + ex.to_s
      end
    }

    puts "reinstated " + reinstated_count.to_s + " users"
    puts "recreated " + state["attachments"].size.to_s + " account attachments"
    puts "recreated " + state["grants"].size.to_s + " account grants"

    return
  end

  admin_email = ""
  if ARGV.size == 1
    admin_email = ARGV[0]
  else
    puts "please provide an admin email to preserve"
    return 1
  end

  admin_user_id = ""
  users = client.accounts.list("email:?", admin_email)
  users.each { |user|
    admin_user_id = user.id
  }

  account_attachments = client.account_attachments.list("")
  account_grants = client.account_grants.list("")

  state = {
    'attachments': account_attachments.map { |x|
      if x.account_id != admin_user_id
        out = {
          'account_id': x.account_id,
          'role_id': x.role_id,
        }
      end
    }.reject { |x| x == nil },
    'grants': account_grants.map { |x|
      if x.account_id != admin_user_id and x.valid_until == nil
        out = {
          'account_id': x.account_id,
          'resource_id': x.resource_id,
        }
      end
    }.reject { |x| x == nil },
  }

  puts "storing " + state[:attachments].size.to_s + " account attachments in state"
  puts "storing " + state[:grants].size.to_s + " account grants in state"

  state_file = File.open("state.json", "w")
  state_file.write(state.to_json)

  suspended_count = 0
  users = client.accounts.list("")
  users.each { |user|
    if user.instance_of? SDM::User and user.email == admin_email
      next
    end
    user.suspended = true
    begin
      client.accounts.update(user)
      suspended_count += 1
    rescue StandardError => ex
      puts "skipping user " + user.id + " on account of error: " + ex.to_s
    end
  }

  puts "suspended " + suspended_count.to_s + " users"
end
okta_sync() click to toggle source
# File examples/okta-sync/oktaSync.rb, line 25
def okta_sync
  if SDM_API_ACCESS_KEY == "" || SDM_API_SECRET_KEY == "" || OKTA_CLIENT_TOKEN == "" || OKTA_CLIENT_ORGURL == ""
    puts "SDM_API_ACCESS_KEY, SDM_API_SECRET_KEY, OKTA_CLIENT_TOKEN, and OKTA_CLIENT_ORGURL must be set"
    exit
  end

  report = {
    :start => Time.now,

    :oktaUsersCount => 0,
    :oktaUsers => [],

    :sdmUsersCount => 0,
    :sdmUsers => [],

    :bothUsersCount => 0,

    :sdmResourcesCount => 0,
    :sdmResources => {},

    :permissionsGranted => 0,
    :permissionsRevoked => 0,
    :grants => [],
    :revocations => [],

    :matchers => {},
  }

  plan = false
  verbose = false
  OptionParser.new do |opts|
    opts.banner = "Usage oktaSync.rb [options]"
    opts.on("-p", "--plan", "calculate changes but do not apply them") do |p|
      plan = p
    end
    opts.on("-v", "--verbose", "print detailed report") do |v|
      verbose = v
    end
  end.parse!

  client = SDM::Client.new(SDM_API_ACCESS_KEY, SDM_API_SECRET_KEY)
  okta_client = Oktakit.new(token: OKTA_CLIENT_TOKEN, api_endpoint: OKTA_CLIENT_ORGURL + "/api/v1")
  matchers = YAML.load(File.read("matchers.yml"))
  report[:matchers] = matchers

  all_users = okta_client.list_users({
    'query': {
      'search': "profile.department eq \"Engineering\" and (status eq \"ACTIVE\")",
    },
  })

  okta_users = Array.new()
  all_users[0].each { |u|
    groups = okta_client.get_member_groups(u.id)
    group_names = Array.new()
    groups[0].each { |ug|
      group_names.push(ug.profile.name)
    }
    okta_users.push({ :login => u.profile.login, :first_name => u.profile.firstName, :last_name => u.profile.LastName, :groups => group_names })
  }
  report[:oktaUsers] = okta_users
  report[:oktaUsersCount] = okta_users.size

  accounts = client.accounts.list("type:user").map { |a| [a.email, a] }.to_h
  report[:sdmUsers] = accounts
  report[:sdmUsersCount] = accounts.size
  grants = client.account_grants.list("").map { |ag| ag }

  current = {}
  grants.each { |g|
    current[g.account_id] = [] if not current[g.account_id]
    current[g.account_id].push({ :resource_id => g.resource_id, :id => g.id })
  }

  desired = {}
  overlapping = 0
  matchers["groups"].each { |group|
    group["resources"].each { |resourceQuery|
      client.resources.list(resourceQuery).each { |res|
        report[:sdmResources][res.id] = res
        okta_users.each { |u|
          if u[:groups].include? group["name"]
            account = accounts[u[:login]]
            if account != nil
              overlapping += 1
              desired[account.id] = [] if not desired[account.id]
              desired[account.id].push(res.id)
            end
          end
        }
      }
    }
  }
  report[:bothUsersCount] = overlapping
  report[:sdmResourcesCount] = report[:sdmResources].size

  revocations = 0
  current.each { |aid, curRes|
    desRes = desired[aid]
    desRes = [] if not desired[aid]
    curRes.each { |r|
      if not(desRes.include? r[:resource_id])
        if plan
          puts "Plan: revoke %s from user %s\n" % [r[:resource_id], aid]
        else
          client.account_grants.delete(r[:id])
        end
        report[:revocations].push(r[:id])
        revocations += 1
      end
    }
  }
  report[:permissionsRevoked] = revocations

  grants = 0
  desired.each { |aid, desRes|
    curRes = current[aid]
    curRes = [] if not current[aid]
    desRes.each { |r|
      if not(curRes.map { |c| c[:resource_id] }.include? r)
        ag = SDM::AccountGrant.new()
        ag.account_id = aid
        ag.resource_id = r
        if plan
          puts "Plan: grant %s to user %s\n" % [r, aid]
        else
          client.account_grants.create(ag)
        end
        report[:grants].push(ag)
        grants += 1
      end
    }
  }
  report[:permissionsGranted] = grants

  report[:complete] = Time.now

  if verbose
    puts report.to_json
  else
    puts "%d Okta users, %d strongDM users, %d overlapping users, %d grants, %d revocations" % [okta_users.size, accounts.size, overlapping, grants, revocations]
  end
end