How to valid input to matches data in CSV file - ruby

I'm using Ruby 2.7 above. I've been working this task and still learning. I'm pretty sure that I am not using the right code. This task require me to do a mimic atm program. One of the requirements is where I need to check user valid inputs are matches the data in the CSV.file before user can access the program.
I'm using ruby (not allowed to use rails or any advance ruby code). I searched for similar program anywhere for reference but mostly does not involve with CSV file. How do I check that input from user is valid and matches in CSV file? I'm having trouble on how to do a validation and how to valid with two inputs (username and password). This program is run on command-line. Apologies if im not being clear enough. Can you tell me from my code where I'm going wrong please?
I have three .rb files and two csv files. I am not sure if I'm supposed to create two separate csv files.
function.rb (where all the functions)
login.rb (verify username and password from 'user.csv' file before proceed to system.rb file)
system.rb (the main where all data save or changes in 'account.csv' file)
below is function.rb file.
require 'csv'
class Function
def log_in(user)
CSV.foreach('user.csv', 'r', headers => true) do |row|
#check the user is valid, else error
if row[0] == uname && row[1] == pwd
puts "succesfully login"
ATMSystem.main_menu
end
end
if login == false
puts "invalid credentials."
Login.log_menu
end
end
login.rb file
require './function'
class Inn
def signin
function = AtmFunction.new
puts "Account login"
puts "Enter username"
uname = gets.chomp
puts "Enter password"
pwd = gets.chomp
user = [uname, pwd]
function.log_in(user)
end
end

Let's say this is your users.csv file:
name,password
bob,1234
alice,5678
This is one possible option.
Load the file into an array of hashes Enumerable#to_h and Hash#transform_keys:
require 'csv'
data_file = 'user.csv'
user_map = CSV.foreach(data_file, headers: true).map do |row|
row.to_h.transform_keys(&:to_sym)
end
user_map
#=> [{:name=>"bob", :password=>"1234"}, {:name=>"alice", :password=>"5678"}]
Then, given the input from the user:
input_username = 'bob'
input_password = '1234'
Check if user exists and in case compare the password:
user = user_map.find { |h| h[:name] == input_username }
#=> {:name=>"bob", :password=>"1234"}
user[:password] == input_password
#=> true
Check the password if Enumerable#find returns a non nil value: the user doesn't exist:
input_username = 'ron'
user = user_map.find { |h| h[:name] == input_username }
user
#=> nil
Following your implementation you can also write:
login_passed = false
CSV.foreach(data_file, headers: true) do |row|
login_passed = row['name'] == input_username && row['password'] == input_password
break if login_passed
end
login_passed
#=> true (or false)

Related

How do I add login functionality (username/password) to my app with CSV

Ruby newbie here. Basically I've got several users in a CSV file (headers below):
first_name,age,location,gender,phone_number,email,username,password
I want user's to login with their username which will check the CSV file for the corresponding username, and when it finds the username it will ask the user for the password, if the passwords match then it will run the 'user_mainmenu' variable which then takes the user to the User Main Menu.
def user_login
print "Enter username: "
username_access = $stdin.gets.chomp
CSV.foreach('users.csv', headers: true) do |row|
if row["#username"] == username_access then
#user = User.new(row.to_hash)
break
end
end
print "Enter password: "
password_access = $stdin.gets.chomp
CSV.foreach('users.csv', headers: true) do |row|
if row["#password"] == password_access then
user_mainmenu
break
end
end
end
I'm pretty sure I'm not using the right code, I'm just using Ruby (not allowed to use Rails as its in a course and we are learning that later).
I can't find any answers anywhere as most involve Rails.
Apologies if there isn't enough info or if I'm not being clear enough, first time posting on here.
You don’t need to read a CSV file twice. Using CSV#open and CSV::Table#new, one might get the data in handy format into memory:
def user_login
# load CSV
csv = CSV::Table.new(CSV.open('users.csv', headers: true))
print "Enter username: "
username_access = $stdin.gets.chomp
# detect row with this username
row = csv.detect { |e| e["username"] == username_access }
# immediately throw if no such user
raise "No such user" unless row
print "Enter password: "
password_access = $stdin.gets.chomp
raise "Wrong password" unless row["password"] == password_access
# everything is fine, logged in, return user
User.new(row.to_hash)
end
Had a mentor help me:
def user_login
login_start
verified(gets.chomp)
end
def verified(input)
user_row = authentication(input)
if user_row
puts 'Please enter your password:'
print "> "
password = gets.chomp
if user_row['password'] == password
user_mainmenu
else
puts "Incorrect password."
sleep 1
user_login
end
else
failed
end
end
def authentication(username)
CSV.open('users.csv', headers: true).find { |row| row['username'] == username }
end
def failed
puts "Username not recognised. Please try again."
sleep(1)
user_login
end
def login_start
puts "Enter username:"
print "> "
end

