O que Processo de Governança do Ciclo de Vida da Identidade faz?
Quando Usar
- A organização não possui processos automatizados de JML (joiner-mover-leaver) para gerenciamento de identidade
- O provisionamento de acesso é manual e leva dias, causando perda de produtividade e falhas de segurança
- Ex-funcionários mantêm acesso a sistemas após o desligamento (contas órfãs)
- A explosão de papéis criou milhares de papéis com propriedades indefinidas e permissões sobrepostas
- Requisitos de conformidade exigem processos documentados de ciclo de vida da identidade (SOX, HIPAA, GDPR)
- Não há visibilidade centralizada de quem tem acesso a o quê em toda a empresa
Não use para gerenciamento de usuários em um único aplicativo; a governança de identidade lida com o gerenciamento do ciclo de vida entre sistemas, exigindo a correlação de fontes autoritativas de RH com o provisionamento em sistemas downstream.
Pré-requisitos
- Sistema de RH autoritativo (Workday, SAP SuccessFactors, BambooHR) como fonte única da verdade para identidade
- Plataforma IGA (SailPoint, Saviynt, One Identity) ou Microsoft Entra ID Governance
- Active Directory e/ou Azure AD como serviços de diretório primários
- Conectores de aplicativos para sistemas alvo que exigem provisionamento automatizado
- Estrutura de papéis organizacionais definida e hierarquia de reporte
- Aprovação das partes interessadas de RH, TI, segurança e gerentes de unidade de negócios
Fluxo de Trabalho
Etapa 1: Definir Estados e Transições do Ciclo de Vida da Identidade
Mapeie o ciclo de vida da identidade, da contratação ao desligamento:
"""
Máquina de Estados do Ciclo de Vida da Identidade
Define todos os estados de identidade e transições válidas com ações automatizadas.
"""
IDENTITY_LIFECYCLE = {
"states": {
"PRE_HIRE": {
"description": "Identidade criada a partir do feed de RH antes da data de início",
"automated_actions": [
"Criar registro de identidade na plataforma IGA",
"Gerar ID de funcionário único",
"Reservar caixa de correio",
"Atribuir papéis iniciais com base no código do cargo",
"Iniciar fluxo de verificação de antecedentes"
],
"valid_transitions": ["ACTIVE", "CANCELLED"]
},
"ACTIVE": {
"description": "Funcionário iniciou, acesso total provisionado",
"automated_actions": [
"Criar conta no Active Directory",
"Criar caixa de correio de e-mail",
"Provisionar acesso inicial a aplicativos",
"Atribuir papéis específicos do departamento",
"Adicionar a grupos de distribuição",
"Emitir token MFA/chave de segurança",
"Criar conta VPN se for trabalhador remoto"
],
"valid_transitions": ["ROLE_CHANGE", "LEAVE_OF_ABSENCE", "TERMINATED"]
},
"ROLE_CHANGE": {
"description": "Funcionário transferido, promovido ou mudou de departamento",
"automated_actions": [
"Recalcular atribuições de papéis com base no novo código do cargo",
"Remover acesso de aplicativos do departamento anterior",
"Provisionar acesso para aplicativos do novo departamento",
"Atualizar associações de grupo",
"Transferir gerente no diretório",
"Acionar revisão de acesso para permissões retidas",
"Notificar novo gerente sobre acessos herdados"
],
"valid_transitions": ["ACTIVE", "LEAVE_OF_ABSENCE", "TERMINATED"]
},
"LEAVE_OF_ABSENCE": {
"description": "Funcionário em licença prolongada (médica, parental, sabática)",
"automated_actions": [
"Desabilitar login interativo (preservar conta)",
"Suspender acesso VPN",
"Configurar resposta automática de ausência",
"Delegar caixa de correio ao gerente",
"Preservar todas as atribuições de papéis para o retorno",
"Definir data de reativação a partir do feed de RH"
],
"valid_transitions": ["ACTIVE", "TERMINATED"]
},
"TERMINATED": {
"description": "Funcionário deixou a organização",
"automated_actions": [
"Desabilitar conta AD imediatamente",
"Revogar todo o acesso a aplicativos",
"Revogar VPN e acesso remoto",
"Converter caixa de correio para compartilhada (acesso do gerente por 90 dias)",
"Transferir arquivos do OneDrive para o gerente",
"Remover de todos os grupos de segurança e distribuição",
"Revogar tokens OAuth e chaves de API",
"Limpar dados corporativos de dispositivos móveis",
"Arquivar registro de identidade",
"Agendar exclusão da conta após período de retenção"
],
"valid_transitions": ["REHIRE", "DELETED"]
},
"REHIRE": {
"description": "Funcionário anteriormente desligado retornando",
"automated_actions": [
"Reativar registro de identidade existente",
"Redefinir credenciais e exigir novo registro MFA",
"Provisionar com base no novo código do cargo (não no acesso anterior)",
"Sinalizar para revisão de acesso reforçada nos primeiros 30 dias"
],
"valid_transitions": ["ACTIVE"]
},
"DELETED": {
"description": "Conta removida permanentemente após período de retenção",
"automated_actions": [
"Excluir conta AD",
"Excluir arquivo de caixa de correio de e-mail",
"Remover registro de identidade do IGA",
"Gerar log de auditoria de exclusão"
],
"valid_transitions": []
}
},
"retention_periods": {
"terminated_to_deleted": "90 dias (padrão)",
"mailbox_retention": "90 dias como caixa de correio compartilhada",
"onedrive_retention": "30 dias de acesso do gerente, depois arquivado",
"audit_log_retention": "7 anos para conformidade"
}
}
Etapa 2: Implementar Integração com Fonte Autoritativa
Conecte o sistema de RH como a fonte única da verdade para dados de identidade:
"""
Integração com Fonte de RH - Conector Workday para Plataforma IGA
Consulta o Workday em busca de eventos de ciclo de vida do funcionário e aciona o provisionamento.
"""
import requests
from datetime import datetime, timedelta
import logging
class WorkdayIdentityConnector:
def __init__(self, config):
self.base_url = config["workday_api_url"]
self.tenant = config["tenant"]
self.client_id = config["client_id"]
self.client_secret = config["client_secret"]
self.session = requests.Session()
self.logger = logging.getLogger("workday_connector")
def get_access_token(self):
"""Autenticar na API REST do Workday."""
token_url = f"{self.base_url}/ccx/oauth2/{self.tenant}/token"
response = self.session.post(token_url, data={
"grant_type": "client_credentials",
"client_id": self.client_id,
"client_secret": self.client_secret
})
response.raise_for_status()
return response.json()["access_token"]
def fetch_worker_changes(self, since_datetime):
"""Buscar todos os eventos de ciclo de vida do trabalhador desde a última sincronização."""
headers = {"Authorization": f"Bearer {self.get_access_token()}"}
params = {
"Updated_From": since_datetime.isoformat(),
"Updated_Through": datetime.utcnow().isoformat(),
"Count": 100
}
workers = []
url = f"{self.base_url}/ccx/api/v1/{self.tenant}/workers"
while url:
response = self.session.get(url, headers=headers, params=params)
response.raise_for_status()
data = response.json()
workers.extend(data.get("data", []))
url = data.get("next", None)
params = {}
return workers
def map_lifecycle_event(self, worker):
"""Mapear dados do trabalhador do Workday para evento de ciclo de vida da identidade."""
worker_data = worker.get("workerData", {})
employment = worker_data.get("employmentData", {})
personal = worker_data.get("personalData", {})
event = {
"employee_id": worker.get("id"),
"first_name": personal.get("legalName", {}).get("firstName"),
"last_name": personal.get("legalName", {}).get("lastName"),
"email": worker_data.get("emailAddress"),
"job_code": employment.get("jobProfile", {}).get("id"),
"job_title": employment.get("jobProfile", {}).get("name"),
"department": employment.get("organization", {}).get("name"),
"department_code": employment.get("organization", {}).get("id"),
"manager_id": employment.get("managerId"),
"location": employment.get("location", {}).get("name"),
"cost_center": employment.get("costCenter", {}).get("id"),
"hire_date": employment.get("hireDate"),
"termination_date": employment.get("terminationDate"),
"status": employment.get("status"),
"worker_type": employment.get("workerType"),
}
# Determinar transição do ciclo de vida
if event["status"] == "Active" and event["hire_date"]:
hire_date = datetime.fromisoformat(event["hire_date"])
if hire_date > datetime.utcnow():
event["lifecycle_event"] = "PRE_HIRE"
else:
event["lifecycle_event"] = "JOINER"
elif event["status"] == "Active":
event["lifecycle_event"] = "MOVER" # Mudança de departamento ou papel
elif event["status"] == "Terminated":
event["lifecycle_event"] = "LEAVER"
elif event["status"] == "On Leave":
event["lifecycle_event"] = "LEAVE_OF_ABSENCE"
return event
def process_lifecycle_events(self, since_datetime):
"""Loop principal de processamento para eventos de ciclo de vida da identidade."""
workers = self.fetch_worker_changes(since_datetime)
events = []
for worker in workers:
event = self.map_lifecycle_event(worker)
events.append(event)
self.logger.info(
f"Evento de ciclo de vida: {event['lifecycle_event']} para "
f"{event['first_name']} {event['last_name']} "
f"(ID Func.: {event['employee_id']})"
)
return events
Etapa 3: Implementar Mineração de Papéis e Acesso Inicial
Defina papéis com base nas funções do cargo para provisionamento automatizado:
"""
Motor de Mineração de Papéis
Analisa padrões de acesso existentes para derivar definições de papéis
para provisionamento automático (birthright).
"""
import pandas as pd
from collections import Counter
from itertools import combinations
class RoleMiningEngine:
def __init__(self, access_data):
"""
access_data: DataFrame com colunas
[employee_id, job_code, department, application, entitlement]
"""
self.access_data = access_data
def mine_birthright_roles(self, min_assignment_pct=0.8):
"""
Identificar permissões que devem ser atribuídas automaticamente
com base no código do cargo. Se 80%+ dos usuários com o mesmo
código de cargo tiverem uma permissão, ela se torna acesso inicial.
"""
birthright_roles = {}
for job_code, group in self.access_data.groupby("job_code"):
total_users = group["employee_id"].nunique()
entitlement_counts = group.groupby(
["application", "entitlement"]
)["employee_id"].nunique()
birthright_entitlements = []
for (app, ent), count in entitlement_counts.items():
pct = count / total_users
if pct >= min_assignment_pct:
birthright_entitlements.append({
"application": app,
"entitlement": ent,
"assignment_percentage": round(pct * 100, 1),
"user_count": count
})
if birthright_entitlements:
birthright_roles[job_code] = {
"job_code": job_code,
"total_users": total_users,
"birthright_entitlements": birthright_entitlements
}
return birthright_roles
def detect_role_explosion(self):
"""Identificar papéis com sobreposição excessiva indicando necessidade de consolidação."""
roles = self.access_data.groupby("job_code").apply(
lambda x: set(zip(x["application"], x["entitlement"]))
)
overlap_report = []
for (role1, ents1), (role2, ents2) in combinations(roles.items(), 2):
if len(ents1) == 0 or len(ents2) == 0:
continue
overlap = len(ents1 & ents2)
max_size = max(len(ents1), len(ents2))
overlap_pct = overlap / max_size * 100
if overlap_pct > 70:
overlap_report.append({
"role_1": role1,
"role_2": role2,
"role_1_entitlements": len(ents1),
"role_2_entitlements": len(ents2),
"overlapping_entitlements": overlap,
"overlap_percentage": round(overlap_pct, 1),
"recommendation": "CONSOLIDATE" if overlap_pct > 90 else "REVIEW"
})
return sorted(overlap_report, key=lambda x: x["overlap_percentage"], reverse=True)
def find_orphaned_access(self):
"""
Encontrar permissões que não se alinham mais a nenhuma definição de papel.
São exceções que se acumularam ao longo do tempo.
"""
# Obter definições de birthright
birthright = self.mine_birthright_roles(min_assignment_pct=0.5)
orphaned = []
for _, row in self.access_data.iterrows():
job_birthright = birthright.get(row["job_code"], {})
expected_ents = set()
for ent in job_birthright.get("birthright_entitlements", []):
expected_ents.add((ent["application"], ent["entitlement"]))
current_ent = (row["application"], row["entitlement"])