mirror of
https://github.com/Luzifer/nginx-sso.git
synced 2024-12-21 05:11:17 +00:00
725 lines
18 KiB
Go
725 lines
18 KiB
Go
package admin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
|
|
"github.com/duosecurity/duo_api_golang"
|
|
)
|
|
|
|
// Client provides access to Duo's admin API.
|
|
type Client struct {
|
|
duoapi.DuoApi
|
|
}
|
|
|
|
type ListResultMetadata struct {
|
|
NextOffset json.Number `json:"next_offset"`
|
|
PrevOffset json.Number `json:"prev_offset"`
|
|
TotalObjects json.Number `json:"total_objects"`
|
|
}
|
|
|
|
type ListResult struct {
|
|
Metadata ListResultMetadata `json:"metadata"`
|
|
}
|
|
|
|
func (l *ListResult) metadata() ListResultMetadata {
|
|
return l.Metadata
|
|
}
|
|
|
|
// New initializes an admin API Client struct.
|
|
func New(base duoapi.DuoApi) *Client {
|
|
return &Client{base}
|
|
}
|
|
|
|
// User models a single user.
|
|
type User struct {
|
|
Alias1 *string
|
|
Alias2 *string
|
|
Alias3 *string
|
|
Alias4 *string
|
|
Created uint64
|
|
Email string
|
|
FirstName *string
|
|
Groups []Group
|
|
LastDirectorySync *uint64 `json:"last_directory_sync"`
|
|
LastLogin *uint64 `json:"last_login"`
|
|
LastName *string
|
|
Notes string
|
|
Phones []Phone
|
|
RealName *string
|
|
Status string
|
|
Tokens []Token
|
|
UserID string `json:"user_id"`
|
|
Username string
|
|
}
|
|
|
|
// Group models a group to which users may belong.
|
|
type Group struct {
|
|
Desc string
|
|
GroupID string `json:"group_id"`
|
|
MobileOTPEnabled bool `json:"mobile_otp_enabled"`
|
|
Name string
|
|
PushEnabled bool `json:"push_enabled"`
|
|
SMSEnabled bool `json:"sms_enabled"`
|
|
Status string
|
|
VoiceEnabled bool `json:"voice_enabled"`
|
|
}
|
|
|
|
// Phone models a user's phone.
|
|
type Phone struct {
|
|
Activated bool
|
|
Capabilities []string
|
|
Encrypted string
|
|
Extension string
|
|
Fingerprint string
|
|
Name string
|
|
Number string
|
|
PhoneID string `json:"phone_id"`
|
|
Platform string
|
|
Postdelay string
|
|
Predelay string
|
|
Screenlock string
|
|
SMSPasscodesSent bool
|
|
Type string
|
|
Users []User
|
|
}
|
|
|
|
// Token models a hardware security token.
|
|
type Token struct {
|
|
TokenID string `json:"token_id"`
|
|
Type string
|
|
Serial string
|
|
TOTPStep *int `json:"totp_step"`
|
|
Users []User
|
|
}
|
|
|
|
// U2FToken models a U2F security token.
|
|
type U2FToken struct {
|
|
DateAdded uint64 `json:"date_added"`
|
|
RegistrationID string `json:"registration_id"`
|
|
User *User
|
|
}
|
|
|
|
// Common URL options
|
|
|
|
// Limit sets the optional limit parameter for an API request.
|
|
func Limit(limit uint64) func(*url.Values) {
|
|
return func(opts *url.Values) {
|
|
opts.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
}
|
|
|
|
// Offset sets the optional offset parameter for an API request.
|
|
func Offset(offset uint64) func(*url.Values) {
|
|
return func(opts *url.Values) {
|
|
opts.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
}
|
|
|
|
// User methods
|
|
|
|
// GetUsersUsername sets the optional username parameter for a GetUsers request.
|
|
func GetUsersUsername(name string) func(*url.Values) {
|
|
return func(opts *url.Values) {
|
|
opts.Set("username", name)
|
|
}
|
|
}
|
|
|
|
// GetUsersResult models responses containing a list of users.
|
|
type GetUsersResult struct {
|
|
duoapi.StatResult
|
|
ListResult
|
|
Response []User
|
|
}
|
|
|
|
// GetUserResult models responses containing a single user.
|
|
type GetUserResult struct {
|
|
duoapi.StatResult
|
|
Response User
|
|
}
|
|
|
|
func (result *GetUsersResult) getResponse() interface{} {
|
|
return result.Response
|
|
}
|
|
|
|
func (result *GetUsersResult) appendResponse(users interface{}) {
|
|
asserted_users := users.([]User)
|
|
result.Response = append(result.Response, asserted_users...)
|
|
}
|
|
|
|
// GetUsers calls GET /admin/v1/users
|
|
// See https://duo.com/docs/adminapi#retrieve-users
|
|
func (c *Client) GetUsers(options ...func(*url.Values)) (*GetUsersResult, error) {
|
|
params := url.Values{}
|
|
for _, o := range options {
|
|
o(¶ms)
|
|
}
|
|
|
|
cb := func(params url.Values) (responsePage, error) {
|
|
return c.retrieveUsers(params)
|
|
}
|
|
response, err := c.retrieveItems(params, cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response.(*GetUsersResult), nil
|
|
}
|
|
|
|
type responsePage interface {
|
|
metadata() ListResultMetadata
|
|
getResponse() interface{}
|
|
appendResponse(interface{})
|
|
}
|
|
|
|
type pageFetcher func(params url.Values) (responsePage, error)
|
|
|
|
func (c *Client) retrieveItems(
|
|
params url.Values,
|
|
fetcher pageFetcher,
|
|
) (responsePage, error) {
|
|
if params.Get("offset") == "" {
|
|
params.Set("offset", "0")
|
|
}
|
|
|
|
if params.Get("limit") == "" {
|
|
params.Set("limit", "100")
|
|
accumulator, firstErr := fetcher(params)
|
|
|
|
if firstErr != nil {
|
|
return nil, firstErr
|
|
}
|
|
|
|
params.Set("offset", accumulator.metadata().NextOffset.String())
|
|
for params.Get("offset") != "" {
|
|
nextResult, err := fetcher(params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
nextResult.appendResponse(accumulator.getResponse())
|
|
accumulator = nextResult
|
|
params.Set("offset", accumulator.metadata().NextOffset.String())
|
|
}
|
|
return accumulator, nil
|
|
}
|
|
|
|
return fetcher(params)
|
|
}
|
|
|
|
func (c *Client) retrieveUsers(params url.Values) (*GetUsersResult, error) {
|
|
_, body, err := c.SignedCall(http.MethodGet, "/admin/v1/users", params, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetUsersResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetUser calls GET /admin/v1/users/:user_id
|
|
// See https://duo.com/docs/adminapi#retrieve-user-by-id
|
|
func (c *Client) GetUser(userID string) (*GetUserResult, error) {
|
|
path := fmt.Sprintf("/admin/v1/users/%s", userID)
|
|
|
|
_, body, err := c.SignedCall(http.MethodGet, path, nil, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetUserResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetUserGroups calls GET /admin/v1/users/:user_id/groups
|
|
// See https://duo.com/docs/adminapi#retrieve-groups-by-user-id
|
|
func (c *Client) GetUserGroups(userID string, options ...func(*url.Values)) (*GetGroupsResult, error) {
|
|
params := url.Values{}
|
|
for _, o := range options {
|
|
o(¶ms)
|
|
}
|
|
|
|
cb := func(params url.Values) (responsePage, error) {
|
|
return c.retrieveUserGroups(userID, params)
|
|
}
|
|
response, err := c.retrieveItems(params, cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response.(*GetGroupsResult), nil
|
|
}
|
|
|
|
func (c *Client) retrieveUserGroups(userID string, params url.Values) (*GetGroupsResult, error) {
|
|
path := fmt.Sprintf("/admin/v1/users/%s/groups", userID)
|
|
|
|
_, body, err := c.SignedCall(http.MethodGet, path, params, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetGroupsResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetUserPhones calls GET /admin/v1/users/:user_id/phones
|
|
// See https://duo.com/docs/adminapi#retrieve-phones-by-user-id
|
|
func (c *Client) GetUserPhones(userID string, options ...func(*url.Values)) (*GetPhonesResult, error) {
|
|
params := url.Values{}
|
|
for _, o := range options {
|
|
o(¶ms)
|
|
}
|
|
|
|
cb := func(params url.Values) (responsePage, error) {
|
|
return c.retrieveUserPhones(userID, params)
|
|
}
|
|
response, err := c.retrieveItems(params, cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response.(*GetPhonesResult), nil
|
|
}
|
|
|
|
func (c *Client) retrieveUserPhones(userID string, params url.Values) (*GetPhonesResult, error) {
|
|
path := fmt.Sprintf("/admin/v1/users/%s/phones", userID)
|
|
|
|
_, body, err := c.SignedCall(http.MethodGet, path, params, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetPhonesResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetUserTokens calls GET /admin/v1/users/:user_id/tokens
|
|
// See https://duo.com/docs/adminapi#retrieve-hardware-tokens-by-user-id
|
|
func (c *Client) GetUserTokens(userID string, options ...func(*url.Values)) (*GetTokensResult, error) {
|
|
params := url.Values{}
|
|
for _, o := range options {
|
|
o(¶ms)
|
|
}
|
|
|
|
cb := func(params url.Values) (responsePage, error) {
|
|
return c.retrieveUserTokens(userID, params)
|
|
}
|
|
response, err := c.retrieveItems(params, cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response.(*GetTokensResult), nil
|
|
}
|
|
|
|
func (c *Client) retrieveUserTokens(userID string, params url.Values) (*GetTokensResult, error) {
|
|
path := fmt.Sprintf("/admin/v1/users/%s/tokens", userID)
|
|
|
|
_, body, err := c.SignedCall(http.MethodGet, path, params, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetTokensResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// StringResult models responses containing a simple string.
|
|
type StringResult struct {
|
|
duoapi.StatResult
|
|
Response string
|
|
}
|
|
|
|
// AssociateUserToken calls POST /admin/v1/users/:user_id/tokens
|
|
// See https://duo.com/docs/adminapi#associate-hardware-token-with-user
|
|
func (c *Client) AssociateUserToken(userID, tokenID string) (*StringResult, error) {
|
|
path := fmt.Sprintf("/admin/v1/users/%s/tokens", userID)
|
|
|
|
params := url.Values{}
|
|
params.Set("token_id", tokenID)
|
|
|
|
_, body, err := c.SignedCall(http.MethodPost, path, params, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &StringResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetUserU2FTokens calls GET /admin/v1/users/:user_id/u2ftokens
|
|
// See https://duo.com/docs/adminapi#retrieve-u2f-tokens-by-user-id
|
|
func (c *Client) GetUserU2FTokens(userID string, options ...func(*url.Values)) (*GetU2FTokensResult, error) {
|
|
params := url.Values{}
|
|
for _, o := range options {
|
|
o(¶ms)
|
|
}
|
|
|
|
cb := func(params url.Values) (responsePage, error) {
|
|
return c.retrieveUserU2FTokens(userID, params)
|
|
}
|
|
response, err := c.retrieveItems(params, cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response.(*GetU2FTokensResult), nil
|
|
}
|
|
|
|
func (c *Client) retrieveUserU2FTokens(userID string, params url.Values) (*GetU2FTokensResult, error) {
|
|
path := fmt.Sprintf("/admin/v1/users/%s/u2ftokens", userID)
|
|
|
|
_, body, err := c.SignedCall(http.MethodGet, path, params, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetU2FTokensResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// Group methods
|
|
|
|
// GetGroupsResult models responses containing a list of groups.
|
|
type GetGroupsResult struct {
|
|
duoapi.StatResult
|
|
ListResult
|
|
Response []Group
|
|
}
|
|
|
|
func (result *GetGroupsResult) getResponse() interface{} {
|
|
return result.Response
|
|
}
|
|
|
|
func (result *GetGroupsResult) appendResponse(groups interface{}) {
|
|
asserted_groups := groups.([]Group)
|
|
result.Response = append(result.Response, asserted_groups...)
|
|
}
|
|
|
|
// GetGroups calls GET /admin/v1/groups
|
|
// See https://duo.com/docs/adminapi#retrieve-groups
|
|
func (c *Client) GetGroups(options ...func(*url.Values)) (*GetGroupsResult, error) {
|
|
params := url.Values{}
|
|
for _, o := range options {
|
|
o(¶ms)
|
|
}
|
|
|
|
cb := func(params url.Values) (responsePage, error) {
|
|
return c.retrieveGroups(params)
|
|
}
|
|
response, err := c.retrieveItems(params, cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response.(*GetGroupsResult), nil
|
|
}
|
|
|
|
func (c *Client) retrieveGroups(params url.Values) (*GetGroupsResult, error) {
|
|
_, body, err := c.SignedCall(http.MethodGet, "/admin/v1/groups", params, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetGroupsResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetGroupResult models responses containing a single group.
|
|
type GetGroupResult struct {
|
|
duoapi.StatResult
|
|
Response Group
|
|
}
|
|
|
|
// GetGroup calls GET /admin/v2/group/:group_id
|
|
// See https://duo.com/docs/adminapi#get-group-info
|
|
func (c *Client) GetGroup(groupID string) (*GetGroupResult, error) {
|
|
path := fmt.Sprintf("/admin/v2/groups/%s", groupID)
|
|
|
|
_, body, err := c.SignedCall(http.MethodGet, path, nil, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetGroupResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// Phone methods
|
|
|
|
// GetPhonesNumber sets the optional number parameter for a GetPhones request.
|
|
func GetPhonesNumber(number string) func(*url.Values) {
|
|
return func(opts *url.Values) {
|
|
opts.Set("number", number)
|
|
}
|
|
}
|
|
|
|
// GetPhonesExtension sets the optional extension parameter for a GetPhones request.
|
|
func GetPhonesExtension(ext string) func(*url.Values) {
|
|
return func(opts *url.Values) {
|
|
opts.Set("extension", ext)
|
|
}
|
|
}
|
|
|
|
// GetPhonesResult models responses containing a list of phones.
|
|
type GetPhonesResult struct {
|
|
duoapi.StatResult
|
|
ListResult
|
|
Response []Phone
|
|
}
|
|
|
|
func (result *GetPhonesResult) getResponse() interface{} {
|
|
return result.Response
|
|
}
|
|
|
|
func (result *GetPhonesResult) appendResponse(phones interface{}) {
|
|
asserted_phones := phones.([]Phone)
|
|
result.Response = append(result.Response, asserted_phones...)
|
|
}
|
|
|
|
// GetPhones calls GET /admin/v1/phones
|
|
// See https://duo.com/docs/adminapi#phones
|
|
func (c *Client) GetPhones(options ...func(*url.Values)) (*GetPhonesResult, error) {
|
|
params := url.Values{}
|
|
for _, o := range options {
|
|
o(¶ms)
|
|
}
|
|
|
|
cb := func(params url.Values) (responsePage, error) {
|
|
return c.retrievePhones(params)
|
|
}
|
|
response, err := c.retrieveItems(params, cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response.(*GetPhonesResult), nil
|
|
}
|
|
|
|
func (c *Client) retrievePhones(params url.Values) (*GetPhonesResult, error) {
|
|
_, body, err := c.SignedCall(http.MethodGet, "/admin/v1/phones", params, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetPhonesResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetPhoneResult models responses containing a single phone.
|
|
type GetPhoneResult struct {
|
|
duoapi.StatResult
|
|
Response Phone
|
|
}
|
|
|
|
// GetPhone calls GET /admin/v1/phones/:phone_id
|
|
// See https://duo.com/docs/adminapi#retrieve-phone-by-id
|
|
func (c *Client) GetPhone(phoneID string) (*GetPhoneResult, error) {
|
|
path := fmt.Sprintf("/admin/v1/phones/%s", phoneID)
|
|
|
|
_, body, err := c.SignedCall(http.MethodGet, path, nil, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetPhoneResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// Token methods
|
|
|
|
// GetTokensTypeAndSerial sets the optional type and serial parameters for a GetTokens request.
|
|
func GetTokensTypeAndSerial(typ, serial string) func(*url.Values) {
|
|
return func(opts *url.Values) {
|
|
opts.Set("type", typ)
|
|
opts.Set("serial", serial)
|
|
}
|
|
}
|
|
|
|
// GetTokensResult models responses containing a list of tokens.
|
|
type GetTokensResult struct {
|
|
duoapi.StatResult
|
|
ListResult
|
|
Response []Token
|
|
}
|
|
|
|
func (result *GetTokensResult) getResponse() interface{} {
|
|
return result.Response
|
|
}
|
|
|
|
func (result *GetTokensResult) appendResponse(tokens interface{}) {
|
|
asserted_tokens := tokens.([]Token)
|
|
result.Response = append(result.Response, asserted_tokens...)
|
|
}
|
|
|
|
// GetTokens calls GET /admin/v1/tokens
|
|
// See https://duo.com/docs/adminapi#retrieve-hardware-tokens
|
|
func (c *Client) GetTokens(options ...func(*url.Values)) (*GetTokensResult, error) {
|
|
params := url.Values{}
|
|
for _, o := range options {
|
|
o(¶ms)
|
|
}
|
|
|
|
cb := func(params url.Values) (responsePage, error) {
|
|
return c.retrieveTokens(params)
|
|
}
|
|
response, err := c.retrieveItems(params, cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response.(*GetTokensResult), nil
|
|
}
|
|
|
|
func (c *Client) retrieveTokens(params url.Values) (*GetTokensResult, error) {
|
|
_, body, err := c.SignedCall(http.MethodGet, "/admin/v1/tokens", params, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetTokensResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetTokenResult models responses containing a single token.
|
|
type GetTokenResult struct {
|
|
duoapi.StatResult
|
|
Response Token
|
|
}
|
|
|
|
// GetToken calls GET /admin/v1/tokens/:token_id
|
|
// See https://duo.com/docs/adminapi#retrieve-hardware-tokens
|
|
func (c *Client) GetToken(tokenID string) (*GetTokenResult, error) {
|
|
path := fmt.Sprintf("/admin/v1/tokens/%s", tokenID)
|
|
|
|
_, body, err := c.SignedCall(http.MethodGet, path, nil, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetTokenResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// U2F token methods
|
|
|
|
// GetU2FTokensResult models responses containing a list of U2F tokens.
|
|
type GetU2FTokensResult struct {
|
|
duoapi.StatResult
|
|
ListResult
|
|
Response []U2FToken
|
|
}
|
|
|
|
func (result *GetU2FTokensResult) getResponse() interface{} {
|
|
return result.Response
|
|
}
|
|
|
|
func (result *GetU2FTokensResult) appendResponse(tokens interface{}) {
|
|
asserted_tokens := tokens.([]U2FToken)
|
|
result.Response = append(result.Response, asserted_tokens...)
|
|
}
|
|
|
|
// GetU2FTokens calls GET /admin/v1/u2ftokens
|
|
// See https://duo.com/docs/adminapi#retrieve-u2f-tokens
|
|
func (c *Client) GetU2FTokens(options ...func(*url.Values)) (*GetU2FTokensResult, error) {
|
|
params := url.Values{}
|
|
for _, o := range options {
|
|
o(¶ms)
|
|
}
|
|
|
|
cb := func(params url.Values) (responsePage, error) {
|
|
return c.retrieveU2FTokens(params)
|
|
}
|
|
response, err := c.retrieveItems(params, cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response.(*GetU2FTokensResult), nil
|
|
}
|
|
|
|
func (c *Client) retrieveU2FTokens(params url.Values) (*GetU2FTokensResult, error) {
|
|
_, body, err := c.SignedCall(http.MethodGet, "/admin/v1/u2ftokens", params, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetU2FTokensResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetU2FToken calls GET /admin/v1/u2ftokens/:registration_id
|
|
// See https://duo.com/docs/adminapi#retrieve-u2f-token-by-id
|
|
func (c *Client) GetU2FToken(registrationID string) (*GetU2FTokensResult, error) {
|
|
path := fmt.Sprintf("/admin/v1/u2ftokens/%s", registrationID)
|
|
|
|
_, body, err := c.SignedCall(http.MethodGet, path, nil, duoapi.UseTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &GetU2FTokensResult{}
|
|
err = json.Unmarshal(body, result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|