601 lines
19 KiB
Go
601 lines
19 KiB
Go
|
package okta
|
|||
|
|
|||
|
import (
|
|||
|
"errors"
|
|||
|
"fmt"
|
|||
|
"net/url"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
const (
|
|||
|
profileEmailFilter = "profile.email"
|
|||
|
profileLoginFilter = "profile.login"
|
|||
|
profileStatusFilter = "status"
|
|||
|
profileIDFilter = "id"
|
|||
|
profileFirstNameFilter = "profile.firstName"
|
|||
|
profileLastNameFilter = "profile.lastName"
|
|||
|
profileLastUpdatedFilter = "lastUpdated"
|
|||
|
// UserStatusActive is a constant to represent OKTA User State returned by the API
|
|||
|
UserStatusActive = "ACTIVE"
|
|||
|
// UserStatusStaged is a constant to represent OKTA User State returned by the API
|
|||
|
UserStatusStaged = "STAGED"
|
|||
|
// UserStatusProvisioned is a constant to represent OKTA User State returned by the API
|
|||
|
UserStatusProvisioned = "PROVISIONED"
|
|||
|
// UserStatusRecovery is a constant to represent OKTA User State returned by the API
|
|||
|
UserStatusRecovery = "RECOVERY"
|
|||
|
// UserStatusLockedOut is a constant to represent OKTA User State returned by the API
|
|||
|
UserStatusLockedOut = "LOCKED_OUT"
|
|||
|
// UserStatusPasswordExpired is a constant to represent OKTA User State returned by the API
|
|||
|
UserStatusPasswordExpired = "PASSWORD_EXPIRED"
|
|||
|
// UserStatusSuspended is a constant to represent OKTA User State returned by the API
|
|||
|
UserStatusSuspended = "SUSPENDED"
|
|||
|
// UserStatusDeprovisioned is a constant to represent OKTA User State returned by the API
|
|||
|
UserStatusDeprovisioned = "DEPROVISIONED"
|
|||
|
|
|||
|
oktaFilterTimeFormat = "2006-01-02T15:05:05.000Z"
|
|||
|
)
|
|||
|
|
|||
|
// UsersService handles communication with the User data related
|
|||
|
// methods of the OKTA API.
|
|||
|
type UsersService service
|
|||
|
|
|||
|
// ActivationResponse - Response coming back from a user activation
|
|||
|
type activationResponse struct {
|
|||
|
ActivationURL string `json:"activationUrl"`
|
|||
|
}
|
|||
|
|
|||
|
type provider struct {
|
|||
|
Name string `json:"name,omitempty"`
|
|||
|
Type string `json:"type,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
type recoveryQuestion struct {
|
|||
|
Question string `json:"question,omitempty"`
|
|||
|
Answer string `json:"answer,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
type passwordValue struct {
|
|||
|
Value string `json:"value,omitempty"`
|
|||
|
}
|
|||
|
type credentials struct {
|
|||
|
Password *passwordValue `json:"password,omitempty"`
|
|||
|
Provider *provider `json:"provider,omitempty"`
|
|||
|
RecoveryQuestion *recoveryQuestion `json:"recovery_question,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
type userProfile struct {
|
|||
|
Email string `json:"email"`
|
|||
|
FirstName string `json:"firstName"`
|
|||
|
LastName string `json:"lastName"`
|
|||
|
Login string `json:"login"`
|
|||
|
MobilePhone string `json:"mobilePhone,omitempty"`
|
|||
|
SecondEmail string `json:"secondEmail,omitempty"`
|
|||
|
PsEmplid string `json:"psEmplid,omitempty"`
|
|||
|
NickName string `json:"nickname,omitempty"`
|
|||
|
DisplayName string `json:"displayName,omitempty"`
|
|||
|
|
|||
|
ProfileURL string `json:"profileUrl,omitempty"`
|
|||
|
PreferredLanguage string `json:"preferredLanguage,omitempty"`
|
|||
|
UserType string `json:"userType,omitempty"`
|
|||
|
Organization string `json:"organization,omitempty"`
|
|||
|
Title string `json:"title,omitempty"`
|
|||
|
Division string `json:"division,omitempty"`
|
|||
|
Department string `json:"department,omitempty"`
|
|||
|
CostCenter string `json:"costCenter,omitempty"`
|
|||
|
EmployeeNumber string `json:"employeeNumber,omitempty"`
|
|||
|
PrimaryPhone string `json:"primaryPhone,omitempty"`
|
|||
|
StreetAddress string `json:"streetAddress,omitempty"`
|
|||
|
City string `json:"city,omitempty"`
|
|||
|
State string `json:"state,omitempty"`
|
|||
|
ZipCode string `json:"zipCode,omitempty"`
|
|||
|
CountryCode string `json:"countryCode,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
type userLinks struct {
|
|||
|
ChangePassword struct {
|
|||
|
Href string `json:"href"`
|
|||
|
} `json:"changePassword"`
|
|||
|
ChangeRecoveryQuestion struct {
|
|||
|
Href string `json:"href"`
|
|||
|
} `json:"changeRecoveryQuestion"`
|
|||
|
Deactivate struct {
|
|||
|
Href string `json:"href"`
|
|||
|
} `json:"deactivate"`
|
|||
|
ExpirePassword struct {
|
|||
|
Href string `json:"href"`
|
|||
|
} `json:"expirePassword"`
|
|||
|
ForgotPassword struct {
|
|||
|
Href string `json:"href"`
|
|||
|
} `json:"forgotPassword"`
|
|||
|
ResetFactors struct {
|
|||
|
Href string `json:"href"`
|
|||
|
} `json:"resetFactors"`
|
|||
|
ResetPassword struct {
|
|||
|
Href string `json:"href"`
|
|||
|
} `json:"resetPassword"`
|
|||
|
}
|
|||
|
|
|||
|
// User is a struct that represents a user object from OKTA.
|
|||
|
type User struct {
|
|||
|
Activated string `json:"activated,omitempty"`
|
|||
|
Created string `json:"created,omitempty"`
|
|||
|
Credentials credentials `json:"credentials,omitempty"`
|
|||
|
ID string `json:"id,omitempty"`
|
|||
|
LastLogin string `json:"lastLogin,omitempty"`
|
|||
|
LastUpdated string `json:"lastUpdated,omitempty"`
|
|||
|
PasswordChanged string `json:"passwordChanged,omitempty"`
|
|||
|
Profile userProfile `json:"profile"`
|
|||
|
Status string `json:"status,omitempty"`
|
|||
|
StatusChanged string `json:"statusChanged,omitempty"`
|
|||
|
Links userLinks `json:"_links,omitempty"`
|
|||
|
MFAFactors []userMFAFactor `json:"-,omitempty"`
|
|||
|
Groups []Group `json:"-,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
type userMFAFactor struct {
|
|||
|
ID string `json:"id,omitempty"`
|
|||
|
FactorType string `json:"factorType,omitempty"`
|
|||
|
Provider string `json:"provider,omitempty"`
|
|||
|
VendorName string `json:"vendorName,omitempty"`
|
|||
|
Status string `json:"status,omitempty"`
|
|||
|
Created time.Time `json:"created,omitempty"`
|
|||
|
LastUpdated time.Time `json:"lastUpdated,omitempty"`
|
|||
|
Profile struct {
|
|||
|
CredentialID string `json:"credentialId,omitempty"`
|
|||
|
} `json:"profile,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
// NewUser object to create user objects in OKTA
|
|||
|
type NewUser struct {
|
|||
|
Profile userProfile `json:"profile"`
|
|||
|
Credentials *credentials `json:"credentials,omitempty"`
|
|||
|
}
|
|||
|
|
|||
|
type newPasswordSet struct {
|
|||
|
Credentials credentials `json:"credentials"`
|
|||
|
}
|
|||
|
|
|||
|
type resetPasswordResponse struct {
|
|||
|
ResetPasswordURL string `json:"resetPasswordUrl"`
|
|||
|
}
|
|||
|
|
|||
|
// NewUser - Returns a new user object. This is used to create users in OKTA. It only has the properties that
|
|||
|
// OKTA will take as input. The "User" object has more feilds that are OKTA returned like the ID, etc
|
|||
|
func (s *UsersService) NewUser() NewUser {
|
|||
|
return NewUser{}
|
|||
|
}
|
|||
|
|
|||
|
// SetPassword Adds a specified password to the new User
|
|||
|
func (u *NewUser) SetPassword(passwordIn string) {
|
|||
|
|
|||
|
if passwordIn != "" {
|
|||
|
|
|||
|
pass := new(passwordValue)
|
|||
|
pass.Value = passwordIn
|
|||
|
|
|||
|
var cred *credentials
|
|||
|
if u.Credentials == nil {
|
|||
|
cred = new(credentials)
|
|||
|
} else {
|
|||
|
cred = u.Credentials
|
|||
|
}
|
|||
|
|
|||
|
cred.Password = pass
|
|||
|
u.Credentials = cred
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// SetRecoveryQuestion - Sets a custom security question and answer on a user object
|
|||
|
func (u *NewUser) SetRecoveryQuestion(questionIn string, answerIn string) {
|
|||
|
|
|||
|
if questionIn != "" && answerIn != "" {
|
|||
|
recovery := new(recoveryQuestion)
|
|||
|
|
|||
|
recovery.Question = questionIn
|
|||
|
recovery.Answer = answerIn
|
|||
|
|
|||
|
var cred *credentials
|
|||
|
if u.Credentials == nil {
|
|||
|
cred = new(credentials)
|
|||
|
} else {
|
|||
|
cred = u.Credentials
|
|||
|
}
|
|||
|
cred.RecoveryQuestion = recovery
|
|||
|
u.Credentials = cred
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (u User) String() string {
|
|||
|
return stringify(u)
|
|||
|
// return fmt.Sprintf("ID: %v \tLogin: %v", u.ID, u.Profile.Login)
|
|||
|
}
|
|||
|
|
|||
|
// GetByID returns a user object for a specific OKTA ID.
|
|||
|
// Generally the id input string is the cryptic OKTA key value from User.ID. However, the OKTA API may accept other values like "me", or login shortname
|
|||
|
func (s *UsersService) GetByID(id string) (*User, *Response, error) {
|
|||
|
u := fmt.Sprintf("users/%v", id)
|
|||
|
req, err := s.client.NewRequest("GET", u, nil)
|
|||
|
if err != nil {
|
|||
|
return nil, nil, err
|
|||
|
}
|
|||
|
|
|||
|
user := new(User)
|
|||
|
resp, err := s.client.Do(req, user)
|
|||
|
if err != nil {
|
|||
|
return nil, resp, err
|
|||
|
}
|
|||
|
|
|||
|
return user, resp, err
|
|||
|
}
|
|||
|
|
|||
|
// UserListFilterOptions is a struct that you can populate which will "filter" user searches
|
|||
|
// the exported struct fields should allow you to do different filters based on what is allowed in the OKTA API.
|
|||
|
// The filter OKTA API is limited in the fields it can search
|
|||
|
// NOTE: In the current form you can't add parenthesis and ordering
|
|||
|
// OKTA API Supports only a limited number of properties:
|
|||
|
// status, lastUpdated, id, profile.login, profile.email, profile.firstName, and profile.lastName.
|
|||
|
// http://developer.okta.com/docs/api/resources/users.html#list-users-with-a-filter
|
|||
|
type UserListFilterOptions struct {
|
|||
|
Limit int `url:"limit,omitempty"`
|
|||
|
EmailEqualTo string `url:"-"`
|
|||
|
LoginEqualTo string `url:"-"`
|
|||
|
StatusEqualTo string `url:"-"`
|
|||
|
IDEqualTo string `url:"-"`
|
|||
|
|
|||
|
FirstNameEqualTo string `url:"-"`
|
|||
|
LastNameEqualTo string `url:"-"`
|
|||
|
// API documenation says you can search with "starts with" but these don't work
|
|||
|
|
|||
|
// FirstNameStartsWith string `url:"-"`
|
|||
|
// LastNameStartsWith string `url:"-"`
|
|||
|
|
|||
|
// This will be built by internal - may not need to export
|
|||
|
FilterString string `url:"filter,omitempty"`
|
|||
|
NextURL *url.URL `url:"-"`
|
|||
|
GetAllPages bool `url:"-"`
|
|||
|
NumberOfPages int `url:"-"`
|
|||
|
LastUpdated dateFilter `url:"-"`
|
|||
|
}
|
|||
|
|
|||
|
// PopulateGroups will populate the groups a user is a member of. You pass in a pointer to an existing users
|
|||
|
func (s *UsersService) PopulateGroups(user *User) (*Response, error) {
|
|||
|
u := fmt.Sprintf("users/%v/groups", user.ID)
|
|||
|
req, err := s.client.NewRequest("GET", u, nil)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
// TODO: If user has more than 200 groups this will only return those first 200
|
|||
|
resp, err := s.client.Do(req, &user.Groups)
|
|||
|
if err != nil {
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
// PopulateEnrolledFactors will populate the Enrolled MFA Factors a user is a member of.
|
|||
|
// You pass in a pointer to an existing users
|
|||
|
// http://developer.okta.com/docs/api/resources/factors.html#list-enrolled-factors
|
|||
|
func (s *UsersService) PopulateEnrolledFactors(user *User) (*Response, error) {
|
|||
|
u := fmt.Sprintf("users/%v/factors", user.ID)
|
|||
|
req, err := s.client.NewRequest("GET", u, nil)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
// TODO: If user has more than 200 groups this will only return those first 200
|
|||
|
resp, err := s.client.Do(req, &user.MFAFactors)
|
|||
|
if err != nil {
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
// List users with status of LOCKED_OUT
|
|||
|
// filter=status eq "LOCKED_OUT"
|
|||
|
// List users updated after 06/01/2013 but before 01/01/2014
|
|||
|
// filter=lastUpdated gt "2013-06-01T00:00:00.000Z" and lastUpdated lt "2014-01-01T00:00:00.000Z"
|
|||
|
// List users updated after 06/01/2013 but before 01/01/2014 with a status of ACTIVE
|
|||
|
// filter=lastUpdated gt "2013-06-01T00:00:00.000Z" and lastUpdated lt "2014-01-01T00:00:00.000Z" and status eq "ACTIVE"
|
|||
|
// TODO - Currently no way to do parenthesis
|
|||
|
// List users updated after 06/01/2013 but with a status of LOCKED_OUT or RECOVERY
|
|||
|
// filter=lastUpdated gt "2013-06-01T00:00:00.000Z" and (status eq "LOCKED_OUT" or status eq "RECOVERY")
|
|||
|
|
|||
|
// OTKA API docs: http://developer.okta.com/docs/api/resources/users.html#list-users-with-a-filter
|
|||
|
|
|||
|
func appendToFilterString(currFilterString string, appendFilterKey string, appendFilterOperator string, appendFilterValue string) (rs string) {
|
|||
|
if currFilterString != "" {
|
|||
|
rs = fmt.Sprintf("%v and %v %v \"%v\"", currFilterString, appendFilterKey, appendFilterOperator, appendFilterValue)
|
|||
|
} else {
|
|||
|
rs = fmt.Sprintf("%v %v \"%v\"", appendFilterKey, appendFilterOperator, appendFilterValue)
|
|||
|
}
|
|||
|
|
|||
|
return rs
|
|||
|
}
|
|||
|
|
|||
|
// ListWithFilter will use the input UserListFilterOptions to find users and return a paged result set
|
|||
|
func (s *UsersService) ListWithFilter(opt *UserListFilterOptions) ([]User, *Response, error) {
|
|||
|
var u string
|
|||
|
var err error
|
|||
|
|
|||
|
pagesRetreived := 0
|
|||
|
|
|||
|
if opt.NextURL != nil {
|
|||
|
u = opt.NextURL.String()
|
|||
|
} else {
|
|||
|
if opt.EmailEqualTo != "" {
|
|||
|
opt.FilterString = appendToFilterString(opt.FilterString, profileEmailFilter, FilterEqualOperator, opt.EmailEqualTo)
|
|||
|
}
|
|||
|
if opt.LoginEqualTo != "" {
|
|||
|
opt.FilterString = appendToFilterString(opt.FilterString, profileLoginFilter, FilterEqualOperator, opt.LoginEqualTo)
|
|||
|
}
|
|||
|
|
|||
|
if opt.StatusEqualTo != "" {
|
|||
|
opt.FilterString = appendToFilterString(opt.FilterString, profileStatusFilter, FilterEqualOperator, opt.StatusEqualTo)
|
|||
|
}
|
|||
|
|
|||
|
if opt.IDEqualTo != "" {
|
|||
|
opt.FilterString = appendToFilterString(opt.FilterString, profileIDFilter, FilterEqualOperator, opt.IDEqualTo)
|
|||
|
}
|
|||
|
|
|||
|
if opt.FirstNameEqualTo != "" {
|
|||
|
opt.FilterString = appendToFilterString(opt.FilterString, profileFirstNameFilter, FilterEqualOperator, opt.FirstNameEqualTo)
|
|||
|
}
|
|||
|
|
|||
|
if opt.LastNameEqualTo != "" {
|
|||
|
opt.FilterString = appendToFilterString(opt.FilterString, profileLastNameFilter, FilterEqualOperator, opt.LastNameEqualTo)
|
|||
|
}
|
|||
|
|
|||
|
// API documenation says you can search with "starts with" but these don't work
|
|||
|
// if opt.FirstNameStartsWith != "" {
|
|||
|
// opt.FilterString = appendToFilterString(opt.FilterString, profileFirstNameFilter, filterStartsWithOperator, opt.FirstNameStartsWith)
|
|||
|
// }
|
|||
|
|
|||
|
// if opt.LastNameStartsWith != "" {
|
|||
|
// opt.FilterString = appendToFilterString(opt.FilterString, profileLastNameFilter, filterStartsWithOperator, opt.LastNameStartsWith)
|
|||
|
// }
|
|||
|
|
|||
|
if !opt.LastUpdated.Value.IsZero() {
|
|||
|
opt.FilterString = appendToFilterString(opt.FilterString, profileLastUpdatedFilter, opt.LastUpdated.Operator, opt.LastUpdated.Value.UTC().Format(oktaFilterTimeFormat))
|
|||
|
}
|
|||
|
|
|||
|
if opt.Limit == 0 {
|
|||
|
opt.Limit = defaultLimit
|
|||
|
}
|
|||
|
|
|||
|
u, err = addOptions("users", opt)
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return nil, nil, err
|
|||
|
}
|
|||
|
|
|||
|
req, err := s.client.NewRequest("GET", u, nil)
|
|||
|
if err != nil {
|
|||
|
return nil, nil, err
|
|||
|
}
|
|||
|
users := make([]User, 1)
|
|||
|
resp, err := s.client.Do(req, &users)
|
|||
|
if err != nil {
|
|||
|
return nil, resp, err
|
|||
|
}
|
|||
|
|
|||
|
pagesRetreived++
|
|||
|
|
|||
|
if (opt.NumberOfPages > 0 && pagesRetreived < opt.NumberOfPages) || opt.GetAllPages {
|
|||
|
|
|||
|
for {
|
|||
|
|
|||
|
if pagesRetreived == opt.NumberOfPages {
|
|||
|
break
|
|||
|
}
|
|||
|
if resp.NextURL != nil {
|
|||
|
var userPage []User
|
|||
|
pageOption := new(UserListFilterOptions)
|
|||
|
pageOption.NextURL = resp.NextURL
|
|||
|
pageOption.NumberOfPages = 1
|
|||
|
pageOption.Limit = opt.Limit
|
|||
|
|
|||
|
userPage, resp, err = s.ListWithFilter(pageOption)
|
|||
|
if err != nil {
|
|||
|
return users, resp, err
|
|||
|
} else {
|
|||
|
users = append(users, userPage...)
|
|||
|
pagesRetreived++
|
|||
|
}
|
|||
|
} else {
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return users, resp, err
|
|||
|
}
|
|||
|
|
|||
|
// Create - Creates a new user. You must pass in a "newUser" object created from Users.NewUser()
|
|||
|
// There are many differnt reasons that OKTA may reject the request so you have to check the error messages
|
|||
|
func (s *UsersService) Create(userIn NewUser, createAsActive bool) (*User, *Response, error) {
|
|||
|
|
|||
|
u := fmt.Sprintf("users?activate=%v", createAsActive)
|
|||
|
|
|||
|
req, err := s.client.NewRequest("POST", u, userIn)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return nil, nil, err
|
|||
|
}
|
|||
|
|
|||
|
newUser := new(User)
|
|||
|
resp, err := s.client.Do(req, newUser)
|
|||
|
if err != nil {
|
|||
|
return nil, resp, err
|
|||
|
}
|
|||
|
|
|||
|
return newUser, resp, err
|
|||
|
}
|
|||
|
|
|||
|
// Activate Activates a user. You can have OKTA send an email by including a "sendEmail=true"
|
|||
|
// If you pass in sendEmail=false, then activationResponse.ActivationURL will have a string URL that
|
|||
|
// can be sent to the end user. You can discard response if sendEmail=true
|
|||
|
func (s *UsersService) Activate(id string, sendEmail bool) (*activationResponse, *Response, error) {
|
|||
|
u := fmt.Sprintf("users/%v/lifecycle/activate?sendEmail=%v", id, sendEmail)
|
|||
|
|
|||
|
req, err := s.client.NewRequest("POST", u, nil)
|
|||
|
if err != nil {
|
|||
|
return nil, nil, err
|
|||
|
}
|
|||
|
|
|||
|
activationInfo := new(activationResponse)
|
|||
|
resp, err := s.client.Do(req, activationInfo)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return nil, resp, err
|
|||
|
}
|
|||
|
|
|||
|
return activationInfo, resp, err
|
|||
|
}
|
|||
|
|
|||
|
// Deactivate - Deactivates a user
|
|||
|
func (s *UsersService) Deactivate(id string) (*Response, error) {
|
|||
|
u := fmt.Sprintf("users/%v/lifecycle/deactivate", id)
|
|||
|
|
|||
|
req, err := s.client.NewRequest("POST", u, nil)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
resp, err := s.client.Do(req, nil)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
// Suspend - Suspends a user - If user is NOT active an Error will come back based on OKTA API:
|
|||
|
// http://developer.okta.com/docs/api/resources/users.html#suspend-user
|
|||
|
func (s *UsersService) Suspend(id string) (*Response, error) {
|
|||
|
u := fmt.Sprintf("users/%v/lifecycle/suspend", id)
|
|||
|
|
|||
|
req, err := s.client.NewRequest("POST", u, nil)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
resp, err := s.client.Do(req, nil)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
// Unsuspend - Unsuspends a user - If user is NOT SUSPENDED, an Error will come back based on OKTA API:
|
|||
|
// http://developer.okta.com/docs/api/resources/users.html#unsuspend-user
|
|||
|
func (s *UsersService) Unsuspend(id string) (*Response, error) {
|
|||
|
u := fmt.Sprintf("users/%v/lifecycle/unsuspend", id)
|
|||
|
|
|||
|
req, err := s.client.NewRequest("POST", u, nil)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
resp, err := s.client.Do(req, nil)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
// Unlock - Unlocks a user - Per docs, only for OKTA Mastered Account
|
|||
|
// http://developer.okta.com/docs/api/resources/users.html#unlock-user
|
|||
|
func (s *UsersService) Unlock(id string) (*Response, error) {
|
|||
|
u := fmt.Sprintf("users/%v/lifecycle/unlock", id)
|
|||
|
|
|||
|
req, err := s.client.NewRequest("POST", u, nil)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
resp, err := s.client.Do(req, nil)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
// SetPassword - Sets a user password to an Admin provided String
|
|||
|
func (s *UsersService) SetPassword(id string, newPassword string) (*User, *Response, error) {
|
|||
|
|
|||
|
if id == "" || newPassword == "" {
|
|||
|
return nil, nil, errors.New("please provide a User ID and Password")
|
|||
|
}
|
|||
|
|
|||
|
passwordUpdate := new(newPasswordSet)
|
|||
|
|
|||
|
pass := new(passwordValue)
|
|||
|
pass.Value = newPassword
|
|||
|
|
|||
|
passwordUpdate.Credentials.Password = pass
|
|||
|
|
|||
|
u := fmt.Sprintf("users/%v", id)
|
|||
|
req, err := s.client.NewRequest("POST", u, passwordUpdate)
|
|||
|
if err != nil {
|
|||
|
return nil, nil, err
|
|||
|
}
|
|||
|
|
|||
|
user := new(User)
|
|||
|
resp, err := s.client.Do(req, user)
|
|||
|
if err != nil {
|
|||
|
return nil, resp, err
|
|||
|
}
|
|||
|
|
|||
|
return user, resp, err
|
|||
|
}
|
|||
|
|
|||
|
// ResetPassword - Generates a one-time token (OTT) that can be used to reset a user’s password.
|
|||
|
// The OTT link can be automatically emailed to the user or returned to the API caller and distributed using a custom flow.
|
|||
|
// http://developer.okta.com/docs/api/resources/users.html#reset-password
|
|||
|
// If you pass in sendEmail=false, then resetPasswordResponse.resetPasswordUrl will have a string URL that
|
|||
|
// can be sent to the end user. You can discard response if sendEmail=true
|
|||
|
func (s *UsersService) ResetPassword(id string, sendEmail bool) (*resetPasswordResponse, *Response, error) {
|
|||
|
u := fmt.Sprintf("users/%v/lifecycle/reset_password?sendEmail=%v", id, sendEmail)
|
|||
|
|
|||
|
req, err := s.client.NewRequest("POST", u, nil)
|
|||
|
if err != nil {
|
|||
|
return nil, nil, err
|
|||
|
}
|
|||
|
|
|||
|
resetInfo := new(resetPasswordResponse)
|
|||
|
resp, err := s.client.Do(req, resetInfo)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return nil, resp, err
|
|||
|
}
|
|||
|
|
|||
|
return resetInfo, resp, err
|
|||
|
}
|
|||
|
|
|||
|
// PopulateMFAFactors will populate the MFA Factors a user is a member of. You pass in a pointer to an existing users
|
|||
|
func (s *UsersService) PopulateMFAFactors(user *User) (*Response, error) {
|
|||
|
u := fmt.Sprintf("users/%v/factors", user.ID)
|
|||
|
|
|||
|
req, err := s.client.NewRequest("GET", u, nil)
|
|||
|
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
resp, err := s.client.Do(req, &user.MFAFactors)
|
|||
|
if err != nil {
|
|||
|
return resp, err
|
|||
|
}
|
|||
|
|
|||
|
return resp, err
|
|||
|
}
|