Skip to main content

Models read-only para autenticação compartilhada entre projetos Django.

Project description

📚 Guia Completo - Biblioteca Compartilhada de Autenticação

🎯 O que Esta Solução Faz?

Permite que múltiplos sistemas Django acessem os dados de autenticação e organizações diretamente do banco de dados, sem fazer requisições HTTP, mantendo a mesma interface do Django ORM que você conhece.

Vantagens

Acesso direto ao banco - Sem latência de API
Interface Django nativa - rascunho.user funciona igual ao Django normal
Read-only - Impossível modificar dados por engano
Sem duplicação - Um único banco de autenticação para todos os sistemas
Type-safe - Validações e exceções customizadas


🏗️ Arquitetura

┌─────────────────────────────────────────┐
│   Sistema de Autenticação (Principal)  │
│                                         │
│  ┌──────────────┐    ┌──────────────┐ │
│  │ Organization │    │     User     │ │
│  └──────────────┘    └──────────────┘ │
│           │                   │         │
│           └─────────┬─────────┘         │
│                     │                   │
│              ┌──────▼──────┐           │
│              │   Member    │           │
│              └─────────────┘           │
└──────────────────┬──────────────────────┘
                   │
      ┌────────────┴────────────┐
      │  Banco de Dados Auth    │
      │  (PostgreSQL/MySQL)     │
      └────────────┬────────────┘
                   │
      ┌────────────┴────────────┐
      │                         │
┌─────▼─────┐            ┌─────▼─────┐
│ Sistema A │            │ Sistema B │
│           │            │           │
│ Rascunho  │            │ Pedido    │
│  ├─ org   │            │  ├─ org   │
│  └─ user  │            │  └─ user  │
└───────────┘            └───────────┘

📦 Passo 1: Criar a Biblioteca

1.1. Estrutura de Diretórios

mkdir shared-auth-lib
cd shared-auth-lib

# Criar estrutura
mkdir shared_auth
touch setup.py README.md
touch shared_auth/__init__.py
touch shared_auth/models.py
touch shared_auth/managers.py
touch shared_auth/exceptions.py

1.2. Copiar o Código

Copie os arquivos do artifact anterior para a estrutura criada.

1.3. Instalar Localmente

# Modo desenvolvimento (changes refletem automaticamente)
pip install -e /path/to/shared-auth-lib

# Ou adicionar ao requirements.txt
echo "-e /path/to/shared-auth-lib" >> requirements.txt

🔧 Passo 2: Configurar Banco de Dados

2.1. Criar Usuário Read-Only no PostgreSQL

-- No banco do sistema de autenticação
CREATE USER readonly_user WITH PASSWORD 'senha_segura';

-- Conceder permissões de leitura
GRANT CONNECT ON DATABASE sistema_auth_db TO readonly_user;
GRANT USAGE ON SCHEMA public TO readonly_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;

-- Para tabelas futuras
ALTER DEFAULT PRIVILEGES IN SCHEMA public 
GRANT SELECT ON TABLES TO readonly_user;

-- Garantir read-only
ALTER USER readonly_user SET default_transaction_read_only = on;

2.2. Para MySQL

CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'senha_segura';
GRANT SELECT ON sistema_auth_db.* TO 'readonly_user'@'%';
FLUSH PRIVILEGES;

⚙️ Passo 3: Configurar Sistema Cliente

3.1. Settings.py

# settings.py do seu outro sistema

DATABASES = {
    'default': {
        # Banco do sistema atual
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'meu_sistema_db',
        'USER': 'meu_user',
        'PASSWORD': 'senha',
        'HOST': 'localhost',
        'PORT': '5432',
    },
    'auth_db': {
        # Banco do sistema de autenticação (READ-ONLY)
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'sistema_auth_db',
        'USER': 'readonly_user',
        'PASSWORD': 'senha_readonly',
        'HOST': 'localhost',  # ou IP do servidor de auth
        'PORT': '5432',
        'OPTIONS': {
            'options': '-c default_transaction_read_only=on'
        }
    }
}

# Router para direcionar queries
DATABASE_ROUTERS = ['myapp.routers.SharedAuthRouter']

INSTALLED_APPS = [
    # ... suas apps
    'shared_auth',  # Adicionar biblioteca
]

3.2. Database Router

# myapp/routers.py

class SharedAuthRouter:
    """
    Direciona queries dos models compartilhados para o banco correto
    """
    
    route_app_labels = {'shared_auth'}
    
    def db_for_read(self, model, **hints):
        """Direciona leituras para auth_db"""
        if model._meta.app_label in self.route_app_labels:
            return 'auth_db'
        return None
    
    def db_for_write(self, model, **hints):
        """Bloqueia escritas"""
        if model._meta.app_label in self.route_app_labels:
            return None  # Impede qualquer escrita
        return None
    
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """Bloqueia migrations"""
        if app_label in self.route_app_labels:
            return False
        return None

💻 Passo 4: Usar nos Seus Models

4.1. Model Básico

# myapp/models.py
from django.db import models

class Rascunho(models.Model):
    titulo = models.CharField(max_length=200)
    conteudo = models.TextField()
    
    # IDs de referência
    organization_id = models.IntegerField()
    user_id = models.IntegerField()
    
    created_at = models.DateTimeField(auto_now_add=True)
    
    @property
    def organization(self):
        """Acessa organização do banco de auth"""
        from shared_auth.models import SharedOrganization
        return SharedOrganization.objects.using('auth_db').get_or_fail(
            self.organization_id

🚀 Guia Prático - Mixins e Serializers Compartilhados

# models.py - UMA LINHA!
from shared_auth.mixins import OrganizationUserMixin, TimestampedMixin
from shared_auth.managers import BaseAuthManager

class Rascunho(OrganizationUserMixin, TimestampedMixin):
    titulo = models.CharField(max_length=200)
    conteudo = models.TextField()
    
    objects = BaseAuthManager()
    
# Pronto! Já tem tudo:
# - organization_id, user_id
# - properties: organization, user
# - métodos: validate_user_belongs_to_organization()
# - created_at, updated_at


# serializers.py - UMA LINHA!
from shared_auth.serializers import OrganizationUserSerializerMixin

class RascunhoSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
    class Meta:
        model = Rascunho
        fields = [
            'id', 'titulo', 'conteudo',
            'organization_name', 'organization_email',  # Já disponíveis!
            'user_email', 'user_full_name',  # Já disponíveis!
        ]
    
# Pronto! Todos os campos já funcionam!

📝 Exemplos de Uso por Cenário

1. Model que Pertence APENAS a Organização

# Exemplo: Configuração da empresa
from shared_auth.mixins import OrganizationMixin

class EmpresaConfig(OrganizationMixin):
    """Configurações específicas da organização"""
    
    tema_cor = models.CharField(max_length=7, default='#3490dc')
    logo = models.ImageField(upload_to='logos/')
    timezone = models.CharField(max_length=50, default='America/Sao_Paulo')
    
    objects = BaseAuthManager()
    
    def __str__(self):
        return f"Config de {self.organization.name}"

# Uso
config = EmpresaConfig.objects.create(
    organization_id=123,
    tema_cor='#ff0000'
)

print(config.organization.name)  # Funciona!
print(config.organization_is_active())  # Método do mixin

2. Model que Pertence APENAS a Usuário

# Exemplo: Preferências do usuário
from shared_auth.mixins import UserMixin

class UserPreferences(UserMixin):
    """Preferências pessoais do usuário"""
    
    theme = models.CharField(max_length=20, default='light')
    notifications_enabled = models.BooleanField(default=True)
    language = models.CharField(max_length=5, default='pt-BR')
    
    objects = BaseAuthManager()

# Uso
prefs = UserPreferences.objects.create(
    user_id=456,
    theme='dark'
)

print(prefs.user.email)  # Funciona!
print(prefs.user_full_name)  # Property do mixin

3. Model com Organização E Usuário (mais comum)

# Exemplo: Pedido, Rascunho, Tarefa, etc
from shared_auth.mixins import OrganizationUserMixin, TimestampedMixin

class Pedido(OrganizationUserMixin, TimestampedMixin):
    """Pedido pertence a organização e foi criado por usuário"""
    
    numero = models.CharField(max_length=20, unique=True)
    valor_total = models.DecimalField(max_digits=10, decimal_places=2)
    status = models.CharField(max_length=20, default='pending')
    
    objects = BaseAuthManager()
    
    def __str__(self):
        return f"Pedido {self.numero}"

# Uso
pedido = Pedido.objects.create(
    organization_id=123,
    user_id=456,
    numero='PED-001',
    valor_total=100.00
)

# Acessos automáticos
print(pedido.organization.name)  # Organização
print(pedido.user.email)  # Usuário que criou
print(pedido.user_full_name)  # Nome completo

# Validação automática
if pedido.validate_user_belongs_to_organization():
    print("OK - Usuário pertence à organização")

# Verificar se outro usuário pode acessar
if pedido.user_can_access(outro_user_id):
    print("Pode acessar")

🎨 Serializers - Casos de Uso

Caso 1: Serializer Básico

from shared_auth.serializers import OrganizationUserSerializerMixin

class PedidoSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
    class Meta:
        model = Pedido
        fields = [
            'id', 'numero', 'valor_total', 'status',
            # Campos automáticos do mixin:
            'organization_name',
            'organization_cnpj',
            'organization_email',
            'user_email',
            'user_full_name',
            'created_at', 'updated_at',
        ]

# Response JSON automática:
{
    "id": 1,
    "numero": "PED-001",
    "valor_total": "100.00",
    "status": "pending",
    "organization_name": "Empresa XYZ",
    "organization_cnpj": "12.345.678/0001-90",
    "organization_email": "contato@xyz.com",
    "user_email": "joao@xyz.com",
    "user_full_name": "João Silva",
    "created_at": "2025-10-01T10:00:00Z",
    "updated_at": "2025-10-01T10:00:00Z"
}

Caso 2: Serializer com Fields Customizados

from shared_auth.serializers import OrganizationUserSerializerMixin
from shared_auth.fields import OrganizationField, UserField

class PedidoDetailSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
    # Fields customizados que retornam objetos completos
    organization_full = OrganizationField(source='*')
    user_full = UserField(source='*')
    
    class Meta:
        model = Pedido
        fields = [
            'id', 'numero', 'valor_total',
            'organization_full',  # Objeto completo
            'user_full',  # Objeto completo
        ]

# Response JSON:
{
    "id": 1,
    "numero": "PED-001",
    "valor_total": "100.00",
    "organization_full": {
        "id": 123,
        "name": "Empresa XYZ",
        "fantasy_name": "XYZ Ltda",
        "cnpj": "12.345.678/0001-90",
        "email": "contato@xyz.com",
        "is_active": true
    },
    "user_full": {
        "id": 456,
        "username": "joao",
        "email": "joao@xyz.com",
        "full_name": "João Silva",
        "is_active": true
    }
}

Caso 3: Serializer Apenas com Organization

from shared_auth.serializers import OrganizationSerializerMixin

class EmpresaConfigSerializer(OrganizationSerializerMixin, serializers.ModelSerializer):
    class Meta:
        model = EmpresaConfig
        fields = [
            'id', 'tema_cor', 'logo', 'timezone',
            'organization_name',  # Do
# 📦 Guia Completo - Serializers com Objetos Aninhados

## Estrutura da Resposta JSON

### ✅ Antes (campos separados)
```json
{
  "id": 1,
  "titulo": "Meu Rascunho",
  "organization_id": 123,
  "organization_name": "Empresa XYZ",
  "organization_cnpj": "12.345.678/0001-90",
  "user_id": 456,
  "user_email": "joao@xyz.com",
  "user_full_name": "João Silva"
}

✨ Depois (objetos aninhados)

{
  "id": 1,
  "titulo": "Meu Rascunho",
  "organization": {
    "id": 123,
    "name": "Empresa XYZ",
    "cnpj": "12.345.678/0001-90",
    "email": "contato@xyz.com"
  },
  "user": {
    "id": 456,
    "email": "joao@xyz.com",
    "full_name": "João Silva"
  }
}

🎯 Cenários de Uso

1. Serializer Completo (Detail)

# serializers.py
from rest_framework import serializers
from shared_auth.serializers import OrganizationUserSerializerMixin
from .models import Rascunho

class RascunhoDetailSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
    """Serializer completo para detalhes do rascunho"""
    
    class Meta:
        model = Rascunho
        fields = [
            'id',
            'titulo',
            'conteudo',
            'organization',  # Objeto completo
            'user',  # Objeto completo
            'created_at',
            'updated_at',
        ]
        read_only_fields = ['organization', 'user', 'created_at', 'updated_at']

# views
class RascunhoViewSet(viewsets.ModelViewSet):
    queryset = Rascunho.objects.all()
    
    def get_serializer_class(self):
        if self.action == 'retrieve':
            return RascunhoDetailSerializer
        return RascunhoListSerializer

# Response GET /api/rascunhos/1/
{
    "id": 1,
    "titulo": "Meu Rascunho",
    "conteudo": "Conteúdo completo aqui...",
    "organization": {
        "id": 123,
        "name": "Empresa XYZ Ltda",
        "fantasy_name": "XYZ",
        "cnpj": "12.345.678/0001-90",
        "email": "contato@xyz.com",
        "telephone": "11-3333-4444",
        "cellphone": "11-99999-8888",
        "is_branch": false,
        "is_active": true
    },
    "user": {
        "id": 456,
        "username": "joao.silva",
        "email": "joao@xyz.com",
        "first_name": "João",
        "last_name": "Silva",
        "full_name": "João Silva",
        "is_active": true
    },
    "created_at": "2025-10-01T10:00:00Z",
    "updated_at": "2025-10-01T15:30:00Z"
}

2. Serializer Simplificado (List)

from shared_auth.serializers import OrganizationSimpleSerializerMixin, UserSimpleSerializerMixin

class RascunhoListSerializer(OrganizationSimpleSerializerMixin, UserSimpleSerializerMixin, serializers.ModelSerializer):
    """Serializer simplificado para listagens"""
    
    class Meta:
        model = Rascunho
        fields = [
            'id',
            'titulo',
            'organization',  # Simplificado
            'user',  # Simplificado
            'created_at',
        ]

# Response GET /api/rascunhos/
{
    "count": 10,
    "results": [
        {
            "id": 1,
            "titulo": "Rascunho 1",
            "organization": {
                "id": 123,
                "name": "Empresa XYZ",
                "cnpj": "12.345.678/0001-90"
            },
            "user": {
                "id": 456,
                "email": "joao@xyz.com",
                "full_name": "João Silva"
            },
            "created_at": "2025-10-01T10:00:00Z"
        },
        // ... mais resultados
    ]
}

3. Apenas Organization

from shared_auth.serializers import OrganizationSerializerMixin

class EmpresaConfigSerializer(OrganizationSerializerMixin, serializers.ModelSerializer):
    """Configurações da empresa"""
    
    class Meta:
        model = EmpresaConfig
        fields = [
            'id',
            'tema_cor',
            'logo',
            'timezone',
            'organization',  # Objeto completo
        ]

# Response
{
    "id": 1,
    "tema_cor": "#3490dc",
    "logo": "https://example.com/logos/xyz.png",
    "timezone": "America/Sao_Paulo",
    "organization": {
        "id": 123,
        "name": "Empresa XYZ Ltda",
        "fantasy_name": "XYZ",
        "cnpj": "12.345.678/0001-90",
        "email": "contato@xyz.com",
        "telephone": "11-3333-4444",
        "cellphone": "11-99999-8888",
        "is_branch": false,
        "is_active": true
    }
}

4. Apenas User

from shared_auth.serializers import UserSerializerMixin

class UserPreferencesSerializer(UserSerializerMixin, serializers.ModelSerializer):
    """Preferências do usuário"""
    
    class Meta:
        model = UserPreferences
        fields = [
            'id',
            'theme',
            'notifications_enabled',
            'language',
            'user',  # Objeto completo
        ]

# Response
{
    "id": 1,
    "theme": "dark",
    "notifications_enabled": true,
    "language": "pt-BR",
    "user": {
        "id": 456,
        "username": "joao.silva",
        "email": "joao@xyz.com",
        "first_name": "João",
        "last_name": "Silva",
        "full_name": "João Silva",
        "is_active": true
    }
}

🔧 Customização Avançada

Adicionar Campos Extras ao Organization

from shared_auth.serializers import OrganizationUserSerializerMixin

class RascunhoSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
    # Sobrescrever o método para adicionar campos extras
    def get_organization(self, obj):
        org_data = super().get_organization(obj)
        if org_data:
            # Adicionar campos customizados
            org_data['logo_url'] = f"/logos/{obj.organization_id}.png"
            org_data['member_count'] = obj.organization.members.count()
        return org_data
    
    class Meta:
        model = Rascunho
        fields = ['id', 'titulo', 'organization', 'user']

# Response
{
    "id": 1,
    "titulo": "Teste",
    "organization": {
        "id": 123,
        "name": "Empresa XYZ",
        // ... campos padrão
        "logo_url": "/logos/123.png",
        "member_count": 15
    },
    "user": { ... }
}

Condicional - Mostrar Mais ou Menos Dados

class RascunhoSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
    
    def get_organization(self, obj):
        # Usuário logado é da mesma organização?
        request = self.context.get('request')
        same_org = (request and 
                   hasattr(request.user, 'logged_organization_id') and
                   request.user.logged_organization_id == obj.organization_id)
        
        try:
            org = obj.organization
            data = {
                'id': org.pk,
                'name': org.name,
            }
            
            # Mostrar mais dados se for da mesma organização
            if same_org:
                data.update({
                    'cnpj': org.cnpj,
                    'email': org.email,
                    'telephone': org.telephone,
                })
            
            return data
        except:
            return None
    
    class Meta:
        model = Rascunho
        fields = ['id', 'titulo', 'organization']

🚀 Performance - Otimização

Problema N+1

# RUIM: Causa N+1 queries
class RascunhoViewSet(viewsets.ModelViewSet):
    queryset = Rascunho.objects.all()  # 1 query
    serializer_class = RascunhoSerializer
    
    # Cada serialização faz 2 queries (org + user)
    # Total: 1 + (N * 2) queries

Solução: Prefetch

# BOM: Usa prefetch do manager
class RascunhoViewSet(viewsets.ModelViewSet):
    serializer_class = RascunhoSerializer
    
    def get_queryset(self):
        # Usa o manager customizado
        return Rascunho.objects.with_auth_data()
        # Total: 3 queries (rascunhos + orgs + users)

# Ainda MELHOR: Cache no serializer
class RascunhoSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
    
    def get_organization(self, obj):
        # Verifica se já está cacheado
        if hasattr(obj, '_cached_organization'):
            org = obj._cached_organization
        else:
            org = obj.organization
        
        return {
            'id': org.pk,
            'name': org.name,
            // ...
        }

💡 Casos de Uso Reais

Sistema de Pedidos

class ItemPedidoSerializer(serializers.ModelSerializer):
    class Meta:
        model = ItemPedido
        fields = ['id', 'produto', 'quantidade', 'valor_unitario']

class PedidoSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
    itens = ItemPedidoSerializer(many=True, read_only=True)
    
    class Meta:
        model = Pedido
        fields = [
            'id', 'numero', 'valor_total', 'status',
            'organization',  # Cliente
            'user',  # Vendedor
            'itens',
            'created_at'
        ]

# Response
{
    "id": 1,
    "numero": "PED-001",
    "valor_total": "1500.00",
    "status": "pending",
    "organization": {
        "id": 123,
        "name": "Cliente ABC",
        "cnpj": "12.345.678/0001-90",
        "email": "contato@abc.com"
    },
    "user": {
        "id": 456,
        "email": "vendedor@empresa.com",
        "full_name": "João Vendedor"
    },
    "itens": [
        {
            "id": 1,
            "produto": "Produto A",
            "quantidade": 2,
            "valor_unitario": "500.00"
        },
        {
            "id": 2,
            "produto": "Produto B",
            "quantidade": 1,
            "valor_unitario": "500.00"
        }
    ],
    "created_at": "2025-10-01T10:00:00Z"
}

Sistema de Tarefas com Múltiplos Usuários

class TarefaSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
    # user do mixin é o criador
    responsavel = serializers.SerializerMethodField()
    
    def get_responsavel(self, obj):
        """Retorna usuário responsável"""
        try:
            resp = obj.responsavel
            return {
                'id': resp.pk,
                'email': resp.email,
                'full_name': resp.get_full_name(),
            }
        except:
            return None
    
    class Meta:
        model = Tarefa
        fields = [
            'id', 'titulo', 'descricao', 'status', 'prioridade',
            'organization',  # Organização dona da tarefa
            'user',  # Quem criou
            'responsavel',  # Quem vai fazer
            'created_at'
        ]

# Response
{
    "id": 1,
    "titulo": "Implementar feature X",
    "descricao": "Descrição detalhada...",
    "status": "in_progress",
    "prioridade": "high",
    "organization": {
        "id": 123,
        "name": "Empresa XYZ"
    },
    "user": {
        "id": 456,
        "email": "gerente@xyz.com",
        "full_name": "Gerente Silva"
    },
    "responsavel": {
        "id": 789,
        "email": "dev@xyz.com",
        "full_name": "Dev Junior"
    },
    "created_at": "2025-10-01T10:00:00Z"
}

🎨 Frontend - Consumindo a API

React Example

// components/RascunhoCard.jsx
function RascunhoCard({ rascunho }) {
  return (
    <div className="card">
      <h3>{rascunho.titulo}</h3>
      <p>{rascunho.conteudo}</p>
      
      {/* Acessar dados aninhados */}
      <div className="meta">
        <span className="organization">
          {rascunho.organization.name}
        </span>
        <span className="user">
          Por: {rascunho.user.full_name}
        </span>
      </div>
      
      {/* Verificar status */}
      {!rascunho.organization.is_active && (
        <div className="warning">
          Organização inativa
        </div>
      )}
    </div>
  );
}

// Uso
fetch('/api/rascunhos/1/')
  .then(res => res.json())
  .then(data => {
    console.log(data.organization.name);  // Fácil!
    console.log(data.user.email);  // Direto!
  });

Vue Example

<template>
  <div class="rascunho">
    <h3>{{ rascunho.titulo }}</h3>
    
    <!-- Dados da organização -->
    <div class="organization-info">
      <h4>{{ rascunho.organization.name }}</h4>
      <p>{{ rascunho.organization.cnpj }}</p>
      <p>{{ rascunho.organization.email }}</p>
    </div>
    
    <!-- Dados do usuário -->
    <div class="user-info">
      <span>Criado por: {{ rascunho.user.full_name }}</span>
      <span>Email: {{ rascunho.user.email }}</span>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      rascunho: null
    }
  },
  async mounted() {
    const response = await fetch('/api/rascunhos/1/');
    this.rascunho = await response.json();
  }
}
</script>

✅ Vantagens desta Abordagem

  1. Mais Semântico: Dados relacionados agrupados
  2. Fácil de Consumir: Frontend acessa obj.organization.name diretamente
  3. Flexível: Pode ter versão completa e simplificada
  4. Type-Safe: TypeScript/Flow adoram objetos aninhados
  5. Cacheable: Pode cachear o objeto organization inteiro
  6. Reutilizável: Mesma estrutura em todos os endpoints

📋 Checklist de Implementação

  • Atualizar shared_auth/serializers.py com novos mixins
  • Reinstalar biblioteca: pip install -e /path/to/shared-auth-lib
  • Atualizar serializers existentes
  • Testar responses dos endpoints
  • Atualizar documentação da API
  • Atualizar código do frontend
  • Verificar performance (N+1 queries)
  • Adicionar testes

Pronto! Agora seus serializers retornam dados organizados em objetos organization e user! 🎉

Project details


Release history Release notifications | RSS feed

This version

0.1.9

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

maquinaweb_shared_auth-0.1.9.tar.gz (18.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

maquinaweb_shared_auth-0.1.9-py3-none-any.whl (21.4 kB view details)

Uploaded Python 3

File details

Details for the file maquinaweb_shared_auth-0.1.9.tar.gz.

File metadata

File hashes

Hashes for maquinaweb_shared_auth-0.1.9.tar.gz
Algorithm Hash digest
SHA256 b2204582f3f53269c2fe980801f895d04fa3c49c6bfeb4a32c58be314870a049
MD5 4fe98649c0faeca0a55441d3b6a7f152
BLAKE2b-256 79b9edcb622cb16efb35365b7062c9695ad2b1fbb38419b4aef3b4721387c22d

See more details on using hashes here.

File details

Details for the file maquinaweb_shared_auth-0.1.9-py3-none-any.whl.

File metadata

File hashes

Hashes for maquinaweb_shared_auth-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 4a8c9f563787999795edac51506a27966e23c0829de54f19c135048d021a1438
MD5 f7156aae7966f875510b2b456ca4e1a3
BLAKE2b-256 9c151f56e3fc2dee44a0317e2cdde192270c9f12078967eafc37b700cb3aa30d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page