miércoles, 31 de octubre de 2007

Consultas contra el Directorio Activo desde SQL Server

grHola, ahora el "chicharron" es que el cliente quiere mantener una tabla de la base de datos de Sql Server 2005 Express, actualizada con la informacion de los usuarios del directorio activo, y pues tecnicamente, esto puede ser hecho directamente con queryes sobre el directorio activo, gracias al uso del bondadoso protocolo LDAP (Lightweight Directory Access Protocol o Protocolo de acceso liviano al Directorio), este protocolo de capa 7 del modelo OSI nos permite a nivel de aplicacion acceder a la informacion de la base de datos del directorio activo y para ello se requiere conocer una cuenta que pertenezca al dominio y obviamente el nombre o direccion IP del controlador de dominio o nombre o IP del servidor replica del directorio activo.

Sera que se pueden consultar los datos del directorio activo desde SQL Server y de la misma forma como se hacen queries estandar?, La respuesta es si y acontinuacion les dire como.
Para ello, debemos crear y configurar un servidor enlazado (Linked Server) cuya fuente de datos apuntara hacia el Directorio activo utilizando el proveedor ADsDSObject, y luego impersonamos el acceso al servidor con cualquier cuenta que pertenezca al directorio activo

1. Cree el procedimiento almacenado con el siguiente script:


-- Procedimiento para crear/borrar un servidor enlazado hacia el directorio activo
-- autor: eider mauricio aristizabal erazo
-- Para que las consultas LDAP funcionen:
-- El servidor de Sql Server debe estar instalado en un pc que pertenezca al dominio
-- Ejemplo de consulta en el directorio activo (Una vez se haya creado el servidor enlazado)
-- SELECT * FROM OpenQuery(ENLACE_DA, 'SELECT * FROM ''LDAP://IP.CONTROLADOR.DE.DOMINIO'' ')

CREATE PROCEDURE [dbo].[USP_ENLAZAR_DIRECTORIO_ACTIVO]
AS
BEGIN

IF ((SELECT COUNT(*) FROM MASTER.DBO.SYSSERVERS WHERE SRVNAME = 'ENLACE_DA') > 0)
BEGIN
EXEC sp_dropserver 'ENLACE_DA', 'droplogins'
SELECT 'EL SERVIDOR [ENLACE_DA] HA SIDO ELIMINADO'
END
ELSE
BEGIN
EXEC sp_addlinkedserver
@server = 'ENLACE_DA',
@provider = 'ADsDSOObject',
@srvproduct = 'ADsDSOObject',
@datasrc ='ADsDSOObject'

EXEC sp_addlinkedsrvlogin
'ENLACE_DA',
'false',
null,
null,
null

SELECT 'EL SERVIDOR ENLAZADO [ENLACE_DA] HA SIDO CREADO'
END
END


2. Ejecute el procedimiento almacenado que acaba de crear (Asegúrese que el resultado el procedimiento retorne el mensaje 'EL SERVIDOR ENLAZADO [ENLACE_DA] HA SIDO CREADO'):


USE [BASE_DE_DATOS]
GO

DECLARE @return_value int

EXEC
@return_value = [dbo].[USP_ENLAZAR_DIRECTORIO_ACTIVO]

SELECT
'Return Value' = @return_value
GO


3. Impersonalizar el acceso al servidor:
En el Sql Server Management Studio, navegue hasta la ubicacion, Server Objects/Linked Servers y dele clic derecho propiedades a ENLACE_DA, aparece la ventana Linked server properties - ENLACE_DA seleccione la pestaña Security y escoja la opcion Be Made using this security context, escribe un usuario del dominio bajo la sintaxis midiominio\usuario_del_dominio
Tambien se puede impersonar con el comando:


USE [BASE_DE_DATOS]
GO
EXEC
sp_addlinkedsrvlogin @rmtsrvname = N'ENLACE_DA', @locallogin = NULL , @useself = N'False', @rmtuser = N'dominio\usuario', @rmtpassword = N'password del usuario'


4. Haga una consulta del Directorio Activo:

--Primer Ejemplo
SELECT
*
FROM
OpenQuery(
ENLACE_DA,
'SELECT * FROM ''LDAP://IP.CONTROLADOR.DE.DOMINIO'''
)

