mirror of https://github.com/interlegis/sigi.git
Sesóstris Vieira
1 year ago
17 changed files with 814 additions and 823 deletions
@ -0,0 +1,136 @@ |
|||||
|
import ldap |
||||
|
from django.conf import settings |
||||
|
from django.utils.translation import gettext as _ |
||||
|
from django_auth_ldap.config import _DeepStringCoder |
||||
|
from django_extensions.management.jobs import DailyJob |
||||
|
from sigi.apps.utils.management.jobs import JobReportMixin |
||||
|
from sigi.apps.servidores.models import Servico, Servidor |
||||
|
from sigi.apps.servidores.utils import ( |
||||
|
servidor_update_from_ldap, |
||||
|
servidor_create_or_update, |
||||
|
user_staff_and_group, |
||||
|
) |
||||
|
|
||||
|
|
||||
|
class Job(JobReportMixin, DailyJob): |
||||
|
help = _("Sincroniza servidores com o ldap") |
||||
|
report_data = [] |
||||
|
|
||||
|
def do_job(self): |
||||
|
coder = _DeepStringCoder("utf8") |
||||
|
|
||||
|
connect = ldap.initialize(settings.AUTH_LDAP_SERVER_URI) |
||||
|
connect.protocol_version = 3 |
||||
|
connect.set_option(ldap.OPT_REFERRALS, 0) |
||||
|
connect.simple_bind_s( |
||||
|
settings.AUTH_LDAP_BIND_DN, settings.AUTH_LDAP_BIND_PASSWORD |
||||
|
) |
||||
|
page_control = ldap.controls.SimplePagedResultsControl( |
||||
|
True, size=1000, cookie="" |
||||
|
) |
||||
|
|
||||
|
total_ldap = 0 |
||||
|
total_create = 0 |
||||
|
total_update = 0 |
||||
|
total_deactive = 0 |
||||
|
|
||||
|
servidores = { |
||||
|
s.ldap_dn: s |
||||
|
for s in Servidor.objects.exclude(ldap_dn="").exclude(externo=True) |
||||
|
} |
||||
|
|
||||
|
while True: |
||||
|
response = connect.search_ext( |
||||
|
settings.AUTH_LDAP_USER, |
||||
|
ldap.SCOPE_ONELEVEL, |
||||
|
settings.LDAP_GET_ALL_USERS, |
||||
|
serverctrls=[page_control], |
||||
|
) |
||||
|
rtype, rdata, rmsgid, serverctrls = connect.result3(response) |
||||
|
decoded_data = coder.decode(rdata) |
||||
|
|
||||
|
controls = [ |
||||
|
control |
||||
|
for control in serverctrls |
||||
|
if control.controlType |
||||
|
== ldap.controls.SimplePagedResultsControl.controlType |
||||
|
] |
||||
|
if not controls: |
||||
|
raise Exception("The LDAP server ignores RFC 2696 control") |
||||
|
|
||||
|
for dn, ldap_user in decoded_data: |
||||
|
total_ldap += 1 |
||||
|
resp, servidor = servidor_create_or_update( |
||||
|
ldap_attrs=ldap_user |
||||
|
) |
||||
|
if servidor.user: |
||||
|
user_staff_and_group(servidor.user, ldap_user) |
||||
|
if resp == servidor_create_or_update.UPDATED: |
||||
|
total_update += 1 |
||||
|
self.report_data.append( |
||||
|
_(f"{servidor.nome_completo} atualizado") |
||||
|
) |
||||
|
elif resp == servidor_create_or_update.CREATED: |
||||
|
total_create += 1 |
||||
|
self.report_data.append( |
||||
|
_(f"{servidor.nome_completo} criado") |
||||
|
) |
||||
|
|
||||
|
if dn in servidores: |
||||
|
del servidores[dn] |
||||
|
|
||||
|
if not controls[0].cookie: |
||||
|
break |
||||
|
page_control.cookie = controls[0].cookie |
||||
|
|
||||
|
for dn, servidor in servidores.items(): |
||||
|
rdata = connect.search_s( |
||||
|
settings.AUTH_LDAP_USER, |
||||
|
ldap.SCOPE_SUBTREE, |
||||
|
ldap.filter.filter_format("(distinguishedName=%s)", [dn]), |
||||
|
) |
||||
|
|
||||
|
if rdata: |
||||
|
ldap_attrs = coder.decode(rdata[0][1]) |
||||
|
if servidor.user: |
||||
|
user_staff_and_group(servidor.user, ldap_attrs) |
||||
|
if ( |
||||
|
servidor_update_from_ldap(servidor, ldap_attrs) |
||||
|
== servidor_create_or_update.UPDATED |
||||
|
): |
||||
|
total_update += 1 |
||||
|
else: |
||||
|
if servidor.user: |
||||
|
servidor.user.is_active = False |
||||
|
total_deactive += 1 |
||||
|
|
||||
|
# Reporta servidores que não estão no LDAP e também não são externos |
||||
|
|
||||
|
self.report_data.append("") |
||||
|
self.report_data.append( |
||||
|
_( |
||||
|
"Servidores que não estão no LDAP e também não estão marcados " |
||||
|
"como Externos" |
||||
|
) |
||||
|
) |
||||
|
self.report_data.append( |
||||
|
_( |
||||
|
"=============================================================" |
||||
|
"=============" |
||||
|
) |
||||
|
) |
||||
|
self.report_data.append("") |
||||
|
|
||||
|
for s in Servidor.objects.filter( |
||||
|
ldap_dn="", externo=False, sigi=False |
||||
|
).order_by("nome_completo"): |
||||
|
self.report_data.append(s.nome_completo) |
||||
|
|
||||
|
self.report_data.append("") |
||||
|
self.report_data.append(_("RESUMO")) |
||||
|
self.report_data.append(_("======")) |
||||
|
self.report_data.append("") |
||||
|
self.report_data.append(_(f"{total_ldap} usuários lidos do LDAP")) |
||||
|
self.report_data.append(_(f"{total_create} novos servidores criados")) |
||||
|
self.report_data.append(_(f"{total_update} servidores atualizados")) |
||||
|
self.report_data.append(_(f"{total_deactive} usuários desativados")) |
@ -1,70 +1,61 @@ |
|||||
# coding: utf-8 |
|
||||
from django.contrib.auth.models import User, Group |
|
||||
from sigi.apps.servidores.models import Servidor |
|
||||
from django.core.management.base import BaseCommand |
from django.core.management.base import BaseCommand |
||||
|
from sigi.apps.servidores.models import Servidor |
||||
|
from sigi.apps.servidores.utils import mescla_servidores |
||||
|
|
||||
|
|
||||
class Command(BaseCommand): |
class Command(BaseCommand): |
||||
help = "Transfere os dados do servidor OLD para o servidor NEW." |
help = "Transfere os dados do servidor SOURCE para o servidor TARGET." |
||||
args = "old_id new_id" |
|
||||
|
def add_arguments(self, parser): |
||||
|
parser.add_argument( |
||||
|
"source_id", |
||||
|
help="ID do servidor que será removido", |
||||
|
nargs=1, |
||||
|
type=int, |
||||
|
) |
||||
|
parser.add_argument( |
||||
|
"target_id", |
||||
|
help="ID do servidor que receberá os dados do que será removido", |
||||
|
nargs=1, |
||||
|
type=int, |
||||
|
) |
||||
|
|
||||
def handle(self, *args, **options): |
def handle(self, *args, **options): |
||||
if len(args) != 2: |
source_id = options["source_id"][0] |
||||
self.stderr.write("Informe old_id e new_id") |
target_id = options["target_id"][0] |
||||
|
|
||||
|
try: |
||||
|
servidor_source = Servidor.objects.get(id=source_id) |
||||
|
except Servidor.DoesNotExist: |
||||
|
self.stdout.write( |
||||
|
self.style.WARNING(f"Não existe servidor com ID {source_id}") |
||||
|
) |
||||
|
return |
||||
|
try: |
||||
|
servidor_target = Servidor.objects.get(id=target_id) |
||||
|
except Servidor.DoesNotExist: |
||||
|
self.stdout.write( |
||||
|
self.style.WARNING(f"Não existe servidor com ID {target_id}") |
||||
|
) |
||||
return |
return |
||||
|
|
||||
old_id = args[0] |
|
||||
new_id = args[1] |
|
||||
|
|
||||
old = Servidor.objects.get(id=old_id) |
|
||||
new = Servidor.objects.get(id=new_id) |
|
||||
|
|
||||
self.stdout.write( |
self.stdout.write( |
||||
self.style.WARNING( |
self.style.WARNING( |
||||
"Transferir dados de {old_name} para {new_name}".format( |
f"Transferir dados de {servidor_source.nome_completo} " |
||||
old_name=old.nome_completo, new_name=new.nome_completo |
f"para {servidor_target.nome_completo}" |
||||
) |
|
||||
) |
) |
||||
) |
) |
||||
|
|
||||
self.stdout.write("\t* Transferindo a carteira de atendimento...") |
resp = input("Continuar? [sim / NÃO]: ") |
||||
for casa in old.casas_que_gerencia.all(): |
|
||||
new.casas_que_gerencia.add(casa) |
|
||||
old.casas_que_gerencia.remove(casa) |
|
||||
|
|
||||
self.stdout.write("\t* Transferindo ocorrências registradas...") |
|
||||
old.ocorrencia_set.all().update(servidor_registro=new) |
|
||||
|
|
||||
self.stdout.write("\t* Transferindo comentários de ocorrências...") |
if resp.lower() != "sim": |
||||
old.comentario_set.all().update(usuario=new) |
self.stdout.write(self.style.NOTICE("Abortado!")) |
||||
|
return |
||||
self.stdout.write("\t* Transferindo convênios geridos...") |
|
||||
old.convenio_set.all().update(servidor_gestao=new) |
|
||||
|
|
||||
self.stdout.write("\t* Transferindo convênios acompanhados...") |
|
||||
old.convenio_set.all().update(acompanha=new) |
|
||||
|
|
||||
self.stdout.write("\t* Transferindo participação em eventos...") |
|
||||
old.equipe_evento.all().update(membro=new) |
|
||||
|
|
||||
self.stdout.write("\t* Transferindo convites para eventos...") |
|
||||
old.convite_set.all().update(servidor=new) |
|
||||
|
|
||||
self.stdout.write("\t* Transferindo diagnósticos...") |
|
||||
old.diagnostico_set.all().update(responsavel=new) |
|
||||
|
|
||||
self.stdout.write("\t* Transferindo participação em diagnósticos...") |
|
||||
old.equipe_set.all().update(membro=new) |
|
||||
|
|
||||
self.stdout.write("\t* Transferindo dados de autenticação...") |
|
||||
|
|
||||
if new.user: |
mescla_servidores( |
||||
old.user.logentry_set.all().update(user=new) |
servidor_source=servidor_source, |
||||
old.user.delete() |
servidor_target=servidor_target, |
||||
else: |
verbose=True, |
||||
new.user = old.user |
) |
||||
new.save() |
|
||||
old.user = None |
|
||||
old.save() |
|
||||
|
|
||||
self.stdout.write("Concluído!") |
self.stdout.write("Concluído!") |
||||
|
@ -0,0 +1,62 @@ |
|||||
|
from django.contrib.auth.models import User |
||||
|
from django.core.management.base import BaseCommand |
||||
|
from sigi.apps.servidores.models import Servidor |
||||
|
from sigi.apps.servidores.utils import mescla_users |
||||
|
|
||||
|
|
||||
|
class Command(BaseCommand): |
||||
|
help = "Transfere os dados do usuário SOURCE para o usuário TARGET." |
||||
|
|
||||
|
def add_arguments(self, parser): |
||||
|
parser.add_argument( |
||||
|
"source_name", |
||||
|
help="username do usuário que será removido", |
||||
|
nargs=1, |
||||
|
type=str, |
||||
|
) |
||||
|
parser.add_argument( |
||||
|
"target_name", |
||||
|
help="username do usuário que receberá os dados do que será removido", |
||||
|
nargs=1, |
||||
|
type=str, |
||||
|
) |
||||
|
|
||||
|
def handle(self, *args, **options): |
||||
|
source_name = options["source_name"][0] |
||||
|
target_name = options["target_name"][0] |
||||
|
|
||||
|
try: |
||||
|
user_source = User.objects.get(username__iexact=source_name) |
||||
|
except User.DoesNotExist: |
||||
|
self.stdout.write( |
||||
|
self.style.WARNING(f"Não existe o usuário {source_name}") |
||||
|
) |
||||
|
return |
||||
|
try: |
||||
|
user_target = User.objects.get(username__iexact=target_name) |
||||
|
except User.DoesNotExist: |
||||
|
self.stdout.write( |
||||
|
self.style.WARNING(f"Não existe o usuário {target_name}") |
||||
|
) |
||||
|
return |
||||
|
|
||||
|
self.stdout.write( |
||||
|
self.style.WARNING( |
||||
|
f"Transferir dados de {user_source.get_full_name()} " |
||||
|
f"para {user_target.get_full_name()}" |
||||
|
) |
||||
|
) |
||||
|
|
||||
|
resp = input("Continuar? [sim / NÃO]: ") |
||||
|
|
||||
|
if resp.lower() != "sim": |
||||
|
self.stdout.write(self.style.NOTICE("Abortado!")) |
||||
|
return |
||||
|
|
||||
|
mescla_users( |
||||
|
user_source=user_source, |
||||
|
user_target=user_target, |
||||
|
verbose=True, |
||||
|
) |
||||
|
|
||||
|
self.stdout.write("Concluído!") |
@ -1,247 +0,0 @@ |
|||||
# coding= utf-8 |
|
||||
import csv |
|
||||
import re |
|
||||
|
|
||||
from datetime import datetime |
|
||||
from django.contrib.auth.models import User |
|
||||
from django.core.management.base import BaseCommand |
|
||||
from django.utils.translation import gettext as _ |
|
||||
|
|
||||
from sigi.apps.contatos.models import Municipio |
|
||||
from sigi.apps.servidores.models import Servidor, Servico, Subsecretaria |
|
||||
|
|
||||
|
|
||||
# Funcao.objects.all().delete() |
|
||||
# Ferias.objects.all().delete() |
|
||||
# Licenca.objects.all().delete() |
|
||||
# for u in User.objects.filter(date_joined__gte=datetime(2011, 12, 9, 10, 58, 49, 83734)).all(): |
|
||||
# u.servidor_set.all().delete() |
|
||||
# u.delete() |
|
||||
|
|
||||
|
|
||||
class MigrationError(Exception): |
|
||||
pass |
|
||||
|
|
||||
|
|
||||
class Command(BaseCommand): |
|
||||
help = _("Migra usuários do antigo Sistema de RH") |
|
||||
|
|
||||
def to_date(self, data): |
|
||||
return datetime.strptime(data, "%Y-%m-%d 00:00:00") |
|
||||
|
|
||||
def handle(self, *args, **options): |
|
||||
reader = csv.reader( |
|
||||
open("/tmp/pessoal.csv"), delimiter=",", quotechar='"' |
|
||||
) |
|
||||
|
|
||||
BRASILIA = Municipio.objects.get(codigo_ibge=5300108) |
|
||||
|
|
||||
# Read the column names from the first line of the file |
|
||||
fields = reader.next() |
|
||||
for row in reader: |
|
||||
# cria um dict com a primeira e a linha atual |
|
||||
pessoa = zip(fields, row) |
|
||||
p = {} |
|
||||
for name, value in pessoa: |
|
||||
p[name] = value.strip() |
|
||||
|
|
||||
user = None |
|
||||
if not p["email"]: |
|
||||
username = "" |
|
||||
email = "" |
|
||||
elif not ("@interlegis" in p["email"]): |
|
||||
username = p["email"].split("@")[0].strip().lower() |
|
||||
email = "" |
|
||||
else: |
|
||||
username = p["email"].split("@")[0].strip().lower() |
|
||||
email = username + "@interlegis.leg.br" |
|
||||
|
|
||||
# buscar usuário e servidor da linha atual |
|
||||
try: |
|
||||
# procuro o usuario por email do interlegis |
|
||||
if email: |
|
||||
try: |
|
||||
user = User.objects.get(email=email) |
|
||||
except User.DoesNotExist: |
|
||||
email = username + "@interlegis.leg.br" |
|
||||
try: |
|
||||
user = User.objects.get(email=email) |
|
||||
except User.DoesNotExist: |
|
||||
pass |
|
||||
|
|
||||
if not user and username: |
|
||||
try: |
|
||||
user = User.objects.get(username=username) |
|
||||
except User.DoesNotExist: |
|
||||
try: |
|
||||
user = User.objects.get(username=username + "__") |
|
||||
except User.DoesNotExist: |
|
||||
pass |
|
||||
|
|
||||
if not user: |
|
||||
if not username: |
|
||||
raise MigrationError |
|
||||
|
|
||||
if not email: |
|
||||
# cria um username a partir do email sem |
|
||||
# colidir com os usuarios ldap |
|
||||
username = username + "__" |
|
||||
|
|
||||
names = p["nome_completo"].split(" ") |
|
||||
first_name = names[0] |
|
||||
last_name = " ".join(names[1:]) |
|
||||
|
|
||||
user = User.objects.create( |
|
||||
username=username, |
|
||||
email=email, |
|
||||
first_name=first_name, |
|
||||
last_name=last_name[:30], |
|
||||
is_active=False, |
|
||||
) |
|
||||
|
|
||||
servidor = user.servidor |
|
||||
except Servidor.DoesNotExist: |
|
||||
servidor = Servidor.objects.create( |
|
||||
user=user, |
|
||||
nome_completo="%s %s" % (user.first_name, user.last_name), |
|
||||
) |
|
||||
except MigrationError as e: |
|
||||
continue |
|
||||
|
|
||||
# mapeando dados simples |
|
||||
servidor.nome_completo = p["nome_completo"] |
|
||||
servidor.cpf = p["cpf"] |
|
||||
servidor.rg = p["identidade"] |
|
||||
servidor.apelido = p["username"] |
|
||||
servidor.matricula = p["matricula"] |
|
||||
servidor.ato_exoneracao = p["ato_exoneracao"] |
|
||||
servidor.ato_numero = p["ato_numero"] |
|
||||
servidor.ramal = p["ramal"] |
|
||||
|
|
||||
if p["email"] and not "@interlegis" in p["email"]: |
|
||||
servidor.email_pessoal = p["email"] |
|
||||
|
|
||||
if p["inativo"] == "-1": |
|
||||
servidor.user.is_active = False |
|
||||
else: |
|
||||
servidor.user.is_active = True |
|
||||
servidor.user.save() |
|
||||
|
|
||||
if p["de_fora"] == "-1": |
|
||||
servidor.de_fora = True |
|
||||
else: |
|
||||
servidor.de_fora = False |
|
||||
|
|
||||
if p["sexo"].upper() == "M": |
|
||||
servidor.sexo = "M" |
|
||||
elif p["sexo"].upper() == "F": |
|
||||
servidor.sexo = "F" |
|
||||
|
|
||||
if p["turno"] == "1": |
|
||||
servidor.turno = "M" |
|
||||
elif p["turno"] == "2": |
|
||||
servidor.turno = "T" |
|
||||
elif p["turno"] == "3": |
|
||||
servidor.turno = "N" |
|
||||
|
|
||||
if p["aniversario"]: |
|
||||
servidor.data_nascimento = self.to_date(p["aniversario"]) |
|
||||
|
|
||||
if p["data_nomeacao"]: |
|
||||
servidor.data_nomeacao = self.to_date(p["data_nomeacao"]) |
|
||||
|
|
||||
if p["secretaria_sigla"]: |
|
||||
if " - " in p["secretaria_nome"]: |
|
||||
secretaria_nome = p["secretaria_nome"].split(" - ")[1] |
|
||||
else: |
|
||||
secretaria_nome = p["secretaria_nome"] |
|
||||
|
|
||||
secretaria = Subsecretaria.objects.get_or_create( |
|
||||
sigla=p["secretaria_sigla"], nome=secretaria_nome |
|
||||
)[0] |
|
||||
|
|
||||
if " - " in p["servico_nome"]: |
|
||||
servico_nome = p["servico_nome"].split(" - ")[1] |
|
||||
else: |
|
||||
servico_nome = p["servico_nome"] |
|
||||
|
|
||||
servico = Servico.objects.get_or_create( |
|
||||
sigla=p["servico_sigla"], nome=servico_nome |
|
||||
)[0] |
|
||||
|
|
||||
servico.subsecretaria = secretaria |
|
||||
servico.save() |
|
||||
servidor.servico = servico |
|
||||
|
|
||||
if p["telefone"]: |
|
||||
try: |
|
||||
t = servidor.telefones.get(numero=p["telefone"]) |
|
||||
except: |
|
||||
t = servidor.telefones.create(numero=p["telefone"]) |
|
||||
t.tipo = "F" |
|
||||
t.save() |
|
||||
|
|
||||
if p["celular"]: |
|
||||
try: |
|
||||
t = servidor.telefones.get(numero=p["celular"]) |
|
||||
except: |
|
||||
t = servidor.telefones.create(numero=p["celular"]) |
|
||||
t.tipo = "M" |
|
||||
t.save() |
|
||||
|
|
||||
if p["endereco"]: |
|
||||
try: |
|
||||
e = servidor.endereco.get(logradouro=p["endereco"]) |
|
||||
except: |
|
||||
e = servidor.endereco.create(logradouro=p["endereco"]) |
|
||||
e.municipio = BRASILIA |
|
||||
e.bairro = p["cidade"] # bizarro mas é isso mesmo |
|
||||
e.cep = re.sub("\D", "", p["cep"]) |
|
||||
e.save() |
|
||||
|
|
||||
servidor.apontamentos = p["apontamentos"] |
|
||||
servidor.obs = p["obs"] |
|
||||
|
|
||||
if p["cargo"] or p["funcao"]: |
|
||||
funcao = servidor.funcao_set.get_or_create( |
|
||||
funcao=p["funcao"], |
|
||||
cargo=p["cargo"], |
|
||||
)[0] |
|
||||
|
|
||||
if p["data_bap_entrada"]: |
|
||||
funcao.data_bap_entrada = self.to_date( |
|
||||
p["data_bap_entrada"] |
|
||||
) |
|
||||
|
|
||||
if p["data_bap_saida"]: |
|
||||
funcao.data_bap_saida = self.to_date(p["data_bap_saida"]) |
|
||||
|
|
||||
if p["data_entrada"]: |
|
||||
funcao.inicio_funcao = self.to_date(p["data_entrada"]) |
|
||||
|
|
||||
if p["data_saida"]: |
|
||||
funcao.fim_funcao = self.to_date(p["data_saida"]) |
|
||||
|
|
||||
funcao.bap_entrada = p["bap_entrada"] |
|
||||
funcao.bap_saida = p["bap_saida"] |
|
||||
funcao.save() |
|
||||
|
|
||||
if re.search(r"estagi.ri[o|a]", p["cargo"], re.I): # XXX i18n |
|
||||
# TODO inserir dados de estagio |
|
||||
pass |
|
||||
|
|
||||
if p["inicio_ferias"] and p["final_ferias"]: |
|
||||
servidor.ferias_set.get_or_create( |
|
||||
inicio_ferias=self.to_date(p["inicio_ferias"]), |
|
||||
fim_ferias=self.to_date(p["final_ferias"]), |
|
||||
obs=p["obs_ferias"], |
|
||||
) |
|
||||
|
|
||||
if p["inicio_licenca"] and p["fim_licenca"]: |
|
||||
servidor.licenca_set.get_or_create( |
|
||||
inicio_licenca=self.to_date(p["inicio_licenca"]), |
|
||||
fim_licenca=self.to_date(p["fim_licenca"]), |
|
||||
obs=p["obs_licenca"], |
|
||||
) |
|
||||
|
|
||||
servidor.save() |
|
@ -1,203 +0,0 @@ |
|||||
# coding: utf-8 |
|
||||
import ldap |
|
||||
from django.contrib.auth.models import User, Group |
|
||||
from django.core.management.base import BaseCommand |
|
||||
|
|
||||
from sigi.apps.servidores.models import Servidor |
|
||||
from sigi.settings import * |
|
||||
|
|
||||
|
|
||||
class Command(BaseCommand): |
|
||||
help = "Sincroniza Usuários e Servidores com o LDAP" |
|
||||
|
|
||||
def handle(self, *args, **options): |
|
||||
self.sync_groups() |
|
||||
self.sync_users() |
|
||||
|
|
||||
def get_ldap_groups(self): |
|
||||
filter = "(&(objectclass=Group))" |
|
||||
values = [ |
|
||||
"cn", |
|
||||
] |
|
||||
l = ldap.initialize(AUTH_LDAP_SERVER_URI) |
|
||||
try: |
|
||||
l.protocol_version = ldap.VERSION3 |
|
||||
l.set_option(ldap.OPT_REFERRALS, 0) |
|
||||
l.simple_bind_s( |
|
||||
AUTH_LDAP_BIND_DN.encode("utf-8"), AUTH_LDAP_BIND_PASSWORD |
|
||||
) |
|
||||
|
|
||||
page_control = ldap.controls.SimplePagedResultsControl( |
|
||||
True, size=1000, cookie="" |
|
||||
) |
|
||||
result = [] |
|
||||
pages = 0 |
|
||||
|
|
||||
while True: |
|
||||
pages += 1 |
|
||||
response = l.search_ext( |
|
||||
AUTH_LDAP_GROUP, |
|
||||
ldap.SCOPE_SUBTREE, |
|
||||
filter, |
|
||||
values, |
|
||||
serverctrls=[page_control], |
|
||||
) |
|
||||
rtype, rdata, rmsgid, serverctrls = l.result3(response) |
|
||||
result.extend(rdata) |
|
||||
controls = [ |
|
||||
control |
|
||||
for control in serverctrls |
|
||||
if control.controlType |
|
||||
== ldap.controls.SimplePagedResultsControl.controlType |
|
||||
] |
|
||||
if not controls: |
|
||||
raise Exception("The server ignores RFC 2696 control") |
|
||||
if not controls[0].cookie: |
|
||||
break |
|
||||
page_control.cookie = controls[0].cookie |
|
||||
# result_id = l.search(AUTH_LDAP_GROUP, ldap.SCOPE_SUBTREE, filter, values) |
|
||||
# result_type, result_data = l.result(result_id, 1) |
|
||||
finally: |
|
||||
l.unbind() |
|
||||
return result |
|
||||
|
|
||||
def get_ldap_users(self): |
|
||||
filter = "(&(objectclass=user)(|(memberof=CN=lgs_ilb,OU=GruposAutomaticosOU,DC=senado,DC=gov,DC=br)(memberof=CN=lgt_ilb,OU=GruposAutomaticosOU,DC=senado,DC=gov,DC=br)(memberof=CN=lge_ilb,OU=GruposAutomaticosOU,DC=senado,DC=gov,DC=br)))" |
|
||||
values = [ |
|
||||
"sAMAccountName", |
|
||||
"userPrincipalName", |
|
||||
"givenName", |
|
||||
"sn", |
|
||||
"cn", |
|
||||
] |
|
||||
l = ldap.initialize(AUTH_LDAP_SERVER_URI) |
|
||||
try: |
|
||||
l.protocol_version = ldap.VERSION3 |
|
||||
l.set_option(ldap.OPT_REFERRALS, 0) |
|
||||
l.simple_bind_s( |
|
||||
AUTH_LDAP_BIND_DN.encode("utf-8"), AUTH_LDAP_BIND_PASSWORD |
|
||||
) |
|
||||
|
|
||||
page_control = ldap.controls.SimplePagedResultsControl( |
|
||||
True, size=1000, cookie="" |
|
||||
) |
|
||||
|
|
||||
result = [] |
|
||||
pages = 0 |
|
||||
|
|
||||
while True: |
|
||||
pages += 1 |
|
||||
response = l.search_ext( |
|
||||
AUTH_LDAP_USER.encode("utf-8"), |
|
||||
ldap.SCOPE_SUBTREE, |
|
||||
filter, |
|
||||
values, |
|
||||
serverctrls=[page_control], |
|
||||
) |
|
||||
rtype, rdata, rmsgid, serverctrls = l.result3(response) |
|
||||
result.extend(rdata) |
|
||||
controls = [ |
|
||||
control |
|
||||
for control in serverctrls |
|
||||
if control.controlType |
|
||||
== ldap.controls.SimplePagedResultsControl.controlType |
|
||||
] |
|
||||
if not controls: |
|
||||
raise Exception("The server ignores RFC 2696 control") |
|
||||
if not controls[0].cookie: |
|
||||
break |
|
||||
page_control.cookie = controls[0].cookie |
|
||||
# result_id = l.search(AUTH_LDAP_USER.encode('utf-8'), ldap.SCOPE_SUBTREE, filter, values) |
|
||||
# result_type, result_data = l.result(result_id, 1) |
|
||||
finally: |
|
||||
l.unbind() |
|
||||
return result |
|
||||
|
|
||||
def sync_groups(self): |
|
||||
print("Syncing groups...") |
|
||||
ldap_groups = self.get_ldap_groups() |
|
||||
print(f"\tFetched groups: {ldap_groups}") |
|
||||
for ldap_group in ldap_groups: |
|
||||
try: |
|
||||
group_name = ldap_group[1]["cn"][0] |
|
||||
except: |
|
||||
pass |
|
||||
else: |
|
||||
try: |
|
||||
group = Group.objects.get(name=group_name) |
|
||||
except Group.DoesNotExist: |
|
||||
group = Group(name=group_name) |
|
||||
group.save() |
|
||||
print(f"\tGroup '{group_name}' created.") |
|
||||
print("Groups are synchronized.") |
|
||||
|
|
||||
def sync_users(self): |
|
||||
print("Syncing users...") |
|
||||
ldap_users = self.get_ldap_users() |
|
||||
print(f"\tFetched users: {ldap_users}") |
|
||||
|
|
||||
def get_ldap_property(ldap_user, property_name, default_value=None): |
|
||||
value = ldap_user[1].get(property_name, None) |
|
||||
return value[0].decode("utf8") if value else default_value |
|
||||
|
|
||||
for ldap_user in ldap_users: |
|
||||
username = get_ldap_property(ldap_user, "sAMAccountName") |
|
||||
if username: |
|
||||
email = get_ldap_property(ldap_user, "userPrincipalName", "") |
|
||||
first_name = get_ldap_property( |
|
||||
ldap_user, "givenName", username |
|
||||
) |
|
||||
last_name = get_ldap_property(ldap_user, "sn", "")[:30] |
|
||||
try: |
|
||||
user = User.objects.get(username=username) |
|
||||
except User.DoesNotExist: |
|
||||
try: |
|
||||
user = User.objects.get(email=email) |
|
||||
old_username = user.username |
|
||||
user.username = username |
|
||||
print( |
|
||||
f"\tUser with email {email} had his/her username updated from [{old_username}] to [{username}]." |
|
||||
) |
|
||||
except User.DoesNotExist: |
|
||||
user = User.objects.create_user( |
|
||||
username=username, |
|
||||
first_name=first_name, |
|
||||
last_name=last_name, |
|
||||
email=email, |
|
||||
) |
|
||||
print(f"\tUser '{username}' created.") |
|
||||
|
|
||||
if not user.first_name == first_name: |
|
||||
user.first_name = first_name |
|
||||
print(f"\tUser '{username}' first name updated.") |
|
||||
if not user.last_name == last_name: |
|
||||
user.last_name = last_name |
|
||||
print(f"\tUser '{username}' last name updated.") |
|
||||
if not user.email == email: |
|
||||
user.email = email |
|
||||
print(f"\tUser '{username}' email updated.") |
|
||||
|
|
||||
nome_completo = get_ldap_property(ldap_user, "cn", "") |
|
||||
try: |
|
||||
servidor = user.servidor |
|
||||
except Servidor.DoesNotExist: |
|
||||
try: |
|
||||
servidor = Servidor.objects.get( |
|
||||
nome_completo=nome_completo |
|
||||
) |
|
||||
except Servidor.DoesNotExist: |
|
||||
servidor = user.servidor_set.create( |
|
||||
nome_completo=nome_completo |
|
||||
) |
|
||||
print(f"\tServidor '{nome_completo}' created.") |
|
||||
else: |
|
||||
if not servidor.nome_completo == nome_completo: |
|
||||
servidor.nome_completo = nome_completo |
|
||||
print( |
|
||||
f"\tFull name of Servidor '{nome_completo}' updated." |
|
||||
) |
|
||||
|
|
||||
servidor.user = user |
|
||||
servidor.save() |
|
||||
user.save() |
|
||||
print("Users are synchronized.") |
|
@ -1,284 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
import pytest |
|
||||
from django.contrib.auth.models import User |
|
||||
from django_dynamic_fixture import G |
|
||||
|
|
||||
from sigi.apps.servidores.management.commands.sync_ldap import Command |
|
||||
|
|
||||
|
|
||||
pytestmark = pytest.mark.django_db |
|
||||
|
|
||||
|
|
||||
class StubCommand(Command): |
|
||||
def __init__(self, users): |
|
||||
super(StubCommand, self).__init__() |
|
||||
self.users = users |
|
||||
|
|
||||
def get_ldap_users(self): |
|
||||
return self.users |
|
||||
|
|
||||
|
|
||||
def create_stub_user(username, nome_completo, first_name, last_name, email): |
|
||||
user = G( |
|
||||
User, |
|
||||
username=username, |
|
||||
first_name=first_name, |
|
||||
last_name=last_name, |
|
||||
email=email, |
|
||||
) |
|
||||
user.servidor.nome_completo = nome_completo |
|
||||
return user |
|
||||
|
|
||||
|
|
||||
ALEX_LDAP, BRUNO_LDAP, RITA_LDAP = [ |
|
||||
( |
|
||||
"...", |
|
||||
{ |
|
||||
"cn": ["Alex Lima"], |
|
||||
"givenName": ["Alex"], |
|
||||
"sAMAccountName": ["alexlima"], |
|
||||
"sn": ["Lima"], |
|
||||
"userPrincipalName": ["alexlima@interlegis.leg.br"], |
|
||||
}, |
|
||||
), |
|
||||
( |
|
||||
"...", |
|
||||
{ |
|
||||
"cn": ["Bruno Almeida Prado"], |
|
||||
"givenName": ["Bruno"], |
|
||||
"sAMAccountName": ["bruno"], |
|
||||
"sn": ["Almeida Prado"], |
|
||||
"userPrincipalName": ["bruno@interlegis.leg.br"], |
|
||||
}, |
|
||||
), |
|
||||
( |
|
||||
"...", |
|
||||
{ |
|
||||
"cn": ["Cl\xc3\xa1udia de C\xc3\xa1ssia"], |
|
||||
"givenName": ["Cl\xc3\xa1udia"], |
|
||||
"sAMAccountName": ["claudia"], |
|
||||
"sn": ["de C\xc3\xa1ssia"], |
|
||||
"userPrincipalName": ["claudia@interlegis.leg.br"], |
|
||||
}, |
|
||||
), |
|
||||
] |
|
||||
|
|
||||
|
|
||||
@pytest.mark.parametrize( |
|
||||
"before, ldap_users, after, messages", |
|
||||
[ |
|
||||
# new user from ldap is created |
|
||||
( |
|
||||
[], |
|
||||
[ALEX_LDAP], |
|
||||
[ |
|
||||
( |
|
||||
"alexlima", |
|
||||
"Alex Lima", |
|
||||
"Alex", |
|
||||
"Lima", |
|
||||
"alexlima@interlegis.leg.br", |
|
||||
) |
|
||||
], |
|
||||
""" |
|
||||
User 'alexlima' created. |
|
||||
Users are synchronized. |
|
||||
""", |
|
||||
), |
|
||||
# nothing changes |
|
||||
( |
|
||||
[ |
|
||||
( |
|
||||
"alexlima", |
|
||||
"Alex Lima", |
|
||||
"Alex", |
|
||||
"Lima", |
|
||||
"alexlima@interlegis.leg.br", |
|
||||
) |
|
||||
], |
|
||||
[ALEX_LDAP], |
|
||||
[ |
|
||||
( |
|
||||
"alexlima", |
|
||||
"Alex Lima", |
|
||||
"Alex", |
|
||||
"Lima", |
|
||||
"alexlima@interlegis.leg.br", |
|
||||
) |
|
||||
], |
|
||||
""" |
|
||||
Users are synchronized. |
|
||||
""", |
|
||||
), |
|
||||
# unicode encoding from LDAP data works well |
|
||||
( |
|
||||
[ |
|
||||
( |
|
||||
"claudia", |
|
||||
"Cláudia de Cássia", |
|
||||
"Cláudia", |
|
||||
"de Cássia", |
|
||||
"claudia@interlegis.leg.br", |
|
||||
) |
|
||||
], |
|
||||
[RITA_LDAP], |
|
||||
[ |
|
||||
( |
|
||||
"claudia", |
|
||||
"Cláudia de Cássia", |
|
||||
"Cláudia", |
|
||||
"de Cássia", |
|
||||
"claudia@interlegis.leg.br", |
|
||||
) |
|
||||
], |
|
||||
""" |
|
||||
Users are synchronized. |
|
||||
""", |
|
||||
), |
|
||||
# update: full name, first name, last name, email |
|
||||
( |
|
||||
[ |
|
||||
( |
|
||||
"alexlima", |
|
||||
"___", |
|
||||
"___", |
|
||||
"___", |
|
||||
"___", |
|
||||
), |
|
||||
( |
|
||||
"bruno", |
|
||||
"Bruno Almeida Prado", |
|
||||
"___", |
|
||||
"Almeida Prado", |
|
||||
"___", |
|
||||
), |
|
||||
( |
|
||||
"claudia", |
|
||||
"___", |
|
||||
"Cláudia", |
|
||||
"___", |
|
||||
"claudia@interlegis.leg.br", |
|
||||
), |
|
||||
], |
|
||||
[ALEX_LDAP, BRUNO_LDAP, RITA_LDAP], |
|
||||
[ |
|
||||
( |
|
||||
"alexlima", |
|
||||
"Alex Lima", |
|
||||
"Alex", |
|
||||
"Lima", |
|
||||
"alexlima@interlegis.leg.br", |
|
||||
), |
|
||||
( |
|
||||
"bruno", |
|
||||
"Bruno Almeida Prado", |
|
||||
"Bruno", |
|
||||
"Almeida Prado", |
|
||||
"bruno@interlegis.leg.br", |
|
||||
), |
|
||||
( |
|
||||
"claudia", |
|
||||
"Cláudia de Cássia", |
|
||||
"Cláudia", |
|
||||
"de Cássia", |
|
||||
"claudia@interlegis.leg.br", |
|
||||
), |
|
||||
], |
|
||||
""" |
|
||||
User 'alexlima' first name updated. |
|
||||
User 'alexlima' last name updated. |
|
||||
User 'alexlima' email updated. |
|
||||
Full name of Servidor 'Alex Lima' updated. |
|
||||
User 'bruno' first name updated. |
|
||||
User 'bruno' email updated. |
|
||||
Full name of Servidor 'Bruno Almeida Prado' updated. |
|
||||
User 'claudia' last name updated. |
|
||||
Full name of Servidor 'Cláudia de Cássia' updated. |
|
||||
Users are synchronized. |
|
||||
""", |
|
||||
), |
|
||||
# update username (username from LDAP not in base, so match user by email and update username) |
|
||||
# TODO: is this functionality really necessary? If not remove this and corresponding code |
|
||||
# connect servidor with nome_completo to user |
|
||||
# TODO: is this functionality really necessary? If not remove this and corresponding code |
|
||||
# create new servidor with nome_completo and connect to user |
|
||||
# TODO: is this functionality really necessary? If not remove this and corresponding code |
|
||||
# user not present in ldap is NOT deleted |
|
||||
( |
|
||||
[ |
|
||||
( |
|
||||
"alexlima", |
|
||||
"Alex Lima", |
|
||||
"Alex", |
|
||||
"Lima", |
|
||||
"alexlima@interlegis.leg.br", |
|
||||
), |
|
||||
( |
|
||||
"bruno", |
|
||||
"Bruno Almeida Prado", |
|
||||
"Bruno", |
|
||||
"Almeida Prado", |
|
||||
"bruno@interlegis.leg.br", |
|
||||
), |
|
||||
( |
|
||||
"claudia", |
|
||||
"Cláudia de Cássia", |
|
||||
"Cláudia", |
|
||||
"de Cássia", |
|
||||
"claudia@interlegis.leg.br", |
|
||||
), |
|
||||
], |
|
||||
[ALEX_LDAP, RITA_LDAP], |
|
||||
[ |
|
||||
( |
|
||||
"alexlima", |
|
||||
"Alex Lima", |
|
||||
"Alex", |
|
||||
"Lima", |
|
||||
"alexlima@interlegis.leg.br", |
|
||||
), |
|
||||
( |
|
||||
"bruno", |
|
||||
"Bruno Almeida Prado", |
|
||||
"Bruno", |
|
||||
"Almeida Prado", |
|
||||
"bruno@interlegis.leg.br", |
|
||||
), |
|
||||
( |
|
||||
"claudia", |
|
||||
"Cláudia de Cássia", |
|
||||
"Cláudia", |
|
||||
"de Cássia", |
|
||||
"claudia@interlegis.leg.br", |
|
||||
), |
|
||||
], |
|
||||
""" |
|
||||
Users are synchronized. |
|
||||
""", |
|
||||
), |
|
||||
], |
|
||||
) |
|
||||
def test_sync_users(before, ldap_users, after, messages, capsys): |
|
||||
# setup |
|
||||
for user_setup in before: |
|
||||
if type(user_setup) == tuple: |
|
||||
create_stub_user(*user_setup) |
|
||||
assert User.objects.count() == len(before) |
|
||||
|
|
||||
command = StubCommand(ldap_users) |
|
||||
command.sync_users() |
|
||||
users = User.objects.all().order_by("username") |
|
||||
for user, expected in zip(users, after): |
|
||||
real = ( |
|
||||
user.username, |
|
||||
user.servidor.nome_completo, |
|
||||
user.first_name, |
|
||||
user.last_name, |
|
||||
user.email, |
|
||||
) |
|
||||
assert real == expected |
|
||||
|
|
||||
# feedbak messages |
|
||||
out, err = capsys.readouterr() |
|
||||
assert out.strip() == messages.strip() |
|
||||
assert err == "" |
|
@ -0,0 +1,30 @@ |
|||||
|
# Generated by Django 4.2.4 on 2023-10-09 19:31 |
||||
|
|
||||
|
from django.conf import settings |
||||
|
from django.db import migrations, models |
||||
|
import django.db.models.deletion |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
dependencies = [ |
||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL), |
||||
|
("servidores", "0013_servidor_moodle_userid"), |
||||
|
] |
||||
|
|
||||
|
operations = [ |
||||
|
migrations.AddField( |
||||
|
model_name="servidor", |
||||
|
name="ldap_dn", |
||||
|
field=models.CharField(blank=True, editable=False, max_length=200), |
||||
|
), |
||||
|
migrations.AlterField( |
||||
|
model_name="servidor", |
||||
|
name="user", |
||||
|
field=models.ForeignKey( |
||||
|
blank=True, |
||||
|
null=True, |
||||
|
on_delete=django.db.models.deletion.SET_NULL, |
||||
|
to=settings.AUTH_USER_MODEL, |
||||
|
), |
||||
|
), |
||||
|
] |
@ -0,0 +1,313 @@ |
|||||
|
# Generated by Django 4.2.4 on 2023-10-10 11:26 |
||||
|
|
||||
|
import ldap |
||||
|
from django.db import migrations |
||||
|
from django.db.models import Q |
||||
|
from django.conf import settings |
||||
|
from django.contrib.auth import get_user_model |
||||
|
from django_auth_ldap.config import _DeepStringCoder |
||||
|
from sigi.apps.utils import to_ascii |
||||
|
from sigi.apps.servidores.models import Servidor |
||||
|
from sigi.apps.servidores.utils import ( |
||||
|
mescla_users, |
||||
|
mescla_servidores, |
||||
|
servidor_update_from_ldap, |
||||
|
user_staff_and_group, |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def update_user_from_ldap(user, ldap_attrs): |
||||
|
for user_attr, ldap_attr in settings.AUTH_LDAP_USER_ATTR_MAP.items(): |
||||
|
setattr(user, user_attr, ldap_attrs.get(ldap_attr, [""])[0]) |
||||
|
user.username = ldap_attrs.get("sAMAccountName", [""])[0] |
||||
|
user_staff_and_group(user, ldap_attrs) |
||||
|
|
||||
|
|
||||
|
def forwards(apps, schema_editor): |
||||
|
User = get_user_model() |
||||
|
|
||||
|
coder = _DeepStringCoder("utf8") |
||||
|
connect = ldap.initialize(settings.AUTH_LDAP_SERVER_URI) |
||||
|
connect.protocol_version = 3 |
||||
|
connect.set_option(ldap.OPT_REFERRALS, 0) |
||||
|
connect.simple_bind_s( |
||||
|
settings.AUTH_LDAP_BIND_DN, settings.AUTH_LDAP_BIND_PASSWORD |
||||
|
) |
||||
|
|
||||
|
# Eliminar usuário "administrador", transferindo seu log para usuário |
||||
|
# "interlegis". |
||||
|
|
||||
|
if ( |
||||
|
User.objects.filter( |
||||
|
username__in=["administrador", "interlegis"] |
||||
|
).count() |
||||
|
== 2 |
||||
|
): |
||||
|
user = User.objects.get(username="administrador") |
||||
|
user_target = User.objects.get(username="interlegis") |
||||
|
user.logentry_set.update(user=user_target) |
||||
|
user.delete() |
||||
|
print( |
||||
|
f"\n\tUser {user.username} deleted. Logs to {user_target.username}" |
||||
|
) |
||||
|
|
||||
|
# Unificar usuários com nomes iguais minusculos > MAIÚSCULOS |
||||
|
|
||||
|
print("\tJoin users with same usernames in upper/lower cases...") |
||||
|
|
||||
|
joined_names = set() |
||||
|
|
||||
|
for user in User.objects.all(): |
||||
|
if ( |
||||
|
user.username.upper() not in joined_names |
||||
|
and User.objects.filter(username__iexact=user.username) |
||||
|
.exclude(id=user.id) |
||||
|
.exists() |
||||
|
): |
||||
|
user_source = User.objects.get(username=user.username.lower()) |
||||
|
user_target = User.objects.get(username=user.username.upper()) |
||||
|
print(f"\t\t{user_source.username} > {user_target.username}") |
||||
|
mescla_users(user_source, user_target) |
||||
|
joined_names.add(user.username.upper()) |
||||
|
|
||||
|
# Identificar e atualizar todos os users que existem no LDAP # |
||||
|
|
||||
|
valid_users = dict() |
||||
|
for user in User.objects.all(): |
||||
|
rdata = connect.search_s( |
||||
|
settings.AUTH_LDAP_USER, |
||||
|
ldap.SCOPE_SUBTREE, |
||||
|
ldap.filter.filter_format("(sAMAccountName=%s)", [user.username]), |
||||
|
) |
||||
|
if rdata: |
||||
|
dn, ldap_attrs = coder.decode(rdata[0]) |
||||
|
valid_users[dn] = user |
||||
|
update_user_from_ldap(user, ldap_attrs) |
||||
|
if user.servidor: |
||||
|
servidor_update_from_ldap(user.servidor, ldap_attrs) |
||||
|
|
||||
|
# Identifica servidores que estão no LDAP mas, por algum motivo, não estão |
||||
|
# vinculados a algum 'valid_user' identificado ali acima |
||||
|
|
||||
|
for servidor in Servidor.objects.exclude( |
||||
|
user__in=valid_users.values() |
||||
|
).exclude(externo=True): |
||||
|
ldap_filter = ldap.filter.filter_format( |
||||
|
"(cn=%s)", [servidor.nome_completo] |
||||
|
) |
||||
|
rdata = connect.search_s( |
||||
|
settings.AUTH_LDAP_USER, ldap.SCOPE_SUBTREE, ldap_filter |
||||
|
) |
||||
|
if rdata: |
||||
|
dn, ldap_attrs = coder.decode(rdata[0]) |
||||
|
ldap_user_name = ldap_attrs.get("sAMAccountName", [""])[0] |
||||
|
if servidor.user: |
||||
|
if User.objects.filter(username=ldap_user_name).exists(): |
||||
|
user_target = User.objects.get(username=ldap_user_name) |
||||
|
mescla_users(servidor.user, user_target) |
||||
|
valid_users[dn] = user_target |
||||
|
else: |
||||
|
update_user_from_ldap(servidor.user, ldap_attrs) |
||||
|
valid_users[dn] = servidor.user |
||||
|
else: |
||||
|
if User.objects.filter(username=ldap_user_name).exists(): |
||||
|
servidor.user = User.objects.get(username=ldap_user_name) |
||||
|
servidor.save() |
||||
|
valid_users[dn] = servidor.user |
||||
|
else: |
||||
|
servidor.ldap_dn = dn |
||||
|
servidor.save() |
||||
|
|
||||
|
# Eliminar servidores e seus usuários que não têm nenhum vínculo no sigi |
||||
|
# e não constam na lista de valid_users |
||||
|
|
||||
|
print("\tRemoving inactive users and servidores") |
||||
|
|
||||
|
filter = ( |
||||
|
Q(externo=False) |
||||
|
& Q(casas_que_gerencia=None) |
||||
|
& Q(chefe=None) |
||||
|
& Q(comentario=None) |
||||
|
& Q(convenio=None) |
||||
|
& Q(convenios_acompanhados=None) |
||||
|
& Q(equipe_evento=None) |
||||
|
& Q(itemsolicitado=None) |
||||
|
& Q(modulo_apresentador=None) |
||||
|
& Q(modulo_monitor=None) |
||||
|
& Q(ocorrencia=None) |
||||
|
& Q(orgao=None) |
||||
|
& Q(solicitacao=None) |
||||
|
) |
||||
|
|
||||
|
inativos = Servidor.objects.filter(filter).exclude( |
||||
|
user__in=valid_users.values() |
||||
|
) |
||||
|
|
||||
|
removed = User.objects.filter(servidor__in=inativos).delete() |
||||
|
print( |
||||
|
"\n".join( |
||||
|
[ |
||||
|
f"\t\t{value} {key} records removed." |
||||
|
for key, value in removed[1].items() |
||||
|
] |
||||
|
) |
||||
|
) |
||||
|
removed = inativos.delete() |
||||
|
print( |
||||
|
"\n".join( |
||||
|
[ |
||||
|
f"\t\t{value} {key} records removed." |
||||
|
for key, value in removed[1].items() |
||||
|
] |
||||
|
) |
||||
|
) |
||||
|
|
||||
|
# Tratar os usuários que têm entrada de log mas não têm Servidor atribuído |
||||
|
|
||||
|
for user in User.objects.filter(servidor=None, is_active=True).exclude( |
||||
|
logentry=None |
||||
|
): |
||||
|
nome_completo = user.get_full_name().strip().lower() or user.username |
||||
|
filter = [ |
||||
|
Q(nome_completo__icontains=to_ascii(n.strip())) |
||||
|
for n in nome_completo.split(" ") |
||||
|
] |
||||
|
try: |
||||
|
# Tenta encontrar um servidor que tenha o mesmo nome e transferir |
||||
|
# todo o log para o usuário desse servidor. |
||||
|
servidor = Servidor.objects.get(*filter) |
||||
|
if servidor.user: |
||||
|
mescla_users(user, servidor.user) |
||||
|
print( |
||||
|
f"\tUser {user.username} deleted. " |
||||
|
f"Logs to {servidor.user.username}" |
||||
|
) |
||||
|
else: |
||||
|
servidor.user = user |
||||
|
servidor.save() |
||||
|
print( |
||||
|
f"\tUser {user.username} linked to " |
||||
|
f"servidor {servidor.nome_completo}" |
||||
|
) |
||||
|
except Servidor.DoesNotExist: |
||||
|
# Realmente não possui um servidor com o mesmo nome. O que resta é |
||||
|
# desativar o usuário para que não possa fazer login |
||||
|
user.is_active = False |
||||
|
user.save() |
||||
|
print(f"\tUser {user.username} deactivated.") |
||||
|
|
||||
|
# Atualizar o campo ldap_dn dos servidores ativos do ILB |
||||
|
print("\tUpdating ldap_dn field in servidor objects...", end=" ") |
||||
|
ldap_filter = ( |
||||
|
"(&(department=*ILB*)(!(title=*Desligad*))(!(title=*inativ*)))" |
||||
|
) |
||||
|
page_control = ldap.controls.SimplePagedResultsControl( |
||||
|
True, size=1000, cookie="" |
||||
|
) |
||||
|
updated = 0 |
||||
|
|
||||
|
while True: |
||||
|
response = connect.search_ext( |
||||
|
settings.AUTH_LDAP_USER, |
||||
|
ldap.SCOPE_ONELEVEL, |
||||
|
ldap_filter, |
||||
|
serverctrls=[page_control], |
||||
|
) |
||||
|
|
||||
|
rtype, rdata, rmsgid, serverctrls = connect.result3(response) |
||||
|
decoded_data = coder.decode(rdata) |
||||
|
|
||||
|
controls = [ |
||||
|
control |
||||
|
for control in serverctrls |
||||
|
if control.controlType |
||||
|
== ldap.controls.SimplePagedResultsControl.controlType |
||||
|
] |
||||
|
if not controls: |
||||
|
raise Exception("The LDAP server ignores RFC 2696 control") |
||||
|
|
||||
|
for dn, ldap_user in decoded_data: |
||||
|
user_name = ldap_user.get("sAMAccountName", [""])[0] |
||||
|
if User.objects.filter(username=user_name).exists(): |
||||
|
user = User.objects.get(username=user_name) |
||||
|
servidor = user.servidor |
||||
|
if servidor: |
||||
|
servidor.ldap_dn = dn |
||||
|
servidor.save() |
||||
|
updated += 1 |
||||
|
|
||||
|
if not controls[0].cookie: |
||||
|
break |
||||
|
page_control.cookie = controls[0].cookie |
||||
|
|
||||
|
print(f"{updated} servidores updated.") |
||||
|
|
||||
|
# Mesclar usuários duplicados |
||||
|
print("\tJoin duplicated users ...") |
||||
|
joined = 0 |
||||
|
|
||||
|
for user in User.objects.exclude(Q(first_name="") & Q(last_name="")): |
||||
|
query = User.objects.filter( |
||||
|
first_name__iexact=user.first_name, |
||||
|
last_name__iexact=user.last_name, |
||||
|
).exclude(id=user.id) |
||||
|
if query.exists(): |
||||
|
user2 = query.get() |
||||
|
dn1 = user.servidor.ldap_dn if user.servidor else "" |
||||
|
|
||||
|
if dn1: |
||||
|
print(f"\t\t{user2} > {user}") |
||||
|
mescla_users(user_source=user2, user_target=user) |
||||
|
else: |
||||
|
print(f"\t\t{user} > {user2}") |
||||
|
mescla_users(user_source=user, user_target=user2) |
||||
|
|
||||
|
# Vincular servidores com seu distinguishedName no LDAP, se existir |
||||
|
|
||||
|
print("\tLink servidor with LDAP by distinguishedNames ... ") |
||||
|
|
||||
|
for servidor in Servidor.objects.filter(externo=False, ldap_dn=""): |
||||
|
if servidor.user: |
||||
|
rdata = connect.search_s( |
||||
|
settings.AUTH_LDAP_USER, |
||||
|
ldap.SCOPE_SUBTREE, |
||||
|
ldap.filter.filter_format( |
||||
|
"(sAMAccountName=%s)", [servidor.user.username] |
||||
|
), |
||||
|
) |
||||
|
else: |
||||
|
rdata = connect.search_s( |
||||
|
settings.AUTH_LDAP_USER, |
||||
|
ldap.SCOPE_SUBTREE, |
||||
|
ldap.filter.filter_format( |
||||
|
"(cn=%s*)", [servidor.nome_completo] |
||||
|
), |
||||
|
) |
||||
|
if rdata: |
||||
|
servidor.ldap_dn = coder.decode(rdata[0][0]) |
||||
|
servidor.save() |
||||
|
print(f"\t\t{servidor.nome_completo} > {servidor.ldap_dn}") |
||||
|
|
||||
|
manuais = [ |
||||
|
(1139, 1147), # Ricardo de Oliveira Murta |
||||
|
(1129, 274), # Adalberto Alves de Oliveira |
||||
|
(1134, 166), # Cláudio Morale |
||||
|
(1130, 108), # Janary Carvão Nunes |
||||
|
] |
||||
|
|
||||
|
for source_id, target_id in manuais: |
||||
|
servidor_source = Servidor.objects.get(id=source_id) |
||||
|
servidor_target = Servidor.objects.get(id=target_id) |
||||
|
print( |
||||
|
f"\tJoining {servidor_source.nome_completo} " |
||||
|
f"to {servidor_target.nome_completo}" |
||||
|
) |
||||
|
mescla_servidores(servidor_source, servidor_target) |
||||
|
|
||||
|
|
||||
|
class Migration(migrations.Migration): |
||||
|
dependencies = [ |
||||
|
("servidores", "0014_servidor_ldap_dn_alter_servidor_user"), |
||||
|
] |
||||
|
|
||||
|
operations = [migrations.RunPython(forwards, migrations.RunPython.noop)] |
@ -0,0 +1,207 @@ |
|||||
|
# Define algumas funções para manutenção de usuários e servidores |
||||
|
|
||||
|
import ldap |
||||
|
from django.contrib.auth.models import Group |
||||
|
from django.conf import settings |
||||
|
from django.db.models import F |
||||
|
from django.db.models.fields.reverse_related import ( |
||||
|
ForeignObjectRel, |
||||
|
ManyToOneRel, |
||||
|
ManyToManyRel, |
||||
|
) |
||||
|
from django.forms.models import model_to_dict |
||||
|
from sigi.apps.servidores.models import Servidor, Servico |
||||
|
|
||||
|
|
||||
|
def _message_out(message, verbose): |
||||
|
"""_message_out Imprime mensagem apenas em modo verboso |
||||
|
|
||||
|
Arguments: |
||||
|
message -- Mensagem |
||||
|
verbose -- True para imprimir, False para não imprimir |
||||
|
""" |
||||
|
if verbose: |
||||
|
print(message) |
||||
|
|
||||
|
|
||||
|
def mescla_users(user_source, user_target, verbose=False): |
||||
|
"""mescla_users |
||||
|
Transfere todos os dados de user_source para user_target e exclui |
||||
|
user_target e o servidor vinculado a ele. |
||||
|
|
||||
|
Arguments: |
||||
|
user_source -- Usuário a ser eliminado |
||||
|
user_target -- Usuário que receberá os dados de user_source |
||||
|
|
||||
|
Keyword Arguments: |
||||
|
verbose -- Se True, imprime mensagens de progresso (default: {False}) |
||||
|
""" |
||||
|
servidor_source = user_source.servidor |
||||
|
servidor_target = user_target.servidor |
||||
|
|
||||
|
# Transfere registros de log |
||||
|
_message_out("Transferring log entries...", verbose) |
||||
|
user_source.logentry_set.update(user=user_target) |
||||
|
|
||||
|
# Se a origem não tem servidor, só resta matá-lo agora |
||||
|
|
||||
|
if servidor_source is None: |
||||
|
user_source.delete() |
||||
|
_message_out(f"User {user_source.username} deleted!", verbose) |
||||
|
return |
||||
|
|
||||
|
# Se só a origem tem servidor |
||||
|
if servidor_source and not servidor_target: |
||||
|
# transfere o servidor inteiro e pronto |
||||
|
servidor_source.user = user_target |
||||
|
servidor_source.save() |
||||
|
_message_out( |
||||
|
f"Servidor {servidor_source.nome_completo} transferred " |
||||
|
f"to {user_target.username}", |
||||
|
verbose, |
||||
|
) |
||||
|
user_source.delete() |
||||
|
_message_out(f"User {user_source.username} deleted!", verbose) |
||||
|
return |
||||
|
|
||||
|
# Só chega aqui se ambos tiverem servidor vinculado |
||||
|
|
||||
|
_message_out("Merging servidores...", verbose) |
||||
|
|
||||
|
mescla_servidores(servidor_source, servidor_target, verbose) |
||||
|
|
||||
|
|
||||
|
def mescla_servidores(servidor_source, servidor_target, verbose=False): |
||||
|
"""mescla_servidor |
||||
|
Transfere todos os dados de servidor_source para servidor_target e exclui |
||||
|
servidor_target e o user vinculado a ele. |
||||
|
|
||||
|
|
||||
|
Arguments: |
||||
|
servidor_source -- Servidor a ser eliminado |
||||
|
servidor_target -- Servidor que receberá os dados de servidor_source |
||||
|
|
||||
|
Keyword Arguments: |
||||
|
verbose -- Se True, imprime mensagens de progresso (default: {False}) |
||||
|
""" |
||||
|
|
||||
|
user_source = servidor_source.user |
||||
|
user_target = servidor_target.user |
||||
|
|
||||
|
for field in Servidor._meta.get_fields(): |
||||
|
if not isinstance(field, ForeignObjectRel): |
||||
|
continue |
||||
|
|
||||
|
accessor = field.get_accessor_name() |
||||
|
remote_field = field.remote_field.name |
||||
|
|
||||
|
if isinstance(field, ManyToOneRel): |
||||
|
getattr(servidor_source, accessor).update( |
||||
|
**{remote_field: servidor_target} |
||||
|
) |
||||
|
_message_out( |
||||
|
f"Updating field {remote_field} in " |
||||
|
f"{field.remote_field.model._meta.verbose_name} " |
||||
|
f"to {servidor_target}", |
||||
|
verbose, |
||||
|
) |
||||
|
if isinstance(field, ManyToManyRel): |
||||
|
for obj in getattr(servidor_source, accessor).all(): |
||||
|
getattr(servidor_target, accessor).add(obj) |
||||
|
getattr(servidor_source, accessor).remove(obj) |
||||
|
_message_out( |
||||
|
f"Transferring {obj} from source {accessor} " |
||||
|
f"to {servidor_target}", |
||||
|
verbose, |
||||
|
) |
||||
|
|
||||
|
# Log dos usuários |
||||
|
if user_source and not user_target: |
||||
|
servidor_target.user = user_source |
||||
|
_message_out( |
||||
|
f"Target has no user. Transferring user {user_source.username} " |
||||
|
"from source", |
||||
|
verbose, |
||||
|
) |
||||
|
elif user_source and user_target: |
||||
|
user_source.logentry_set.update(user=user_target) |
||||
|
_message_out( |
||||
|
f"Transferring {user_source.username} logentries " |
||||
|
f"to {user_target.username}", |
||||
|
verbose, |
||||
|
) |
||||
|
user_source.delete() |
||||
|
_message_out(f"{user_source.username} deleted", verbose) |
||||
|
|
||||
|
servidor_source.delete() |
||||
|
_message_out(f"{servidor_source.nome_completo} deleted", verbose) |
||||
|
|
||||
|
|
||||
|
def user_staff_and_group(user, ldap_attrs): |
||||
|
dep = ldap_attrs.get("department", [""])[0] |
||||
|
title = ldap_attrs.get("title", [""])[0] |
||||
|
deps = dep.split("-") |
||||
|
titles = [s.strip().upper() for s in title.split("-", 1)] |
||||
|
group_names = [f"{d}-{t}" for d in deps for t in titles] |
||||
|
group_names.extend(deps) |
||||
|
group_names.extend(titles) |
||||
|
group_names.extend([dep, title.upper()]) |
||||
|
user.is_staff = "ILB" in dep |
||||
|
user.save() |
||||
|
user.groups.clear() |
||||
|
if user.is_staff: |
||||
|
# Só cria grupos para o ILB # |
||||
|
for name in group_names: |
||||
|
group, created = Group.objects.get_or_create(name=name) |
||||
|
user.groups.add(group) |
||||
|
|
||||
|
|
||||
|
def servidor_update_from_ldap(servidor, ldap_attrs, commit=True): |
||||
|
sigla_servico = ldap_attrs.get("department", [""])[0].split("-")[-1] |
||||
|
nome_cargo = ldap_attrs.get("title", [""])[0].split("-")[-1].strip() |
||||
|
nome_completo = ldap_attrs.get("name", [""])[0] |
||||
|
dn = ldap_attrs.get("distinguishedName", [""])[0] |
||||
|
cargo = f"{nome_cargo} - {sigla_servico}" |
||||
|
servico = Servico.objects.filter(sigla=sigla_servico).first() |
||||
|
|
||||
|
initial = model_to_dict(servidor) |
||||
|
|
||||
|
servidor.nome_completo = nome_completo |
||||
|
servidor.servico = servico |
||||
|
servidor.cargo = cargo |
||||
|
servidor.ldap_dn = dn |
||||
|
|
||||
|
if servico is not None and nome_cargo.lower() in [ |
||||
|
"chefe de serviço", |
||||
|
"coordenador", |
||||
|
]: |
||||
|
servidor.save() # Commit is needed to update servico instance |
||||
|
servico.responsavel = servidor |
||||
|
servico.save() |
||||
|
elif commit: |
||||
|
servidor.save() |
||||
|
|
||||
|
return ( |
||||
|
servidor_create_or_update.UNCHANGED |
||||
|
if initial == model_to_dict(servidor) |
||||
|
else servidor_create_or_update.UPDATED |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def servidor_create_or_update(ldap_attrs, commit=True): |
||||
|
dn = ldap_attrs.get("distinguishedName", [""])[0] |
||||
|
|
||||
|
if dn != "" and Servidor.objects.filter(ldap_dn=dn).exists(): |
||||
|
servidor = Servidor.objects.get(ldap_dn=dn) |
||||
|
result_code = servidor_update_from_ldap(servidor, ldap_attrs, commit) |
||||
|
else: |
||||
|
servidor = Servidor() |
||||
|
servidor_update_from_ldap(servidor, ldap_attrs, commit) |
||||
|
result_code = servidor_create_or_update.CREATED |
||||
|
|
||||
|
return (result_code, servidor) |
||||
|
|
||||
|
|
||||
|
servidor_create_or_update.UNCHANGED = "unchanged" |
||||
|
servidor_create_or_update.UPDATED = "updated" |
||||
|
servidor_create_or_update.CREATED = "created" |
Loading…
Reference in new issue