Digesting value in YAML file into MD5 hash

I have a YAML file containing usernames and passwords.
Overview of YAML:
users:
test:
password: test
test2:
password: test2
I want to encrypt the password value into an MD5 hash using Digest::MD5 for example:
user:
Lost Bam:
password: testtesttest #<=I want to overwrite this password with a MD5 hash
In Digest is there a way to encrypt a hash value? If so how do I implement this into a YAML file?
md5.rb Source:
require 'yaml'
require 'digest'
private
def load_file
File.exist?('info.yml') ? YAML.load_file('info.yml') : {users: {}}
end
def read_file
File.read('info.yml')
end
def save_file( hash )
File.open('info.yml', 'w') { |f| f.write(hash.to_yaml)}
end
def add_user
hash = load_file
hash["users"][prompt('Enter username:')] =
{ "password" => prompt('Enter password:') }
puts "Encrypt information?"
information = gets.chomp
case input
when /yes/i
# hash = Digest::MD5.digest(["password"]'value')<-Doesn't work
#
#This is where I want to be able to encrypt the
#value of the password key that was entered by the user
#
# save_file( hash )
else
puts "Add another?"#Not completed yet
end
save_file( hash )
end
main.rb Source:
require_relative 'md5.rb'
def main
puts <<-END.gsub(/^\s*>/, '')
>
>To load information type "L" to quit system type "Q"
>
END
input = gets.chomp.upcase
case input
when "L"
add_user
when "Q"
exit_system
else
exit_lock
end
end
def exit_system
puts "Exiting..."
exit
end
def exit_lock
puts "Locked out, please contact system administrator"
exit
end
def restart
puts "Encrypt more?"
input = gets.chomp
if input =~ /yes/i
return true
else
exit_system
end
end
def prompt( message )
puts message
gets.chomp
end
main
You can use Digest::MD5:
require 'digest'
Digest::MD5.digest('value')
http://ruby-doc.org/stdlib-2.1.0/libdoc/digest/rdoc/Digest.html

How to print all linux users with Puppet and ruby?

