Dear,
I'm developing a system to restrict user login times in AD, I can view login times and even change. But for example, I'm not able to establish this login time from 17:00 to midnight, the system appears to be zero, another issue is to establish the time from 00:00 to 00:00, when this done in the system in AD, it displays as denied.
I'm using as a base the classes available here: LogonTime.cs
Observe the days of Friday and Saturday, the system is showing up empty, but in AD is marked the allowed times.
I need help getting these details right.
Seeking to understand the position of bytes in the LogonHours mask, I noticed that it is shown as if it were inverted.
Is it possible to be making this spin?
Sorry. That's the downside of looking at screenshots on small devices like a phone. Now that I'm at a PC, I can see that the console output on the right is showing the bits low to high order which corresponds to how the data about logon hours is represented.
Everything is correct. The data in the bytes on the console output on the right correctly matches up with the the graphical representation on the left. The thing that you don't seem to be taking into account is that the byte data is in UTC while the graphical display is in local time. Notice how for Sunday, the first active hour is on bit 7 of the 2nd byte. That corresponds to 15:00 UTC (7 + (2-1) * 8 == 15). In your GUI, that first active hour is at 12:00. So that would imply that your local time zone is at -3 hours offset. That is completely consistent with the Tuesday that you highlighted. The first active hour is at 03:00 UTC, but your GUI is showing the active hour at 00:00 local. Since the user is on a 24 shift, notice how there are 3 active hours on Wednesday from 00:00-03:00 UTC in the console which should be 21:00-00:00 local on your GUI. Also on the Saturday, notice the leading 3 inactive hours in UTC, but it is active on in your local time.
Personally, despite the folksy easy to read style of Anlai's blogpost, it is also got some subtle errors in it. I think that this is a better blog post that explains how the data is stored:
Just wanted to share. With less than 80 lines of code here:
LogonHours.cs:
using System;using System.Collections;using System.Collections.Generic;using System.Linq;using AXFSoftware.Utilities;namespace AXFSoftware.Utilities.ActiveDirectory.LogonHours
{publicclassLogonHours{// One bit per hour over an entire weekconstint BitArraySize =24*7;publicBitArray Bits {get;}publicbyte[] Bitmask
{get{var bytes =newbyte[BitArraySize/8];
Bits.CopyTo(bytes,0);return bytes;}}publicTimeZoneInfo TimeZoneInfo {get;}publicbool IsUtc => TimeZoneInfo.Id == TimeZoneInfo.Utc.Id;
Lazy<Week> _days;publicWeek Days => _days.Value;publicboolthis[int hour]{get=> Bits[hour];set=> Bits[hour]=value;}publicLogonHours(byte[] bitmask)=>newLogonHours(newBitArray(bitmask), TimeZoneInfo.Utc);publicLogonHours(BitArray bits)=>newLogonHours(newBitArray(bits), TimeZoneInfo.Utc);LogonHours(BitArray bits,TimeZoneInfo timeZoneInfo){if(bits.Length != BitArraySize)thrownewArgumentException("Must hold exact 168 bits for 24 hours over 7 days.",nameof(bits));
Bits = bits;
TimeZoneInfo = timeZoneInfo;
_days =newLazy<Week>(()=>newWeek(this));}publicLogonHoursToTimeZone(TimeZoneInfo timeZoneInfo)=>ToTimeZone(timeZoneInfo, DateTime.Now);publicLogonHoursToTimeZone(TimeZoneInfo timeZoneInfo,DateTime dateTime){var srcOffset = TimeZoneInfo.GetUtcOffset(dateTime);var dstOffset = timeZoneInfo.GetUtcOffset(dateTime);var newOffset = dstOffset - srcOffset;int dstHour =(int)Math.Round(newOffset.TotalHours);var bits =newBitArray(Bits);if(dstHour <0)
bits.LeftRotate(-dstHour);elseif(dstHour >0)
bits.RightRotate(dstHour);returnnewLogonHours(bits, timeZoneInfo);}publicLogonHoursToUtc()=>ToUtc(DateTime.Now);publicLogonHoursToUtc(DateTime dateTime)=> IsUtc ?newLogonHours(newBitArray(Bits), TimeZoneInfo.Utc):ToTimeZone(TimeZoneInfo.Utc, dateTime);}}
and the supporting 43 lines of code here:
BitArrayExtensions.cs:
using System;using System.Collections;using System.Collections.Generic;using System.Linq;using System.Text;namespace AXFSoftware.Utilities
{publicstaticclassBitArrayExtensions{publicstaticBitArrayRightRotate(thisBitArray bits,int count){if(count <0)thrownewArgumentOutOfRangeException(nameof(count), count,"Must be greater than or equal zero");
count %= bits.Count;if(count ==0)return bits;var lowBits =(BitArray) bits.Clone();
lowBits.LeftShift(bits.Count - count);
bits.RightShift(count);return bits.Or(lowBits);}publicstaticBitArrayLeftRotate(thisBitArray bits,int count){if(count <0)thrownewArgumentOutOfRangeException(nameof(count), count,"Must be greater than or equal zero");
count %= bits.Count;if(count ==0)return bits;var highBits =(BitArray)bits.Clone();
highBits.RightShift(bits.Count - count);
bits.LeftShift(count);return bits.Or(highBits);}}}
It is already enough to be able load the AD logon hours bitmask, convert to your desired timezone, and query and set the availability hours using the indexer, and the convert back to UTC, get the appropriate bitmask to put back into AD.
For the sake convenience and just a mere 80 extra lines, these helper classes let you index into the hours by day and hour (instead of computing the hour index yourself with the above classes):
Day.cs:
using System;using System.Collections;using System.Collections.Generic;namespace AXFSoftware.Utilities.ActiveDirectory.LogonHours
{publicstruct Day : IEnumerable<bool>{readonlyint _startOfDayHour;publicLogonHours LogonHours {get;}publicTimeZoneInfo TimeZoneInfo => LogonHours.TimeZoneInfo;publicboolthis[int hour]{get{ValidateHour(hour);return LogonHours.Bits[_startOfDayHour + hour];}set{ValidateHour(hour);
LogonHours.Bits[_startOfDayHour + hour]=value;}}internalDay(LogonHours logonHours,int day){
LogonHours = logonHours;
_startOfDayHour = day *24;}voidValidateHour(int hour){if(!(0<= hour && hour <=23))thrownewIndexOutOfRangeException("Hour value must be between 0 and 23 inclusively.");}public IEnumerator<bool>GetEnumerator(){for(int hour =0; hour <24; hour++)yieldreturnthis[hour];}IEnumerator IEnumerable.GetEnumerator()=>GetEnumerator();}}
Just wanted to share. With less than 80 lines of code here:
LogonHours.cs:
using System;using System.Collections;using System.Collections.Generic;using System.Linq;using AXFSoftware.Utilities;namespace AXFSoftware.Utilities.ActiveDirectory.LogonHours
{publicclassLogonHours{// One bit per hour over an entire weekconstint BitArraySize =24*7;publicBitArray Bits {get;}publicbyte[] Bitmask
{get{var bytes =newbyte[BitArraySize/8];
Bits.CopyTo(bytes,0);return bytes;}}publicTimeZoneInfo TimeZoneInfo {get;}publicbool IsUtc => TimeZoneInfo.Id == TimeZoneInfo.Utc.Id;
Lazy<Week> _days;publicWeek Days => _days.Value;publicboolthis[int hour]{get=> Bits[hour];set=> Bits[hour]=value;}publicLogonHours(byte[] bitmask)=>newLogonHours(newBitArray(bitmask), TimeZoneInfo.Utc);publicLogonHours(BitArray bits)=>newLogonHours(newBitArray(bits), TimeZoneInfo.Utc);LogonHours(BitArray bits,TimeZoneInfo timeZoneInfo){if(bits.Length != BitArraySize)thrownewArgumentException("Must hold exact 168 bits for 24 hours over 7 days.",nameof(bits));
Bits = bits;
TimeZoneInfo = timeZoneInfo;
_days =newLazy<Week>(()=>newWeek(this));}publicLogonHoursToTimeZone(TimeZoneInfo timeZoneInfo)=>ToTimeZone(timeZoneInfo, DateTime.Now);publicLogonHoursToTimeZone(TimeZoneInfo timeZoneInfo,DateTime dateTime){var srcOffset = TimeZoneInfo.GetUtcOffset(dateTime);var dstOffset = timeZoneInfo.GetUtcOffset(dateTime);var newOffset = dstOffset - srcOffset;int dstHour =(int)Math.Round(newOffset.TotalHours);var bits =newBitArray(Bits);if(dstHour <0)
bits.LeftRotate(-dstHour);elseif(dstHour >0)
bits.RightRotate(dstHour);returnnewLogonHours(bits, timeZoneInfo);}publicLogonHoursToUtc()=>ToUtc(DateTime.Now);publicLogonHoursToUtc(DateTime dateTime)=> IsUtc ?newLogonHours(newBitArray(Bits), TimeZoneInfo.Utc):ToTimeZone(TimeZoneInfo.Utc, dateTime);}}
and the supporting 43 lines of code here:
BitArrayExtensions.cs:
using System;using System.Collections;using System.Collections.Generic;using System.Linq;using System.Text;namespace AXFSoftware.Utilities
{publicstaticclassBitArrayExtensions{publicstaticBitArrayRightRotate(thisBitArray bits,int count){if(count <0)thrownewArgumentOutOfRangeException(nameof(count), count,"Must be greater than or equal zero");
count %= bits.Count;if(count ==0)return bits;var lowBits =(BitArray) bits.Clone();
lowBits.LeftShift(bits.Count - count);
bits.RightShift(count);return bits.Or(lowBits);}publicstaticBitArrayLeftRotate(thisBitArray bits,int count){if(count <0)thrownewArgumentOutOfRangeException(nameof(count), count,"Must be greater than or equal zero");
count %= bits.Count;if(count ==0)return bits;var highBits =(BitArray)bits.Clone();
highBits.RightShift(bits.Count - count);
bits.LeftShift(count);return bits.Or(highBits);}}}
It is already enough to be able load the AD logon hours bitmask, convert to your desired timezone, and query and set the availability hours using the indexer, and the convert back to UTC, get the appropriate bitmask to put back into AD.
For the sake convenience and just a mere 80 extra lines, these helper classes let you index into the hours by day and hour (instead of computing the hour index yourself with the above classes):
Day.cs:
using System;using System.Collections;using System.Collections.Generic;namespace AXFSoftware.Utilities.ActiveDirectory.LogonHours
{publicstruct Day : IEnumerable<bool>{readonlyint _startOfDayHour;publicLogonHours LogonHours {get;}publicTimeZoneInfo TimeZoneInfo => LogonHours.TimeZoneInfo;publicboolthis[int hour]{get{ValidateHour(hour);return LogonHours.Bits[_startOfDayHour + hour];}set{ValidateHour(hour);
LogonHours.Bits[_startOfDayHour + hour]=value;}}internalDay(LogonHours logonHours,int day){
LogonHours = logonHours;
_startOfDayHour = day *24;}voidValidateHour(int hour){if(!(0<= hour && hour <=23))thrownewIndexOutOfRangeException("Hour value must be between 0 and 23 inclusively.");}public IEnumerator<bool>GetEnumerator(){for(int hour =0; hour <24; hour++)yieldreturnthis[hour];}IEnumerator IEnumerable.GetEnumerator()=>GetEnumerator();}}
Dear,
I'm developing a system to restrict user login times in AD, I can view login times and even change. But for example, I'm not able to establish this login time from 17:00 to midnight, the system appears to be zero, another issue is to establish the time from 00:00 to 00:00, when this done in the system in AD, it displays as denied.
I'm using as a base the classes available here: LogonTime.cs
Observe the days of Friday and Saturday, the system is showing up empty, but in AD is marked the allowed times.
I need help getting these details right.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.