From 6488ad4e7672a4d3af1f2426c1a1fb27c9e11093 Mon Sep 17 00:00:00 2001 From: Luan Cazarine <luanhc@gmail.com> Date: Thu, 13 Feb 2025 13:58:30 -0300 Subject: [PATCH 1/3] onelogin init --- .../actions/create-user/create-user.mjs | 232 +++++++++++ .../revoke-user-sessions.mjs | 23 ++ .../actions/update-user/update-user.mjs | 224 +++++++++++ components/onelogin/onelogin.app.mjs | 368 +++++++++++++++++- components/onelogin/package.json | 2 +- .../new-directory-sync-event.mjs | 104 +++++ .../new-login-attempt/new-login-attempt.mjs | 124 ++++++ .../onelogin/sources/new-user/new-user.mjs | 102 +++++ 8 files changed, 1176 insertions(+), 3 deletions(-) create mode 100644 components/onelogin/actions/create-user/create-user.mjs create mode 100644 components/onelogin/actions/revoke-user-sessions/revoke-user-sessions.mjs create mode 100644 components/onelogin/actions/update-user/update-user.mjs create mode 100644 components/onelogin/sources/new-directory-sync-event/new-directory-sync-event.mjs create mode 100644 components/onelogin/sources/new-login-attempt/new-login-attempt.mjs create mode 100644 components/onelogin/sources/new-user/new-user.mjs diff --git a/components/onelogin/actions/create-user/create-user.mjs b/components/onelogin/actions/create-user/create-user.mjs new file mode 100644 index 0000000000000..7f3a52a1d4a8e --- /dev/null +++ b/components/onelogin/actions/create-user/create-user.mjs @@ -0,0 +1,232 @@ +import onelogin from "../../onelogin.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "onelogin-create-user", + name: "Create User", + description: "Creates a new user in OneLogin. [See the documentation](https://developers.onelogin.com/api-docs/1/users/create-user)", + version: "0.0.{{ts}}", + type: "action", + props: { + onelogin, + firstname: { + propDefinition: [ + onelogin, + "firstname", + ], + }, + lastname: { + propDefinition: [ + onelogin, + "lastname", + ], + }, + email: { + propDefinition: [ + onelogin, + "email", + ], + optional: true, + }, + username: { + propDefinition: [ + onelogin, + "username", + ], + optional: true, + }, + company: { + propDefinition: [ + onelogin, + "company", + ], + optional: true, + }, + department: { + propDefinition: [ + onelogin, + "department", + ], + optional: true, + }, + directoryId: { + propDefinition: [ + onelogin, + "directoryId", + ], + optional: true, + }, + distinguishedName: { + propDefinition: [ + onelogin, + "distinguishedName", + ], + optional: true, + }, + externalId: { + propDefinition: [ + onelogin, + "externalId", + ], + optional: true, + }, + groupId: { + propDefinition: [ + onelogin, + "groupId", + ], + optional: true, + }, + invalidLoginAttempts: { + propDefinition: [ + onelogin, + "invalidLoginAttempts", + ], + optional: true, + }, + localeCode: { + propDefinition: [ + onelogin, + "localeCode", + ], + optional: true, + }, + memberOf: { + propDefinition: [ + onelogin, + "memberOf", + ], + optional: true, + }, + openidName: { + propDefinition: [ + onelogin, + "openidName", + ], + optional: true, + }, + phone: { + propDefinition: [ + onelogin, + "phone", + ], + optional: true, + }, + samaccountname: { + propDefinition: [ + onelogin, + "samaccountname", + ], + optional: true, + }, + title: { + propDefinition: [ + onelogin, + "title", + ], + optional: true, + }, + customAttributes: { + propDefinition: [ + onelogin, + "customAttributes", + ], + optional: true, + }, + }, + async run({ $ }) { + if (!this.email && !this.username) { + throw new Error("Either email or username must be provided."); + } + + const userData = { + firstname: this.firstname, + lastname: this.lastname, + ...(this.email + ? { + email: this.email, + } + : {}), + ...(this.username + ? { + username: this.username, + } + : {}), + ...(this.company != null + ? { + company: this.company, + } + : {}), + ...(this.department != null + ? { + department: this.department, + } + : {}), + ...(this.directoryId != null + ? { + directory_id: this.directoryId, + } + : {}), + ...(this.distinguishedName != null + ? { + distinguished_name: this.distinguishedName, + } + : {}), + ...(this.externalId != null + ? { + external_id: this.externalId, + } + : {}), + ...(this.groupId != null + ? { + group_id: parseInt(this.groupId, 10), + } + : {}), + ...(this.invalidLoginAttempts != null + ? { + invalid_login_attempts: this.invalidLoginAttempts, + } + : {}), + ...(this.localeCode != null + ? { + locale_code: this.localeCode, + } + : {}), + ...(this.memberOf != null + ? { + member_of: this.memberOf, + } + : {}), + ...(this.openidName != null + ? { + openid_name: this.openidName, + } + : {}), + ...(this.phone != null + ? { + phone: this.phone, + } + : {}), + ...(this.samaccountname != null + ? { + samaccountname: this.samaccountname, + } + : {}), + ...(this.title != null + ? { + title: this.title, + } + : {}), + ...(this.customAttributes != null + ? { + custom_attributes: this.customAttributes, + } + : {}), + }; + + const response = await this.onelogin.createUser(userData); + + $.export("$summary", `Created user ${response.username} with ID ${response.id}`); + return response; + }, +}; diff --git a/components/onelogin/actions/revoke-user-sessions/revoke-user-sessions.mjs b/components/onelogin/actions/revoke-user-sessions/revoke-user-sessions.mjs new file mode 100644 index 0000000000000..0f71ad5fc9272 --- /dev/null +++ b/components/onelogin/actions/revoke-user-sessions/revoke-user-sessions.mjs @@ -0,0 +1,23 @@ +import onelogin from "../../onelogin.app.mjs"; + +export default { + key: "onelogin-revoke-user-sessions", + name: "Revoke User Sessions", + description: "Revokes all active sessions for a specified user in OneLogin. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + onelogin, + revokeUserId: { + propDefinition: [ + onelogin, + "revokeUserId", + ], + }, + }, + async run({ $ }) { + const response = await this.onelogin.revokeUserSessions(this.revokeUserId); + $.export("$summary", `Successfully revoked sessions for user ID ${this.revokeUserId}`); + return response; + }, +}; diff --git a/components/onelogin/actions/update-user/update-user.mjs b/components/onelogin/actions/update-user/update-user.mjs new file mode 100644 index 0000000000000..bd86bf0a781b1 --- /dev/null +++ b/components/onelogin/actions/update-user/update-user.mjs @@ -0,0 +1,224 @@ +import onelogin from "../../onelogin.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "onelogin-update-user", + name: "Update User", + description: "Updates an existing user's details in OneLogin. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + onelogin, + updateUserId: { + propDefinition: [ + onelogin, + "updateUserId", + ], + }, + firstname: { + propDefinition: [ + onelogin, + "firstname", + ], + optional: true, + }, + lastname: { + propDefinition: [ + onelogin, + "lastname", + ], + optional: true, + }, + email: { + propDefinition: [ + onelogin, + "email", + ], + optional: true, + }, + username: { + propDefinition: [ + onelogin, + "username", + ], + optional: true, + }, + company: { + propDefinition: [ + onelogin, + "company", + ], + optional: true, + }, + department: { + propDefinition: [ + onelogin, + "department", + ], + optional: true, + }, + directoryId: { + propDefinition: [ + onelogin, + "directoryId", + ], + optional: true, + }, + distinguishedName: { + propDefinition: [ + onelogin, + "distinguishedName", + ], + optional: true, + }, + externalId: { + propDefinition: [ + onelogin, + "externalId", + ], + optional: true, + }, + groupId: { + propDefinition: [ + onelogin, + "groupId", + ], + optional: true, + }, + invalidLoginAttempts: { + propDefinition: [ + onelogin, + "invalidLoginAttempts", + ], + optional: true, + }, + localeCode: { + propDefinition: [ + onelogin, + "localeCode", + ], + optional: true, + }, + memberOf: { + propDefinition: [ + onelogin, + "memberOf", + ], + optional: true, + }, + openidName: { + propDefinition: [ + onelogin, + "openidName", + ], + optional: true, + }, + phone: { + propDefinition: [ + onelogin, + "phone", + ], + optional: true, + }, + samaccountname: { + propDefinition: [ + onelogin, + "samaccountname", + ], + optional: true, + }, + title: { + propDefinition: [ + onelogin, + "title", + ], + optional: true, + }, + customAttributes: { + propDefinition: [ + onelogin, + "customAttributes", + ], + optional: true, + }, + state: { + propDefinition: [ + onelogin, + "state", + ], + optional: true, + }, + status: { + propDefinition: [ + onelogin, + "status", + ], + optional: true, + }, + userprincipalname: { + propDefinition: [ + onelogin, + "userprincipalname", + ], + optional: true, + }, + managerAdId: { + propDefinition: [ + onelogin, + "manager_ad_id", + ], + optional: true, + }, + managerUserId: { + propDefinition: [ + onelogin, + "manager_user_id", + ], + optional: true, + }, + roleId: { + propDefinition: [ + onelogin, + "role_id", + ], + optional: true, + }, + }, + async run({ $ }) { + const userId = this.updateUserId; + const data = {}; + + if (this.firstname) data.firstname = this.firstname; + if (this.lastname) data.lastname = this.lastname; + if (this.email) data.email = this.email; + if (this.username) data.username = this.username; + if (this.company) data.company = this.company; + if (this.department) data.department = this.department; + if (this.directoryId !== undefined) data.directory_id = this.directoryId; + if (this.distinguishedName) data.distinguished_name = this.distinguishedName; + if (this.externalId) data.external_id = this.externalId; + if (this.groupId) data.group_id = parseInt(this.groupId, 10); + if (this.invalidLoginAttempts !== undefined) data.invalid_login_attempts = this.invalidLoginAttempts; + if (this.localeCode) data.locale_code = this.localeCode; + if (this.memberOf) data.member_of = this.memberOf; + if (this.openidName) data.openid_name = this.openidName; + if (this.phone) data.phone = this.phone; + if (this.samaccountname) data.samaccountname = this.samaccountname; + if (this.title) data.title = this.title; + if (this.customAttributes) data.custom_attributes = this.customAttributes; + if (this.state !== undefined) data.state = this.state; + if (this.status !== undefined) data.status = this.status; + if (this.userprincipalname) data.userprincipalname = this.userprincipalname; + if (this.managerAdId) data.manager_ad_id = this.managerAdId; + if (this.managerUserId !== undefined) data.manager_user_id = this.managerUserId; + if (this.roleId) { + data.role_id = Array.isArray(this.roleId) + ? this.roleId.map((id) => parseInt(id, 10)) + : parseInt(this.roleId, 10); + } + + const response = await this.onelogin.updateUser(userId, data); + $.export("$summary", `Successfully updated user with ID ${userId}`); + return response; + }, +}; diff --git a/components/onelogin/onelogin.app.mjs b/components/onelogin/onelogin.app.mjs index bc625b9d09792..c88ee2c3f3732 100644 --- a/components/onelogin/onelogin.app.mjs +++ b/components/onelogin/onelogin.app.mjs @@ -1,11 +1,375 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "onelogin", - propDefinitions: {}, + propDefinitions: { + // Required props for creating a new user + firstname: { + type: "string", + label: "First Name", + description: "User's first name.", + }, + lastname: { + type: "string", + label: "Last Name", + description: "User's last name.", + }, + email: { + type: "string", + label: "Email", + description: "User's email address.", + optional: true, + }, + username: { + type: "string", + label: "Username", + description: "User's username.", + optional: true, + }, + // Optional props for creating/updating a user + company: { + type: "string", + label: "Company", + description: "Company the user is associated with.", + optional: true, + }, + department: { + type: "string", + label: "Department", + description: "Department the user works in.", + optional: true, + }, + directoryId: { + type: "integer", + label: "Directory ID", + description: "ID of the directory (e.g., Active Directory, LDAP) from which the user was created.", + optional: true, + }, + distinguishedName: { + type: "string", + label: "Distinguished Name", + description: "Synchronized from Active Directory.", + optional: true, + }, + externalId: { + type: "string", + label: "External ID", + description: "External ID that can be used to uniquely identify the user in another system.", + optional: true, + }, + groupId: { + type: "string", + label: "Group ID", + description: "Group to which the user belongs.", + async options() { + const groups = await this.listGroups(); + return groups.map((group) => ({ + label: group.name, + value: group.id.toString(), + })); + }, + optional: true, + }, + invalidLoginAttempts: { + type: "integer", + label: "Invalid Login Attempts", + description: "Number of sequential invalid login attempts the user has made.", + optional: true, + }, + localeCode: { + type: "string", + label: "Locale Code", + description: "Locale code representing a geographical, political, or cultural region.", + optional: true, + }, + memberOf: { + type: "string", + label: "Member Of", + description: "Groups the user is a member of.", + optional: true, + }, + openidName: { + type: "string", + label: "OpenID Name", + description: "OpenID URL that can be configured in other applications that accept OpenID for sign-in.", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "User's phone number.", + optional: true, + }, + samaccountname: { + type: "string", + label: "SAMAccountName", + description: "Synchronized from Active Directory.", + optional: true, + }, + title: { + type: "string", + label: "Title", + description: "User's title.", + optional: true, + }, + customAttributes: { + type: "object", + label: "Custom Attributes", + description: "Custom attributes for the user.", + optional: true, + }, + // Props for updating an existing user + updateUserId: { + type: "string", + label: "User ID", + description: "Unique ID of the user to update.", + async options() { + const users = await this.listUsers(); + return users.map((user) => ({ + label: `${user.firstname} ${user.lastname} (${user.email})`, + value: user.id.toString(), + })); + }, + }, + // Props for revoking user sessions + revokeUserId: { + type: "string", + label: "User ID", + description: "Unique ID of the user to revoke sessions for.", + async options() { + const users = await this.listUsers(); + return users.map((user) => ({ + label: `${user.firstname} ${user.lastname} (${user.email})`, + value: user.id.toString(), + })); + }, + }, + // Props for User Created Event Trigger filters + filterUserRole: { + type: "string", + label: "User Role", + description: "Filter by user role.", + async options() { + const roles = await this.listRoles(); + return roles.map((role) => ({ + label: role.name, + value: role.id.toString(), + })); + }, + optional: true, + }, + filterGroup: { + type: "string", + label: "Group", + description: "Filter by user group.", + async options() { + const groups = await this.listGroups(); + return groups.map((group) => ({ + label: group.name, + value: group.id.toString(), + })); + }, + optional: true, + }, + // Props for Login Attempt Event Trigger filters + filterLoginSuccess: { + type: "boolean", + label: "Successful Attempts", + description: "Filter to only include successful login attempts.", + optional: true, + }, + filterLoginFailure: { + type: "boolean", + label: "Failed Attempts", + description: "Filter to only include failed login attempts.", + optional: true, + }, + // Props for Directory Sync Event Trigger filters + directoryName: { + type: "string", + label: "Directory Name", + description: "Filter by specific directory name.", + async options() { + const directories = await this.listDirectories(); + return directories.map((dir) => ({ + label: dir.name, + value: dir.id.toString(), + })); + }, + optional: true, + }, + syncStatus: { + type: "string", + label: "Sync Status", + description: "Filter by sync status.", + options: [ + { + label: "Success", + value: "success", + }, + { + label: "Failure", + value: "failure", + }, + { + label: "In Progress", + value: "in_progress", + }, + ], + optional: true, + }, + }, methods: { - // this.$auth contains connected account data + // Existing method authKeys() { console.log(Object.keys(this.$auth)); }, + // Base URL + _baseUrl() { + return "https://api.onelogin.com/api/1"; + }, + // Make Request + async _makeRequest(opts = {}) { + const { + $ = this, + method = "GET", + path = "/", + data, + params, + headers = {}, + ...otherOpts + } = opts; + return axios($, { + method, + url: this._baseUrl() + path, + data: data, + params: params, + headers: { + ...headers, + "Authorization": `bearer:${this.$auth.access_token}`, + "Content-Type": "application/json", + }, + ...otherOpts, + }); + }, + // Pagination Logic + async paginate(fn, ...args) { + const results = []; + let page = 1; + let hasNext = true; + while (hasNext) { + const response = await fn({ + page, + ...args, + }); + if (Array.isArray(response)) { + results.push(...response); + hasNext = false; + } else { + results.push(...response); + hasNext = false; + } + } + return results; + }, + // List Groups + async listGroups(opts = {}) { + return this.paginate(async ({ page }) => { + const response = await this._makeRequest({ + path: "/groups", + params: { + limit: 100, + page, + ...opts.params, + }, + }); + return response; + }, opts); + }, + // List Roles + async listRoles(opts = {}) { + return this.paginate(async ({ page }) => { + const response = await this._makeRequest({ + path: "/roles", + params: { + limit: 100, + page, + ...opts.params, + }, + }); + return response; + }, opts); + }, + // List Users + async listUsers(opts = {}) { + return this.paginate(async ({ page }) => { + const response = await this._makeRequest({ + path: "/users", + params: { + limit: 100, + page, + ...opts.params, + }, + }); + return response; + }, opts); + }, + // List Directories + async listDirectories(opts = {}) { + return this.paginate(async ({ page }) => { + const response = await this._makeRequest({ + path: "/directories", + params: { + limit: 100, + page, + ...opts.params, + }, + }); + return response; + }, opts); + }, + // Create User + async createUser(data, opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/users", + data, + ...opts, + }); + }, + // Update User + async updateUser(userId, data, opts = {}) { + return this._makeRequest({ + method: "PUT", + path: `/users/${userId}`, + data, + ...opts, + }); + }, + // Revoke User Sessions + async revokeUserSessions(userId, opts = {}) { + return this._makeRequest({ + method: "POST", + path: `/users/${userId}/logout`, + ...opts, + }); + }, + // Emit User Created Event + async emitUserCreatedEvent(filters) { + // Implementation to emit event based on filters + // This would typically involve setting up a webhook or listening to API events + }, + // Emit Login Attempt Event + async emitLoginAttemptEvent(filters) { + // Implementation to emit event based on filters + // This would typically involve setting up a webhook or listening to API events + }, + // Emit Directory Sync Event + async emitDirectorySyncEvent(filters) { + // Implementation to emit event based on filters + // This would typically involve setting up a webhook or listening to API events + }, }, + version: "0.0.ts", }; diff --git a/components/onelogin/package.json b/components/onelogin/package.json index 687cdf4ac603b..0945197d47fa2 100644 --- a/components/onelogin/package.json +++ b/components/onelogin/package.json @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/onelogin/sources/new-directory-sync-event/new-directory-sync-event.mjs b/components/onelogin/sources/new-directory-sync-event/new-directory-sync-event.mjs new file mode 100644 index 0000000000000..762de5d847b10 --- /dev/null +++ b/components/onelogin/sources/new-directory-sync-event/new-directory-sync-event.mjs @@ -0,0 +1,104 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import onelogin from "../../onelogin.app.mjs"; + +export default { + key: "onelogin-new-directory-sync-event", + name: "New Directory Sync Event", + description: "Emit new event when a directory sync event is triggered in OneLogin. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + onelogin, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + directoryName: { + propDefinition: [ + onelogin, + "directoryName", + ], + optional: true, + }, + syncStatus: { + propDefinition: [ + onelogin, + "syncStatus", + ], + optional: true, + }, + }, + hooks: { + async deploy() { + const events = await this.runFetchEvents(); + const recentEvents = events.slice(-50).reverse(); + for (const event of recentEvents) { + this.$emit( + event, + { + id: event.id.toString(), + summary: `Directory Sync Event #${event.id}`, + ts: Date.parse(event.created_at), + }, + ); + } + const lastEvent = recentEvents[recentEvents.length - 1]; + if (lastEvent) { + await this.db.set("last_cursor", lastEvent.id); + } + }, + async activate() { + // No webhook setup required for polling source + }, + async deactivate() { + // No webhook teardown required for polling source + }, + }, + methods: { + async runFetchEvents() { + const lastCursor = await this.db.get("last_cursor"); + const params = { + event_type_id: 13, + limit: 50, + sort: "-id", + }; + if (lastCursor) { + params.after_cursor = lastCursor; + } + if (this.directoryName) { + params.directory_id = this.directoryName; + } + if (this.syncStatus) { + params.resolution = this.syncStatus; + } + const response = await this.onelogin._makeRequest({ + path: "/events", + params, + }); + return response; + }, + }, + async run() { + const events = await this.runFetchEvents(); + for (const event of events) { + this.$emit( + event, + { + id: event.id.toString(), + summary: `Directory Sync Event #${event.id}`, + ts: Date.parse(event.created_at), + }, + ); + } + if (events.length > 0) { + const lastEvent = events[events.length - 1]; + await this.db.set("last_cursor", lastEvent.id); + } + }, +}; diff --git a/components/onelogin/sources/new-login-attempt/new-login-attempt.mjs b/components/onelogin/sources/new-login-attempt/new-login-attempt.mjs new file mode 100644 index 0000000000000..f6e5f887e46e0 --- /dev/null +++ b/components/onelogin/sources/new-login-attempt/new-login-attempt.mjs @@ -0,0 +1,124 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import onelogin from "../../onelogin.app.mjs"; + +export default { + key: "onelogin-new-login-attempt", + name: "New Login Attempt", + description: "Emit new event when a login attempt occurs in OneLogin. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + onelogin: { + type: "app", + app: "onelogin", + }, + db: { + type: "$.service.db", + }, + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + filterLoginSuccess: { + propDefinition: [ + "onelogin", + "filterLoginSuccess", + ], + }, + filterLoginFailure: { + propDefinition: [ + "onelogin", + "filterLoginFailure", + ], + }, + }, + hooks: { + async deploy() { + await this.fetchAndEmitEvents(true); + }, + async activate() { + // No webhook setup required for polling source + }, + async deactivate() { + // No webhook cleanup required for polling source + }, + }, + methods: { + async fetchAndEmitEvents(isDeploy = false) { + const lastCursor = await this.db.get("after_cursor"); + const params = { + limit: 50, + sort: "-created_at", + }; + + if (lastCursor) { + params.after_cursor = lastCursor; + } + + // Define event_type_ids that correspond to login attempts + // These IDs should be replaced with actual ones from OneLogin + const loginAttemptEventTypeIds = [ + 5, + 13, + ]; + params.event_type_id = loginAttemptEventTypeIds.join(","); + + const response = await this.onelogin._makeRequest({ + path: "/events", + params, + }); + + const events = response; + + // Emit events from oldest to newest + for (const event of events.reverse()) { + if (this._isLoginAttempt(event) && this._matchesFilters(event)) { + const eventId = event.id + ? event.id.toString() + : undefined; + const eventTimestamp = event.created_at + ? Date.parse(event.created_at) + : Date.now(); + const emitMeta = { + id: eventId || undefined, + summary: `Login attempt by ${event.user_name}`, + ts: eventTimestamp, + }; + this.$emit(event, emitMeta); + } + } + + // Update the cursor + if (events.length > 0 && response.pagination && response.pagination.after_cursor) { + await this.db.set("after_cursor", response.pagination.after_cursor); + } + }, + _isLoginAttempt(event) { + return [ + 5, + 13, + ].includes(event.event_type_id); + }, + _matchesFilters(event) { + let isSuccess = !event.error_description; + if (this.filterLoginSuccess && isSuccess) { + return true; + } + if (this.filterLoginFailure && !isSuccess) { + return true; + } + if (!this.filterLoginSuccess && !this.filterLoginFailure) { + return true; + } + return false; + }, + }, + async run() { + await this.fetchAndEmitEvents(); + }, +}; diff --git a/components/onelogin/sources/new-user/new-user.mjs b/components/onelogin/sources/new-user/new-user.mjs new file mode 100644 index 0000000000000..6b6a60967cdda --- /dev/null +++ b/components/onelogin/sources/new-user/new-user.mjs @@ -0,0 +1,102 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import onelogin from "../../onelogin.app.mjs"; + +export default { + key: "onelogin-new-user", + name: "New User Created", + description: "Emit a new event when a user is created in OneLogin. [See the documentation]().", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + onelogin, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + filterUserRole: { + propDefinition: [ + onelogin, + "filterUserRole", + ], + optional: true, + }, + filterGroup: { + propDefinition: [ + onelogin, + "filterGroup", + ], + optional: true, + }, + }, + hooks: { + async deploy() { + const lastRun = this.db.get("lastRun"); + const now = new Date().toISOString(); + const since = lastRun || new Date(Date.now() - this.timer.intervalSeconds * 1000).toISOString(); + const params = { + event_type_id: "13", + since, + limit: 50, + sort: "-created_at", + }; + const events = await this.onelogin._makeRequest({ + path: "/events", + params, + }); + for (const event of events) { + if (this.filterUserRole && event.role_id !== parseInt(this.filterUserRole, 10)) { + continue; + } + if (this.filterGroup && event.group_id !== parseInt(this.filterGroup, 10)) { + continue; + } + this.$emit(event, { + id: event.id.toString(), + summary: `New user created: ${event.user_name}`, + ts: Date.parse(event.created_at) || Date.now(), + }); + } + this.db.set("lastRun", now); + }, + async activate() { + // No webhook setup required for polling source + }, + async deactivate() { + // No webhook cleanup required for polling source + }, + }, + async run() { + const lastRun = this.db.get("lastRun"); + const now = new Date().toISOString(); + const params = { + event_type_id: "13", + since: lastRun || new Date(Date.now() - this.timer.intervalSeconds * 1000).toISOString(), + limit: 100, + sort: "-created_at", + }; + const events = await this.onelogin._makeRequest({ + path: "/events", + params, + }); + for (const event of events) { + if (this.filterUserRole && event.role_id !== parseInt(this.filterUserRole, 10)) { + continue; + } + if (this.filterGroup && event.group_id !== parseInt(this.filterGroup, 10)) { + continue; + } + this.$emit(event, { + id: event.id.toString(), + summary: `New user created: ${event.user_name}`, + ts: Date.parse(event.created_at) || Date.now(), + }); + } + this.db.set("lastRun", now); + }, +}; From 077473881d556d7f41af1f3df8525834eaedbe02 Mon Sep 17 00:00:00 2001 From: Luan Cazarine <luanhc@gmail.com> Date: Fri, 14 Feb 2025 11:42:48 -0300 Subject: [PATCH 2/3] some adjusts --- .../actions/create-user/create-user.mjs | 114 ++++------------ components/onelogin/common/utils.mjs | 24 ++++ components/onelogin/onelogin.app.mjs | 124 ++++++------------ 3 files changed, 87 insertions(+), 175 deletions(-) create mode 100644 components/onelogin/common/utils.mjs diff --git a/components/onelogin/actions/create-user/create-user.mjs b/components/onelogin/actions/create-user/create-user.mjs index 7f3a52a1d4a8e..5847e19354679 100644 --- a/components/onelogin/actions/create-user/create-user.mjs +++ b/components/onelogin/actions/create-user/create-user.mjs @@ -1,5 +1,6 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { parseObject } from "../../common/utils.mjs"; import onelogin from "../../onelogin.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "onelogin-create-user", @@ -136,95 +137,32 @@ export default { }, async run({ $ }) { if (!this.email && !this.username) { - throw new Error("Either email or username must be provided."); + throw new ConfigurationError("Either email or username must be provided."); } - const userData = { - firstname: this.firstname, - lastname: this.lastname, - ...(this.email - ? { - email: this.email, - } - : {}), - ...(this.username - ? { - username: this.username, - } - : {}), - ...(this.company != null - ? { - company: this.company, - } - : {}), - ...(this.department != null - ? { - department: this.department, - } - : {}), - ...(this.directoryId != null - ? { - directory_id: this.directoryId, - } - : {}), - ...(this.distinguishedName != null - ? { - distinguished_name: this.distinguishedName, - } - : {}), - ...(this.externalId != null - ? { - external_id: this.externalId, - } - : {}), - ...(this.groupId != null - ? { - group_id: parseInt(this.groupId, 10), - } - : {}), - ...(this.invalidLoginAttempts != null - ? { - invalid_login_attempts: this.invalidLoginAttempts, - } - : {}), - ...(this.localeCode != null - ? { - locale_code: this.localeCode, - } - : {}), - ...(this.memberOf != null - ? { - member_of: this.memberOf, - } - : {}), - ...(this.openidName != null - ? { - openid_name: this.openidName, - } - : {}), - ...(this.phone != null - ? { - phone: this.phone, - } - : {}), - ...(this.samaccountname != null - ? { - samaccountname: this.samaccountname, - } - : {}), - ...(this.title != null - ? { - title: this.title, - } - : {}), - ...(this.customAttributes != null - ? { - custom_attributes: this.customAttributes, - } - : {}), - }; - - const response = await this.onelogin.createUser(userData); + const response = await this.onelogin.createUser({ + $, + data: { + firstname: this.firstname, + lastname: this.lastname, + email: this.email, + username: this.username, + company: this.company, + department: this.department, + directory_id: this.directoryId, + distinguished_name: this.distinguishedName, + external_id: this.externalId, + group_id: this.groupId, + invalid_login_attempts: this.invalidLoginAttempts, + locale_code: this.localeCode, + member_of: this.memberOf, + openid_name: this.openidName, + phone: this.phone, + samaccountname: this.samaccountname, + title: this.title, + custom_attributes: parseObject(this.customAttributes), + }, + }); $.export("$summary", `Created user ${response.username} with ID ${response.id}`); return response; diff --git a/components/onelogin/common/utils.mjs b/components/onelogin/common/utils.mjs new file mode 100644 index 0000000000000..dcc9cc61f6f41 --- /dev/null +++ b/components/onelogin/common/utils.mjs @@ -0,0 +1,24 @@ +export const parseObject = (obj) => { + if (!obj) return undefined; + + if (Array.isArray(obj)) { + return obj.map((item) => { + if (typeof item === "string") { + try { + return JSON.parse(item); + } catch (e) { + return item; + } + } + return item; + }); + } + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + return obj; +}; diff --git a/components/onelogin/onelogin.app.mjs b/components/onelogin/onelogin.app.mjs index c88ee2c3f3732..b17e8b512a708 100644 --- a/components/onelogin/onelogin.app.mjs +++ b/components/onelogin/onelogin.app.mjs @@ -59,7 +59,7 @@ export default { optional: true, }, groupId: { - type: "string", + type: "integer", label: "Group ID", description: "Group to which the user belongs.", async options() { @@ -221,38 +221,34 @@ export default { }, }, methods: { - // Existing method - authKeys() { - console.log(Object.keys(this.$auth)); - }, - // Base URL _baseUrl() { - return "https://api.onelogin.com/api/1"; - }, - // Make Request - async _makeRequest(opts = {}) { - const { - $ = this, - method = "GET", - path = "/", - data, - params, - headers = {}, - ...otherOpts - } = opts; - return axios($, { - method, + return `https://${this.$auth.subdomain}.onelogin.com/api/1`; + }, + _headers() { + return { + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + const config = { url: this._baseUrl() + path, - data: data, - params: params, - headers: { - ...headers, - "Authorization": `bearer:${this.$auth.access_token}`, - "Content-Type": "application/json", - }, - ...otherOpts, + headers: this._headers(), + ...opts, + }; + console.log("config: ", config); + return axios($, config); + }, + createUser(data, opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/users", + data, + ...opts, }); }, + // Pagination Logic async paginate(fn, ...args) { const results = []; @@ -275,45 +271,24 @@ export default { }, // List Groups async listGroups(opts = {}) { - return this.paginate(async ({ page }) => { - const response = await this._makeRequest({ - path: "/groups", - params: { - limit: 100, - page, - ...opts.params, - }, - }); - return response; - }, opts); + return this._makeRequest({ + path: "/groups", + ...opts, + }); }, // List Roles async listRoles(opts = {}) { - return this.paginate(async ({ page }) => { - const response = await this._makeRequest({ - path: "/roles", - params: { - limit: 100, - page, - ...opts.params, - }, - }); - return response; - }, opts); + return this._makeRequest({ + path: "/roles", + ...opts, + }); }, // List Users async listUsers(opts = {}) { - return this.paginate(async ({ page }) => { - const response = await this._makeRequest({ - path: "/users", - params: { - limit: 100, - page, - ...opts.params, - }, - }); - return response; - }, opts); + return this._makeRequest({ + path: "/users", + ...opts, + }); }, // List Directories async listDirectories(opts = {}) { @@ -329,15 +304,6 @@ export default { return response; }, opts); }, - // Create User - async createUser(data, opts = {}) { - return this._makeRequest({ - method: "POST", - path: "/users", - data, - ...opts, - }); - }, // Update User async updateUser(userId, data, opts = {}) { return this._makeRequest({ @@ -355,21 +321,5 @@ export default { ...opts, }); }, - // Emit User Created Event - async emitUserCreatedEvent(filters) { - // Implementation to emit event based on filters - // This would typically involve setting up a webhook or listening to API events - }, - // Emit Login Attempt Event - async emitLoginAttemptEvent(filters) { - // Implementation to emit event based on filters - // This would typically involve setting up a webhook or listening to API events - }, - // Emit Directory Sync Event - async emitDirectorySyncEvent(filters) { - // Implementation to emit event based on filters - // This would typically involve setting up a webhook or listening to API events - }, }, - version: "0.0.ts", }; From 4d54b6e2b016ec28872db9ea9fb97e249698c259 Mon Sep 17 00:00:00 2001 From: Luan Cazarine <luanhc@gmail.com> Date: Fri, 14 Feb 2025 11:43:57 -0300 Subject: [PATCH 3/3] pnpm update --- pnpm-lock.yaml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83d4926f198d0..98ff93acf9b7b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -774,8 +774,7 @@ importers: specifier: ^2.3.3 version: 2.3.3 - components/applicantstack: - specifiers: {} + components/applicantstack: {} components/appointedd: dependencies: @@ -3613,8 +3612,7 @@ importers: specifier: ^1.2.0 version: 1.6.6 - components/felt: - specifiers: {} + components/felt: {} components/fibery: dependencies: @@ -7585,8 +7583,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 - components/ory: - specifiers: {} + components/ory: {} components/osu: {} @@ -12419,8 +12416,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 - components/zep: - specifiers: {} + components/zep: {} components/zerobounce: dependencies: