Monday, December 20, 2010

WinAPI::getTempFilename() im .NET Business Connector auf einer x64 Maschine

Ein Aufruf auf die externe Methode GetTempFileNameW auf Kernel32 verursacht eine Ausnahme, wenn sie im .NET Business Connector verwendet wird und dieser auf einem x64-System ausgeführt wird.

Das Problem betrifft zumindest AX 2009 (vielleicht AX 4.0 und AX 3.0 ebenfalls).

Als Workaround kann man folgenden Code verwenden:

fileName = System.IO.Path::GetTempFileName(); // returns a filename in the temporary directory

oder

fileName = CLRInterop::getAnytypeForObject(System.IO.Path::GetTempPath() )+ curuserid() + int642str(CLRInterop::getAnyTypeForObject(netTime.get_Ticks());


Egal wie man es löst, natürlich sollte man eine temporäre Datei nach Verwendung auch immer wieder löschen.

Friday, December 10, 2010

RLS in display methods on reports (AX 4.0)

X++ select statements do ignore Record Level Security (RLS) by default. With the property  recordLevelSecurity the behaviour to use Record Level Security can be modified. The property can also be used to retrieve the current behaviour.

But there is a difference to select statements inside display methods. Every select statement has activated Record Level Security if the command is in a scope inside a call stack with a display method root. But the current status is not really available trough the property recordLevelsSecurity. A call to this propery will return false, make you thinking Record Level Security is off - but it is not.

display showEmplName()
{
    EmplTable emplTable;
    EmplName name;
    ;
    info(strfmt("%1", emplTable.recordLevelSecurity())); // this will return false, but actually it's true!

    select firstonly Name from emplTable 
        where emplTable.EmplId == reportTable.emplId; // the record will use RLS (yeah, it's magic)

name = emplTable.Name;

    return name;
}

Even here the Record Level Security is on, without explicit change to recordLevelSecurity property:
display showEmplName()
{
    EmplName name;
    ;
    name = EmplTable::find(reportTable.EmplId); // the select inside the find method will use RLS even you haven't activate this

    return name;
}


If you want to ensure, that Record Level Security is really off, you need to touch the property explicit.
display showEmplName()
{
    EmplTable emplTable;
    EmplName name;
    ;
    emplTable.recordLevelSecurity(false); // this will really free from RLS   

    select firstonly Name from emplTable 
        where emplTable.EmplId == reportTable.emplId;

    name = emplTable.Name;

    return name;
}


This Problem appears in AX 4.0. In AX 2009 this phenomen does not exists; Record Level Security stays off until you change it explicit.

RLS in Displaymethoden auf Berichten (AX 4.0)

X++ Select-Statements sind per Standard so eingestellt, dass sie die Sicherheit auf Datensatzebene (RLS) ignorieren. Über die Eigenschaft recordLevelSecurity kann aber dieses Feature ein- oder ausgeschaltet werden. Die Eigenschaft kann aber auch dazu verwendet werden, den aktuellen Status des Features zu ermitteln.

Select-Anweisungen innerhalb von Display-Methoden verhalten sich jedoch ein wenig anders. Und zwar ist für jede Select-Anweisung die Sicherheit auf Datensatzebene aktiviert, welche aus dem Aufrufstapel einer Display-Methode stammt. Allerdings ist über die Abfrage der Eigenschaft recordLevelSecurity nicht ermittelbar ob das Feature nun aktiviert ist oder nicht. Ein Aufruf von recordLevelSecurity gibt in einer solchen Situation stets false zurück was vermeintlicherweise zu der Annahme führen könnte, dass die Sicherheit auf Datensatzebene nicht aktiv ist obwohl sie das ist.

display showEmplName()
{
    EmplTable emplTable;
    EmplName name;
    ;
    info(strfmt("%1", emplTable.recordLevelSecurity())); // this will return false, but actually it's true!

    select firstonly Name from emplTable 
        where emplTable.EmplId == reportTable.emplId; // the record will use RLS (yeah, it's magic)

name = emplTable.Name;

    return name;
}

Auch so ist die Sicherheit auf Datensatzebene aktiv - ohne dass eine explizite Änderung der Eigschaft recordLevelSecurity erfolgt:
display showEmplName()
{
    EmplName name;
    ;
    name = EmplTable::find(reportTable.EmplId); // the select inside the find method will use RLS even you haven't activate this

    return name;
}


Will man nun sicherstellen, dass die Sicherheit auf Datensatzebene wirklich deaktiviert ist, muss man explizit die Eigenschaft nocheinmal setzen.

display showEmplName()
{
    EmplTable emplTable;
    EmplName name;
    ;
    emplTable.recordLevelSecurity(false); // this will really free from RLS   

    select firstonly Name from emplTable 
        where emplTable.EmplId == reportTable.emplId;

    name = emplTable.Name;

    return name;
}


Dieses Problem betrifft AX 4.0. In AX 2009 tritt das Phänomen nicht mehr auf; die Sicherheit auf Datensatzebene bleibt deaktiviert bis sie explizit aktiviert wird.

Benutzergruppe lässt sich nicht löschen nach Migration von AX 3.0 nach AX 2009

Manchmal kann es vorkommen, dass sich von AX 3.0 migrierte Benutzergruppen nicht löschen lassen. Folgender Effekt tritt ein: Man löscht die Benutzergruppe und die Zeile verschwindet, öffnet man das Formular allerdings erneut, wird die Benutzergruppe wieder angezeigt.

Das passiert, wenn der Konfigurationsschlüssel für Reporting Services aktiviert ist, aber keine Reporting Services Server angegeben und eingerichtet wurden.

Mit einer kleinen Anpassung in AOT/Forms/SysUserGroupInfo/Data Sources/User Group Info/delete kann dem Problem abgeholfen werden.

public void delete()
{
    userGroupId groupID;
    SysSRSTablePermissions permissionsTable;
    AifEndpointUser aifEndpointUser;
    SysSecurityFormTable    sysSecurityFormTable;
    ;

    groupID = userGroupInfo.Id;

    ttsbegin;

    delete_from aifEndpointUser
        where aifEndpointUser.UserId == groupID
        && aifEndpointUser.AxaptaUserType == AifAxaptaUserType::UserGroup;

    delete_from sysSecurityFormTable
        where sysSecurityFormTable.UserGroupId == groupID;

    super();

    if (isConfigurationkeyEnabled(configurationKeyName2Id('ReportingServices')))
    {
        //Now that the group is deleted, we can synchronize all the secure views that
        //include RLS criteria for this group. We need to keep the record for this
        //group in SRSTablePermissions for now because they are used to determine which
        //tables have permissions assigned to them for the specified group.
        //Once all views have been synchronized, then we can delete those records.

        if (SRSSecureViewManagerProxy::syncWithGroup(groupID) != 0)
        {
            // modificaton begin
            if ((select * from SRSServers).RecId)
            {
                ttsabort;
                return;
            }
            /*else
            {
                just fall trough, there is no need to abort, since there is no Reporting Services Servers located
            }*/
            // modification end
        }

        //Delete all records for the group from the SRSTablePermissions table.
        delete_from permissionsTable where permissionsTable.GroupId == groupID;
    }

    ttscommit;
}

Nun können auch alte migrierte Benutzergruppen gelöscht werden.