1package okta
2
3import (
4	"fmt"
5	"net/url"
6	"time"
7)
8
9// AppsService is a service to retreives applications from OKTA.
10type AppsService service
11
12// AppFilterOptions is used to generate a "Filter" to search for different Apps
13// The values here coorelate to API Search paramgters on the group API
14type AppFilterOptions struct {
15	NextURL       *url.URL `url:"-"`
16	GetAllPages   bool     `url:"-"`
17	NumberOfPages int      `url:"-"`
18	Limit         int      `url:"limit,omitempty"`
19}
20
21// App is the Model for an OKTA Application
22type App struct {
23	ID            string    `json:"id"`
24	Name          string    `json:"name"`
25	Label         string    `json:"label"`
26	Status        string    `json:"status"`
27	LastUpdated   time.Time `json:"lastUpdated"`
28	Created       time.Time `json:"created"`
29	Accessibility struct {
30		SelfService      bool        `json:"selfService"`
31		ErrorRedirectURL interface{} `json:"errorRedirectUrl"`
32		LoginRedirectURL interface{} `json:"loginRedirectUrl"`
33	} `json:"accessibility"`
34	Visibility struct {
35		AutoSubmitToolbar bool `json:"autoSubmitToolbar"`
36		Hide              struct {
37			IOS bool `json:"iOS"`
38			Web bool `json:"web"`
39		} `json:"hide"`
40		AppLinks struct {
41			TestorgoneCustomsaml20App1Link bool `json:"testorgone_customsaml20app_1_link"`
42		} `json:"appLinks"`
43	} `json:"visibility"`
44	Features    []interface{} `json:"features"`
45	SignOnMode  string        `json:"signOnMode"`
46	Credentials struct {
47		UserNameTemplate struct {
48			Template string `json:"template"`
49			Type     string `json:"type"`
50		} `json:"userNameTemplate"`
51		Signing struct {
52		} `json:"signing"`
53	} `json:"credentials"`
54	Settings struct {
55		App struct {
56		} `json:"app"`
57		Notifications struct {
58			Vpn struct {
59				Network struct {
60					Connection string `json:"connection"`
61				} `json:"network"`
62				Message interface{} `json:"message"`
63				HelpURL interface{} `json:"helpUrl"`
64			} `json:"vpn"`
65		} `json:"notifications"`
66		SignOn struct {
67			DefaultRelayState     string        `json:"defaultRelayState"`
68			SsoAcsURL             string        `json:"ssoAcsUrl"`
69			IdpIssuer             string        `json:"idpIssuer"`
70			Audience              string        `json:"audience"`
71			Recipient             string        `json:"recipient"`
72			Destination           string        `json:"destination"`
73			SubjectNameIDTemplate string        `json:"subjectNameIdTemplate"`
74			SubjectNameIDFormat   string        `json:"subjectNameIdFormat"`
75			ResponseSigned        bool          `json:"responseSigned"`
76			AssertionSigned       bool          `json:"assertionSigned"`
77			SignatureAlgorithm    string        `json:"signatureAlgorithm"`
78			DigestAlgorithm       string        `json:"digestAlgorithm"`
79			HonorForceAuthn       bool          `json:"honorForceAuthn"`
80			AuthnContextClassRef  string        `json:"authnContextClassRef"`
81			SpIssuer              interface{}   `json:"spIssuer"`
82			RequestCompressed     bool          `json:"requestCompressed"`
83			AttributeStatements   []interface{} `json:"attributeStatements"`
84		} `json:"signOn"`
85	} `json:"settings"`
86	Links struct {
87		Logo []struct {
88			Name string `json:"name"`
89			Href string `json:"href"`
90			Type string `json:"type"`
91		} `json:"logo"`
92		AppLinks []struct {
93			Name string `json:"name"`
94			Href string `json:"href"`
95			Type string `json:"type"`
96		} `json:"appLinks"`
97		Help struct {
98			Href string `json:"href"`
99			Type string `json:"type"`
100		} `json:"help"`
101		Users struct {
102			Href string `json:"href"`
103		} `json:"users"`
104		Deactivate struct {
105			Href string `json:"href"`
106		} `json:"deactivate"`
107		Groups struct {
108			Href string `json:"href"`
109		} `json:"groups"`
110		Metadata struct {
111			Href string `json:"href"`
112			Type string `json:"type"`
113		} `json:"metadata"`
114	} `json:"_links"`
115}
116
117func (a App) String() string {
118	// return Stringify(g)
119	return fmt.Sprintf("App:(ID: {%v} - Name: {%v})\n", a.ID, a.Name)
120}
121
122// GetByID gets a group from OKTA by the Gropu ID. An error is returned if the group is not found
123func (a *AppsService) GetByID(appID string) (*App, *Response, error) {
124
125	u := fmt.Sprintf("apps/%v", appID)
126	req, err := a.client.NewRequest("GET", u, nil)
127
128	if err != nil {
129		return nil, nil, err
130	}
131
132	app := new(App)
133
134	resp, err := a.client.Do(req, app)
135
136	if err != nil {
137		return nil, resp, err
138	}
139
140	return app, resp, err
141}
142
143// AppUser is the model for a user of an OKTA App
144type AppUser struct {
145	ID              string     `json:"id"`
146	ExternalID      string     `json:"externalId"`
147	Created         time.Time  `json:"created"`
148	LastUpdated     time.Time  `json:"lastUpdated"`
149	Scope           string     `json:"scope"`
150	Status          string     `json:"status"`
151	StatusChanged   *time.Time `json:"statusChanged"`
152	PasswordChanged *time.Time `json:"passwordChanged"`
153	SyncState       string     `json:"syncState"`
154	LastSync        *time.Time `json:"lastSync"`
155	Credentials     struct {
156		UserName string `json:"userName"`
157		Password struct {
158		} `json:"password"`
159	} `json:"credentials"`
160	Profile struct {
161		SecondEmail      interface{} `json:"secondEmail"`
162		LastName         string      `json:"lastName"`
163		MobilePhone      interface{} `json:"mobilePhone"`
164		Email            string      `json:"email"`
165		SalesforceGroups []string    `json:"salesforceGroups"`
166		Role             string      `json:"role"`
167		FirstName        string      `json:"firstName"`
168		Profile          string      `json:"profile"`
169	} `json:"profile"`
170	Links struct {
171		App struct {
172			Href string `json:"href"`
173		} `json:"app"`
174		User struct {
175			Href string `json:"href"`
176		} `json:"user"`
177	} `json:"_links"`
178}
179
180// GetUsers returns the members in an App
181//   Pass in an optional AppFilterOptions struct to filter the results
182//   The Users in the app are returned
183func (a *AppsService) GetUsers(appID string, opt *AppFilterOptions) (appUsers []AppUser, resp *Response, err error) {
184
185	pagesRetreived := 0
186	var u string
187	if opt.NextURL != nil {
188		u = opt.NextURL.String()
189	} else {
190		u = fmt.Sprintf("apps/%v/users", appID)
191
192		if opt.Limit == 0 {
193			opt.Limit = defaultLimit
194		}
195
196		u, _ = addOptions(u, opt)
197	}
198
199	req, err := a.client.NewRequest("GET", u, nil)
200
201	if err != nil {
202		// fmt.Printf("____ERROR HERE\n")
203		return nil, nil, err
204	}
205	resp, err = a.client.Do(req, &appUsers)
206
207	if err != nil {
208		// fmt.Printf("____ERROR HERE 2\n")
209		return nil, resp, err
210	}
211
212	pagesRetreived++
213
214	if (opt.NumberOfPages > 0 && pagesRetreived < opt.NumberOfPages) || opt.GetAllPages {
215
216		for {
217
218			if pagesRetreived == opt.NumberOfPages {
219				break
220			}
221			if resp.NextURL != nil {
222
223				var userPage []AppUser
224				pageOpts := new(AppFilterOptions)
225				pageOpts.NextURL = resp.NextURL
226				pageOpts.Limit = opt.Limit
227				pageOpts.NumberOfPages = 1
228
229				userPage, resp, err = a.GetUsers(appID, pageOpts)
230
231				if err != nil {
232					return appUsers, resp, err
233				}
234				appUsers = append(appUsers, userPage...)
235				pagesRetreived++
236			} else {
237				break
238			}
239
240		}
241	}
242
243	return appUsers, resp, err
244}
245
246// AppGroups - Groups assigned to Application
247type AppGroups struct {
248	ID          string    `json:"id"`
249	LastUpdated time.Time `json:"lastUpdated"`
250	Priority    int       `json:"priority"`
251	Links       struct {
252		User struct {
253			Href string `json:"href"`
254		} `json:"user"`
255	} `json:"_links"`
256}
257
258// GetGroups returns groups assigned to the application - Input appID is the Application GUID
259func (a *AppsService) GetGroups(appID string) (appGroups []AppGroups, resp *Response, err error) {
260
261	var u string
262	u = fmt.Sprintf("apps/%v/groups", appID)
263
264	req, err := a.client.NewRequest("GET", u, nil)
265
266	if err != nil {
267		return nil, nil, err
268	}
269	resp, err = a.client.Do(req, &appGroups)
270
271	if err != nil {
272		return nil, resp, err
273	}
274
275	for {
276
277		if resp.NextURL != nil {
278
279			var appGroupPage []AppGroups
280
281			appGroupPage, resp, err = a.GetGroups(appID)
282
283			if err != nil {
284				return appGroups, resp, err
285			} else {
286				appGroups = append(appGroups, appGroupPage...)
287
288			}
289		} else {
290			break
291		}
292
293	}
294
295	return appGroups, resp, err
296}
297
298// GetUser returns the AppUser model for one app users
299func (a *AppsService) GetUser(appID string, userID string) (appUser AppUser, resp *Response, err error) {
300
301	var u string
302	u = fmt.Sprintf("apps/%v/users/%v", appID, userID)
303
304	req, err := a.client.NewRequest("GET", u, nil)
305
306	if err != nil {
307		return appUser, nil, err
308	}
309	resp, err = a.client.Do(req, &appUser)
310
311	if err != nil {
312		return appUser, resp, err
313	}
314	return appUser, resp, nil
315}
316