--Segundo Ejemplo
SELECT
sAMAccountName, cn, displayName, mail, info, department, homephone, manager
FROM
OpenQuery(
ENLACE_DA,
'SELECT
info, sAMAccountName, cn, displayName, manager, homephone, department, company, title, samaccounttype, mail FROM ''LDAP://IP.CONTROLADOR.DE.DOMINIO'''
)


Finalmente, para ver una lista completa de los campos validos del directorio activo abra la linea de comandos y ejecute el comando:
CSVDE -f DatosDelDominio.csv

abra el archivo utilizando Microsoft Excel y sitúese sobre la primera fila, note que ésta contiene todos los campos válidos que se pueden utilizar en las consultas LDAP contra el directorio activo.
En mi caso aparecen 293 campos, genial si o que?


accountExpires

adminCount

appSchemaVersion

auditingPolicy

badPasswordTime

badPwdCount

c

cn

co

codePage

cOMClassID

comment

company

countryCode

creationTime

dc

deliveryMechanism

department

description

directReports

displayName

distinguishedName

DN

dNSHostName

dNSProperty

dnsRecord

dNSTombstoned

driverName

driverVersion

dSCorePropagationData

extensionName

flags

forceLogoff

frsComputerReference

frsComputerReferenceBL

fRSFileFilter

fRSMemberReference

fRSMemberReferenceBL

fRSPrimaryMember

fRSReplicaSetGUID

fRSReplicaSetType

fRSRootPath

fRSStagingPath

fRSVersionGUID

fRSWorkingPath

fSMORoleOwner

givenName

gPCFileSysPath

gPCFunctionalityVersion

gPCMachineExtensionNames

gPCUserExtensionNames

gPLink

gPOptions

groupType

homeMDB

homeMTA

homePhone

info

initials

installUiLevel

instanceType

internetEncoding

ipsecData

ipsecDataType

ipsecFilterReference

ipsecID

ipsecISAKMPReference

ipsecName

iPSECNegotiationPolicyAction

ipsecNegotiationPolicyReference

iPSECNegotiationPolicyType

ipsecNFAReference

ipsecOwnersReference

isCriticalSystemObject

keywords

l

lastLogoff

lastLogon

lastLogonTimestamp

lastSetTime

lastUpdateSequence

legacyExchangeDN

localeID

localPolicyFlags

location

lockoutDuration

lockOutObservationWindow

lockoutThreshold

logonCount

logonHours

machineArchitecture

mail

mailNickname

managedBy

managedObjects

manager

mAPIRecipient

masteredBy

maxPwdAge

mDBUseDefaults

member

memberOf

middleName

minPwdAge

minPwdLength

mobile

modifiedCount

modifiedCountAtLastProm

msDS-AllowedToDelegateTo

msDS-AllUsersTrustQuota

msDS-Behavior-Version

mS-DS-CreatorSID

ms-DS-MachineAccountQuota

msDs-masteredBy

msDS-PerUserTrustQuota

msDS-PerUserTrustTombstonesQuota

msDS-TombstoneQuotaFactor

msExchALObjectVersion

msExchExpansionServerName

msExchHideFromAddressLists

msExchHomeServerName

msExchMailboxGuid

msExchMailboxSecurityDescriptor

msExchMasterAccountSid

msExchMobileMailboxFlags

msExchPFTreeType

msExchPoliciesIncluded

msExchRecipientDisplayType

msExchRecipientTypeDetails

msExchRequireAuthToSendTo

msExchUserAccountControl

msExchUserCulture

msExchVersion

msieee80211-Data

msieee80211-DataType

msieee80211-ID

msiFileList

msiScriptName

msiScriptPath

mSMQDependentClientServices

mSMQDigests

mSMQDsServices

mSMQEncryptKey

mSMQOSType

mSMQRoutingServices

mSMQServiceType

mSMQSignCertificates

mSMQSignKey

mSMQSites

msNPAllowDialin

msRTCSIP-ArchiveDefault

msRTCSIP-ArchiveDefaultFlags

msRTCSIP-ArchiveFederationDefault

msRTCSIP-ArchiveFederationDefaultFlags

msRTCSIP-BackEndServer

msRTCSIP-DefPresenceSubscriptionTimeout

msRTCSIP-DefRegistrationTimeout

msRTCSIP-DefRoamingDataSubscriptionTimeout

msRTCSIP-DomainName

msRTCSIP-EnableBestEffortNotify

msRTCSIP-EnableFederation

msRTCSIP-EnterpriseServices

msRTCSIP-FederationEnabled

msRTCSIP-FrontEndServers

msRTCSIP-GlobalSettingsData

msRTCSIP-InternetAccessEnabled

msRTCSIP-MaxNumOutstandingSearchPerServer

msRTCSIP-MaxNumSubscriptionsPerUser

msRTCSIP-MaxPresenceSubscriptionTimeout

msRTCSIP-MaxRegistrationTimeout

msRTCSIP-MaxRoamingDataSubscriptionTimeout

msRTCSIP-MinPresenceSubscriptionTimeout

msRTCSIP-MinRegistrationTimeout

msRTCSIP-MinRoamingDataSubscriptionTimeout

msRTCSIP-NumDevicesPerUser

msRTCSIP-OptionFlags

msRTCSIP-PoolAddress

msRTCSIP-PoolDisplayName

msRTCSIP-PoolType

msRTCSIP-PoolVersion

msRTCSIP-PrimaryHomeServer

msRTCSIP-PrimaryUserAddress

msRTCSIP-SearchMaxRequests

msRTCSIP-SearchMaxResults

msRTCSIP-TrustedServerFQDN

msRTCSIP-TrustedServerVersion

msRTCSIP-UserEnabled

name

nextRid

nTMixedDomain

objectCategory

objectClass

objectGUID

objectSid

objectVersion

operatingSystem

operatingSystemServicePack

operatingSystemVersion

otherHomePhone

otherMailbox

ou

packageFlags

packageName

packageType

pager

physicalDeliveryOfficeName

portName

primaryGroupID

printBinNames

printCollate

printColor

printDuplexSupported

printEndTime

printerName

printKeepPrintedJobs

printLanguage

printMaxResolutionSupported

printMaxXExtent

printMaxYExtent

printMediaReady

printMediaSupported

printMemory

printMinXExtent

printMinYExtent

printOrientationsSupported

printPagesPerMinute

printRate

printRateUnit

printShareName

printSpooling

printStaplingSupported

printStartTime

priority

priorSetTime

productCode

protocolSettings

proxyAddresses

publicDelegates

publicDelegatesBL

pwdHistoryLength

pwdLastSet

pwdProperties

replUpToDateVector

reportToOriginator

repsFrom

revision

rIDAllocationPool

rIDAvailablePool

rIDManagerReference

rIDNextRID

rIDPreviousAllocationPool

rIDSetReferences

rIDUsedPool

sAMAccountName

sAMAccountType

serverName

serverReference

serverReferenceBL

serverState

serviceBindingInformation

serviceClassName

serviceDNSName

serviceDNSNameType

servicePrincipalName

shortServerName

showInAddressBook

showInAdvancedViewOnly

sn

st

streetAddress

subRefs

systemFlags

targetAddress

telephoneNumber

textEncodedORAddress

title

uASCompat

uNCName

upgradeProductCode

url

userAccountControl

userCertificate

userParameters

userPrincipalName

uSNChanged

uSNCreated

versionNumber

versionNumberHi

versionNumberLo

wellKnownObjects

whenChanged

whenCreated

wWWHomePage




Es todo, asi de facil!....




Salu2 desde Cali, Colombia

viernes, 26 de octubre de 2007

Un código en JavaScript para impedir que una caja de texto tenga espacios al principio y al final

Hola

Esta mañana me salió la niña de calidad con que no quería ver espacios ni delante ni detrás del texto que se escribe en un TextBox de la página web para la cual estoy trabajando, después de analizar el caso esta fue la solución que implementé y probé en Internet Explorer 7.0


<
script
language="javascript"
type="text/javascript">



function textoSinEspacios(cadena, evt)

{

if(evt.keyCode == 32 && cadena.length==0) return
false;


if(evt.keyCode == 32 && cadena.length > 2 && cadena.charAt(cadena.length-1) == ' ') return
false;


return
true;

}

</script>


 

¿Cómo aplicarla?

Esto lo apliqué en ASP.net pero tambien funciona sobre controles HTML estándar y para ello agrégale estos atributos a la caja de texto:

onKeyDown="return textoSinEspacios(this.value, event)" OnContextMenu="return false;"

así quedaria para un webcontrol en asp.net:
<
asp:TextBox
ID="txtNombre"
runat="server"
onKeyDown="return textoSinEspacios(this.value, event)"


OnContextMenu="return false;"></asp:TextBox>


 


 

¿Cómo funciona?


Para evitar espacios vacios al principio:

Teniendo en cuenta que el codigo ASCII del espacio es el 32 se puede validar que cuando la lonjitud de la cadena que el usuario escribe en la caja de texto sea cero y si el caracter que acaba de presionar es 32 entonces que retorne un false.

Para evitar espacios vacios al final:
Si la lonjitud de la cadena es mayor que dos y el penultimo carácter es un espacio entonces que retorne false de lo contrario siempre retorna true.

miércoles, 3 de octubre de 2007

Un query que funciona en ORACLE


 


 


 

Modelo de datos: Una persona puede tener una o muchas mascotas.


 

CREATE TABLE PERSONA(


ID NUMBER NOT NULL PRIMARY KEY,


NOMBRE VARCHAR2(100)

)


 

CREATE TABLE MASCOTA (


ID NUMBER NOT NULL PRIMARY KEY,


ID_PERSONA NUMBER REFERENCES PERSONA (ID),


EDAD NUMBER,


NOMBRE VARCHAR2(100)

)


 

--Poblar la tabla Personas

INSERT INTO PERSONA(ID,NOMBRE) VALUES ( 1,'Ronal');

INSERT INTO PERSONA(ID,NOMBRE) VALUES ( 2,'Mauricio');

INSERT INTO PERSONA(ID,NOMBRE) VALUES ( 3,'Luisca');


 

--Poblar la tabla mascotas

INSERT INTO MASCOTA(ID,ID_PERSONA,EDAD,NOMBRE) VALUES ( 1, 1, 13,'osa');

INSERT INTO MASCOTA(ID,ID_PERSONA,EDAD,NOMBRE) VALUES ( 2, 1, 5,'karen');

INSERT INTO MASCOTA(ID,ID_PERSONA,EDAD,NOMBRE) VALUES ( 10, 2, 20,'Tata');

INSERT INTO MASCOTA(ID,ID_PERSONA,EDAD,NOMBRE) VALUES ( 11, 2, 7,'Conguito');

INSERT INTO MASCOTA(ID,ID_PERSONA,EDAD,NOMBRE) VALUES ( 3, 1, 2,'Sapito');

INSERT INTO MASCOTA(ID,ID_PERSONA,EDAD,NOMBRE) VALUES ( 20, 3, 3,'Piojo');

INSERT INTO MASCOTA(ID,ID_PERSONA,EDAD,NOMBRE) VALUES ( 21, 3, 3,'Pioja');

INSERT INTO MASCOTA(ID,ID_PERSONA,EDAD,NOMBRE) VALUES ( 22, 3, 3,'liendra');


 

--Ejecutar el query que saca las mascotas mas jóvenes por persona

SELECT

    P.ID AS ID_PERSONA

    ,P.NOMBRE AS NOMBRE_PERSONA

    ,M.ID AS ID_MASCOTA

    ,M.NOMBRE AS NOMBRE_MASCOTA

    ,M.EDAD AS EDAD

FROM     

    MASCOTA M

    INNER JOIN PERSONA P ON (M.ID_PERSONA = P.ID)

WHERE

    M.EDAD = (

     SELECT MIN(MH.EDAD)

     FROM     MASCOTA MH

     WHERE MH.ID_PERSONA = P.ID

    )

ORDER BY

    ID_PERSONA, ID_MASCOTA


 

--El resultado es el siguiente

ID_PERSONA

NOMBRE_PERSONA

ID_MASCOTA

NOMBRE_MASCOTA

EDAD

1

Ronal

3

Sapito

2

2

Mauricio

11

Conguito

7

3

Luisca

20

Piojo

3

3

Luisca

21

Pioja

3

3

Luisca

22

Liendra

3