@@ -23,6 +23,11 @@ const (
23
23
// ControlTypeSubtreeDelete - https://datatracker.ietf.org/doc/html/draft-armijo-ldap-treedelete-02
24
24
ControlTypeSubtreeDelete = "1.2.840.113556.1.4.805"
25
25
26
+ // ControlTypeServerSideSorting - https://www.ietf.org/rfc/rfc2891.txt
27
+ ControlTypeServerSideSorting = "1.2.840.113556.1.4.473"
28
+ // ControlTypeServerSideSorting - https://www.ietf.org/rfc/rfc2891.txt
29
+ ControlTypeServerSideSortingResult = "1.2.840.113556.1.4.474"
30
+
26
31
// ControlTypeMicrosoftNotification - https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
27
32
ControlTypeMicrosoftNotification = "1.2.840.113556.1.4.528"
28
33
// ControlTypeMicrosoftShowDeleted - https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx
@@ -33,13 +38,15 @@ const (
33
38
34
39
// ControlTypeMap maps controls to text descriptions
35
40
var ControlTypeMap = map [string ]string {
36
- ControlTypePaging : "Paging" ,
37
- ControlTypeBeheraPasswordPolicy : "Password Policy - Behera Draft" ,
38
- ControlTypeManageDsaIT : "Manage DSA IT" ,
39
- ControlTypeSubtreeDelete : "Subtree Delete Control" ,
40
- ControlTypeMicrosoftNotification : "Change Notification - Microsoft" ,
41
- ControlTypeMicrosoftShowDeleted : "Show Deleted Objects - Microsoft" ,
42
- ControlTypeMicrosoftServerLinkTTL : "Return TTL-DNs for link values with associated expiry times - Microsoft" ,
41
+ ControlTypePaging : "Paging" ,
42
+ ControlTypeBeheraPasswordPolicy : "Password Policy - Behera Draft" ,
43
+ ControlTypeManageDsaIT : "Manage DSA IT" ,
44
+ ControlTypeSubtreeDelete : "Subtree Delete Control" ,
45
+ ControlTypeMicrosoftNotification : "Change Notification - Microsoft" ,
46
+ ControlTypeMicrosoftShowDeleted : "Show Deleted Objects - Microsoft" ,
47
+ ControlTypeMicrosoftServerLinkTTL : "Return TTL-DNs for link values with associated expiry times - Microsoft" ,
48
+ ControlTypeServerSideSorting : "Server Side Sorting Request - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)" ,
49
+ ControlTypeServerSideSortingResult : "Server Side Sorting Results - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)" ,
43
50
}
44
51
45
52
// Control defines an interface controls provide to encode and describe themselves
@@ -490,6 +497,10 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
490
497
return NewControlMicrosoftServerLinkTTL (), nil
491
498
case ControlTypeSubtreeDelete :
492
499
return NewControlSubtreeDelete (), nil
500
+ case ControlTypeServerSideSorting :
501
+ return NewControlServerSideSorting (value )
502
+ case ControlTypeServerSideSortingResult :
503
+ return NewControlServerSideSortingResult (value )
493
504
default :
494
505
c := new (ControlString )
495
506
c .ControlType = ControlType
@@ -560,3 +571,189 @@ func encodeControls(controls []Control) *ber.Packet {
560
571
}
561
572
return packet
562
573
}
574
+
575
+ // ControlServerSideSorting
576
+
577
+ type SortKey struct {
578
+ Reverse bool
579
+ AttributeType string
580
+ MatchingRule string
581
+ }
582
+
583
+ type ControlServerSideSorting struct {
584
+ SortKeys []* SortKey
585
+ }
586
+
587
+ func (c * ControlServerSideSorting ) GetControlType () string {
588
+ return ControlTypeServerSideSorting
589
+ }
590
+
591
+ func NewControlServerSideSorting (value * ber.Packet ) (* ControlServerSideSorting , error ) {
592
+ sortKeys := []* SortKey {}
593
+
594
+ val := value .Children [1 ].Children
595
+
596
+ if len (val ) != 1 {
597
+ return nil , fmt .Errorf ("no sequence value in packet" )
598
+ }
599
+
600
+ sequences := val [0 ].Children
601
+
602
+ for i , sequence := range sequences {
603
+ sortKey := & SortKey {}
604
+
605
+ if len (sequence .Children ) < 2 {
606
+ return nil , fmt .Errorf ("attributeType or matchingRule is missing from sequence %d" , i )
607
+ }
608
+
609
+ sortKey .AttributeType = sequence .Children [0 ].Value .(string )
610
+ sortKey .MatchingRule = sequence .Children [1 ].Value .(string )
611
+
612
+ if len (sequence .Children ) == 3 {
613
+ sortKey .Reverse = sequence .Children [2 ].Value .(bool )
614
+ }
615
+
616
+ sortKeys = append (sortKeys , sortKey )
617
+ }
618
+
619
+ return & ControlServerSideSorting {SortKeys : sortKeys }, nil
620
+ }
621
+
622
+ func NewControlServerSideSortingWithSortKeys (sortKeys []* SortKey ) * ControlServerSideSorting {
623
+ return & ControlServerSideSorting {SortKeys : sortKeys }
624
+ }
625
+
626
+ func (c * ControlServerSideSorting ) Encode () * ber.Packet {
627
+ packet := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "Control" )
628
+ control := ber .NewString (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , c .GetControlType (), "Control Type" )
629
+
630
+ value := ber .Encode (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , nil , "Control Value" )
631
+ seqs := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "SortKeyList" )
632
+
633
+ for _ , f := range c .SortKeys {
634
+ seq := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "" )
635
+
636
+ seq .AppendChild (
637
+ ber .NewString (ber .ClassUniversal , ber .TypePrimitive , ber .TagOctetString , f .AttributeType , "attributeType" ),
638
+ )
639
+ seq .AppendChild (
640
+ ber .NewString (ber .ClassContext , ber .TypePrimitive , 0 , f .MatchingRule , "orderingRule" ),
641
+ )
642
+ if f .Reverse {
643
+ seq .AppendChild (
644
+ ber .NewBoolean (ber .ClassContext , ber .TypePrimitive , 1 , f .Reverse , "reverseOrder" ),
645
+ )
646
+ }
647
+
648
+ seqs .AppendChild (seq )
649
+ }
650
+
651
+ value .AppendChild (seqs )
652
+
653
+ packet .AppendChild (control )
654
+ packet .AppendChild (value )
655
+
656
+ return packet
657
+ }
658
+
659
+ func (c * ControlServerSideSorting ) String () string {
660
+ return fmt .Sprintf (
661
+ "Control Type: %s (%q) Criticality:%t %+v" ,
662
+ "Server Side Sorting" ,
663
+ c .GetControlType (),
664
+ false ,
665
+ c .SortKeys ,
666
+ )
667
+ }
668
+
669
+ // ControlServerSideSortingResponse
670
+
671
+ const (
672
+ ControlServerSideSortingCodeSuccess ControlServerSideSortingCode = 0
673
+ ControlServerSideSortingCodeOperationsError ControlServerSideSortingCode = 1
674
+ ControlServerSideSortingCodeTimeLimitExceeded ControlServerSideSortingCode = 2
675
+ ControlServerSideSortingCodeStrongAuthRequired ControlServerSideSortingCode = 8
676
+ ControlServerSideSortingCodeAdminLimitExceeded ControlServerSideSortingCode = 11
677
+ ControlServerSideSortingCodeNoSuchAttribute ControlServerSideSortingCode = 16
678
+ ControlServerSideSortingCodeInappropriateMatching ControlServerSideSortingCode = 18
679
+ ControlServerSideSortingCodeInsufficientAccessRights ControlServerSideSortingCode = 50
680
+ ControlServerSideSortingCodeBusy ControlServerSideSortingCode = 51
681
+ ControlServerSideSortingCodeUnwillingToPerform ControlServerSideSortingCode = 53
682
+ ControlServerSideSortingCodeOther ControlServerSideSortingCode = 80
683
+ )
684
+
685
+ var ControlServerSideSortingCodes = []ControlServerSideSortingCode {
686
+ ControlServerSideSortingCodeSuccess ,
687
+ ControlServerSideSortingCodeOperationsError ,
688
+ ControlServerSideSortingCodeTimeLimitExceeded ,
689
+ ControlServerSideSortingCodeStrongAuthRequired ,
690
+ ControlServerSideSortingCodeAdminLimitExceeded ,
691
+ ControlServerSideSortingCodeNoSuchAttribute ,
692
+ ControlServerSideSortingCodeInappropriateMatching ,
693
+ ControlServerSideSortingCodeInsufficientAccessRights ,
694
+ ControlServerSideSortingCodeBusy ,
695
+ ControlServerSideSortingCodeUnwillingToPerform ,
696
+ ControlServerSideSortingCodeOther ,
697
+ }
698
+
699
+ type ControlServerSideSortingCode int64
700
+
701
+ // Valid test the code contained in the control against the ControlServerSideSortingCodes slice and return an error if the code is unknown.
702
+ func (c ControlServerSideSortingCode ) Valid () error {
703
+ for _ , validRet := range ControlServerSideSortingCodes {
704
+ if c == validRet {
705
+ return nil
706
+ }
707
+ }
708
+ return fmt .Errorf ("unknown return code : %d" , c )
709
+ }
710
+
711
+ func NewControlServerSideSortingResult (pkt * ber.Packet ) (* ControlServerSideSortingResult , error ) {
712
+ control := & ControlServerSideSortingResult {}
713
+
714
+ if pkt == nil || len (pkt .Children ) == 0 {
715
+ return nil , fmt .Errorf ("bad packet" )
716
+ }
717
+
718
+ codeInt , err := ber .ParseInt64 (pkt .Children [0 ].Data .Bytes ())
719
+ if err != nil {
720
+ return nil , err
721
+ }
722
+
723
+ code := ControlServerSideSortingCode (codeInt )
724
+ if err := code .Valid (); err != nil {
725
+ return nil , err
726
+ }
727
+
728
+ return control , nil
729
+ }
730
+
731
+ type ControlServerSideSortingResult struct {
732
+ Criticality bool
733
+
734
+ Result ControlServerSideSortingCode
735
+
736
+ // Not populated for now. I can't get openldap to send me this value, so I think this is specific to other directory server
737
+ // AttributeType string
738
+ }
739
+
740
+ func (control * ControlServerSideSortingResult ) GetControlType () string {
741
+ return ControlTypeServerSideSortingResult
742
+ }
743
+ func (c * ControlServerSideSortingResult ) Encode () * ber.Packet {
744
+ packet := ber .Encode (ber .ClassUniversal , ber .TypeConstructed , ber .TagSequence , nil , "SortResult sequence" )
745
+ sortResult := ber .NewInteger (ber .ClassUniversal , ber .TypePrimitive , ber .TagEnumerated , int64 (c .Result ), "SortResult" )
746
+ packet .AppendChild (sortResult )
747
+
748
+ return packet
749
+ }
750
+
751
+ func (c * ControlServerSideSortingResult ) String () string {
752
+ return fmt .Sprintf (
753
+ "Control Type: %s (%q) Criticality:%t ResultCode:%+v" ,
754
+ "Server Side Sorting Result" ,
755
+ c .GetControlType (),
756
+ c .Criticality ,
757
+ c .Result ,
758
+ )
759
+ }
0 commit comments