I want to create a facter that returns all users.
Facter.add("sysusers") do
setcode do
File.readlines('/etc/passwd').each do |line|
line.match(/^[^:]+/)[0]
end
end
end
Then in my .pp file I have this:
$users = inline_template("<%= scope.lookupvar('sysusers') %>")
$users.each |String $user| {
notify { "$user":}
}
This should work but the facter returns just one letter at a time.
So notify { "$user":} just prints:
Notify[r]
Notify[o]
And then it craches because the next letter is also "o" (two o`s in "root" and root is the first user stated in /etc/passwd).
So how can I print all the users?
EDIT
With the edit to:
Facter.add("sysusers") do
setcode do
File.readlines('/etc/passwd').each do |line|
line.match(/^[^:]+/).to_s
end
end
end
Then the output is:
root#mymachine]# facter sysusers
[
"root:x:0:0:root:/root:/bin/bash
",
"bin:x:1:1:bin:/bin:/usr/bin/nologin
",
"daemon:x:2:2:daemon:/:/usr/bin/nologin
...
...
So it still does not seem to work as expeced.
This is the match you want.
line.match(/^[^:]+/).to_s
When you add [0], it is taking the first character from the string that is the user name.
EDIT
File.readlines('/etc/passwd').collect do |line|
line.match(/^[^:]+/).to_s
end
That will collect an array which to be returned in your setcode.
Parsing /etc/passwd is a clunky approach to your problem.
It's cleaner to use the Etc module
require 'etc'
result = []
Etc.passwd { |user| result << user.name }
result
Use the following ruby code, which reads and prints the user names from /etc/passwd file.
IO.readlines("/etc/passwd").each do |val|
user = val.split(":").first
puts user
end

How can I store user defined data in a hash

Help, I am a noob, just need some advice on this bit of code. I have got most of my program working this part has me stuped i want to get a name and password. Then make the name the key and the password the value. Now it must be user defined.. Then I must be able to pull that hash info again. I thought that return would work... here is my code
def login_prompt
vault = {}
puts "WELCOME! please enter an existing username: "
username = gets.chomp
checkname = Noxread.new
comparename = checkname.read_file
comparename.keys.include?("#{username}")
if comparename == true
puts "please enter your password: "
password = gets.chomp
vault[username]= password
else puts "username already exists!! would you like to retry? (y/n)"
case answer
when /^y/
login_prompt
when /^n/
exit
end
end
end
so that should gather the info. and this is my code to merge that and an hash that i pulled from a file. in a NoxRead class
require_relative 'read'
require 'csv'
class Noxwrite
attr_accessor :name :password
def initialize
#name = name
#password = password
end
def upsum
x = Noxread.new
y = x.read_file
z = login_prompt
y.merge(z) {|name, password| name + ',' + password}
return y
end
def write_file
ehash = upsum
CSV.open("data.csv", "wb") do |csv|
csv << ehash
end
end
end
What is the problem with this code. Seems fine, apart from the fact that passwords should not be read like this in open text.
When you write something like
user_hash = login_prompt
user_hash will have the hash as desired
{"username"=>"password"}

Ruby regular expressions - speed problem

I want to obtain the information of students in class c79363 ONLY.
The following file(users.txt) contains the user id's
c79363::7117:dputnam,gliao01,hmccon01,crober06,cpurce01,cdavid03,dlevin01,jsmith88
d79363::7117:dputn,gliao0,hmcc01,crob06,cpur01,cdad03,dlen01,jsmh88
f79363::7117:dpnam,gli01,hmcn01,ober06,crce01,cdav03,dln01,jith88
FILENAME=user_info.txt
The other one contains specific information about a user like in this format
jsmith88:*:4185:208:jsmith113:/students/jsmith88:/usr/bin/bash
userd:*:4185:208:jsmith113:/students/jsmith88:/usr/bin/bash
gliao01:*:4185:208:jsmith113:/students/jsmith88:/usr/bin/bash
Here was my solution but was slow! I want to optimize the speed.
pseudo code
I read the file using File.readlines(users.txt) ->
I used split(/,/) -> I then pop array until i had an array with the following values
dputnam,gliao01,hmccon01,crober06,cpurce01,cdavid03,dlevin01,jsmith88
I then continue to read user_info.txt with File.readlines(user_info.txt)
I split(/:/) i have USER ARRAY
Finally I compared the first entry USER ARRAY with my users in class c79363.
user_info = {}
File.foreach("user_info.txt") {|line| user_info[line[/^[^:]+/]] = line.chomp}
class_name = "c79363"
File.foreach("users.txt") do |line|
next unless line[/^[^:]+/] == class_name
line[/[^:]+$/].rstrip.split(/,/).each do |user|
puts user_info[user] if user_info.has_key?(user)
end
end
user_info = {}
File.readlines("users_info.txt").each do |line|
user_info[line.split(/:/,2)[0]] = line.chomp
end
class_name = "c79363"
File.readlines("users.txt").each do |line|
line=line.strip.split(/:/)
if line[0] == class_name then
line[-1].split(",").each {|u| puts user_info[u] if user_info[u] }
end
end

Resources