• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / tigerdata-app / 3d88d2e1-809d-4b21-a053-6167ddb1e670

06 Nov 2025 03:55PM UTC coverage: 91.201% (+10.5%) from 80.673%
3d88d2e1-809d-4b21-a053-6167ddb1e670

push

circleci

web-flow
Fixed link, replaced image, added a test (#2158)

Closes #2156 and #2141

2840 of 3114 relevant lines covered (91.2%)

1092.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/app/services/princeton_users.rb
1
# frozen_string_literal: true
2
class PrincetonUsers
3✔
3
  CHARS_AND_NUMS =  ('a'..'z').to_a + (0..9).to_a + ['-']
3✔
4

5
  # `cbentler` and `az3007` are required because they come as data manager and data sponsor
6
  # in the default project in the Mediaflux in our Docker image.
7
  RDSS_DEVELOPERS = %w[bs3097 jrg5 cac9 hc8719 rl3667 kl37 pp9425 jh6441 cbentler az3007].freeze
3✔
8

9
  class << self
3✔
10

11

12
    # Returns a list of Users that match the given query
13
    def user_list_query(query)
3✔
14
      tokens = query.downcase.strip.split(/[^a-zA-Z\d]/).compact_blank
62✔
15
      return [] if tokens.count == 0
62✔
16
      if (tokens.count == 1)
60✔
17
        # if I have a single token I might be trying a uid search, so put all the uid matches at the top
18
        uid_query(tokens[0]) | name_query(tokens)
43✔
19
      else
20
        name_query(tokens)
17✔
21
      end.map{|user| { uid: user.uid, name: user.display_name, display_name: user.display_name_safe } }
64✔
22
    end
23

24
    def uid_query(token)
3✔
25
      order_sql = User.sanitize_sql_for_order("LENGTH(uid)-LENGTH('#{token}')")
43✔
26
      search_token = User.sanitize_sql_like(token)+'%'
43✔
27
      User.where("(uid like ?)",search_token).order(Arel.sql(order_sql)).order(:uid)
43✔
28
    end
29

30
    def name_query(tokens)
3✔
31
      tokens.inject(User.all) do |partial_query, token|
60✔
32
        search_token = '%'+User.sanitize_sql_like(token)+'%'
81✔
33
        partial_query.where("(LOWER(display_name) like ?) OR (LOWER(uid) like ?)", search_token, search_token)
81✔
34
      end.order(:given_name).order(:family_name)
35
    end
36

37
    def load_rdss_developers
3✔
38
      RDSS_DEVELOPERS.each do |netid|
4✔
39
        create_user_from_ldap_by_uid(netid)
22✔
40
        rescue TigerData::LdapError
41
        raise TigerData::LdapError, "Unable to create user from LDAP. Are you connected to VPN?"
2✔
42
      end
43
    end
44

45
    # Creates users from LDAP data, starting with the given uid prefix.
46
    def create_users_from_ldap(current_uid_start: "", ldap_connection: default_ldap_connection)
3✔
47
      CHARS_AND_NUMS.each do |char|
10✔
48
        filter =(~ Net::LDAP::Filter.eq( "pustatus", "guest" )) & Net::LDAP::Filter.eq("uid", "#{current_uid_start}#{char}*")
370✔
49
        people = ldap_connection.search(filter:, attributes: [:pudisplayname, :givenname, :sn, :uid, :edupersonprincipalname]);
370✔
50
        if ldap_connection.get_operation_result.message == "Success"
370✔
51
          people.each{|person| user_from_ldap(person)}
376✔
52
        else
53
          create_users_from_ldap(current_uid_start: "#{current_uid_start}#{char}", ldap_connection:)
2✔
54
        end
55
      end
56
    end
57

58
    def create_user_from_ldap_by_uid(uid, ldap_connection: default_ldap_connection)
3✔
59
      filter = Net::LDAP::Filter.eq('uid', uid)
20✔
60
      person = ldap_connection.search(filter:, attributes: [:pudisplayname, :givenname, :sn, :uid, :edupersonprincipalname]);
20✔
61
      raise TigerData::LdapError, "More than one user matches supplied uid: #{uid}" if person.length > 1
20✔
62
      raise TigerData::LdapError, "No user with uid #{uid} found" if person.empty?
20✔
63
      user_from_ldap(person.first)
20✔
64
    end
65

66
  # Creates or updates a User from an LDAP entry.
67
  # @param ldap_person [Net::LDAP::Entry] an LDAP entry representing a person
68
  # @return [User, nil] the created or updated User, or nil if the LDAP entry is missing a edupersonprincipalname
69
    def user_from_ldap(ldap_person)
3✔
70
      return if check_for_malformed_ldap_entries(ldap_person)
36✔
71
      uid = ldap_person[:uid].first.downcase
30✔
72
      current_entries = User.where(uid:)
30✔
73
      if current_entries.empty?
30✔
74
        User.create(uid: , display_name: ldap_person[:pudisplayname].first,
28✔
75
                    family_name: ldap_person[:sn].first, given_name: ldap_person[:givenname].first,
76
                    email: ldap_person[:edupersonprincipalname].first, provider: "cas")
77
      else
78
        user = current_entries.first
2✔
79
        if user.display_name.blank?
2✔
80
          user.display_name = ldap_person[:pudisplayname].first
2✔
81
          user.family_name = ldap_person[:sn].first
2✔
82
          user.given_name = ldap_person[:givenname].first
2✔
83
          user.provider = "cas"
2✔
84
          user.save
2✔
85
        end
86
        user
2✔
87
      end
88
    end
89

90
    # If any required LDAP fields are missing, return true
91
    # @param ldap_person [Net::LDAP::Entry] an LDAP entry representing a person
92
    # @return [Boolean] true if the LDAP entry is missing required fields, false otherwise
93
    def check_for_malformed_ldap_entries(ldap_person)
3✔
94
      uid_blank = ldap_person[:uid].blank?
38✔
95
      edupersonprincipalname_blank = ldap_person[:edupersonprincipalname].blank?
38✔
96
      malformed = uid_blank || edupersonprincipalname_blank
38✔
97
      malformed
38✔
98
    end
99

100
    def default_ldap_connection
3✔
101
      @default_ldap_connection ||= Net::LDAP.new host: "ldap.princeton.edu", base: "o=Princeton University,c=US", port: 636,
22✔
102
                                                  encryption: {
103
                                                    method: :simple_tls,
104
                                                    tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
105
                                                  }
106
    end
107
  end
108
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc