yast/yast-yast2

View on GitHub
library/general/src/lib/cfa/login_defs.rb

Summary

Maintainability
A
0 mins
Test Coverage
# Copyright (c) [2019] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "yast"
require "cfa/base_model"
require "yast2/target_file"
require "yast2/execute"

Yast.import "FileUtils"

module CFA
  # Model to handle login.defs configuration files
  #
  # @example Reading a value
  #   file = LoginDefs.new(file_path: "/etc/login.defs")
  #   file.load
  #   file.encrypt_method #=> "SHA512"
  #
  # @example Writing a value
  #   file = LoginDefs.new(file_path: "/etc/login.defs.d/70-yast.defs")
  #   file.encrypt_method = "DES"
  #   file.save
  #
  # @example Loading shortcut
  #   file = LoginDefs.load(file_path: "/etc/login.defs.d/70-yast.defs")
  #   file.encrypt_method #=> "SHA512"
  class LoginDefs < BaseModel
    include Yast::Logger

    # @return [Array<Symbol>] List of known login.defs attributes
    KNOWN_ATTRIBUTES = [
      :character_class,
      :create_home,
      :encrypt_method,
      :fail_delay,
      :gid_max,
      :gid_min,
      :groupadd_cmd,
      :home_mode,
      :pass_max_days,
      :pass_min_days,
      :pass_warn_age,
      :sub_gid_min,
      :sub_gid_max,
      :sub_gid_count,
      :sub_uid_min,
      :sub_uid_max,
      :sub_uid_count,
      :sys_gid_max,
      :sys_gid_min,
      :sys_uid_max,
      :sys_uid_min,
      :uid_max,
      :uid_min,
      :umask,
      :useradd_cmd,
      :userdel_postcmd,
      :userdel_precmd,
      :usergroups_enab
    ].freeze

    DEFAULT_PATH = "/etc/login.defs".freeze

    class << self
      # Returns the list of known attributes
      #
      # @return [Array<Symbol>]
      def known_attributes
        KNOWN_ATTRIBUTES
      end

      # Instantiates and loads a file
      #
      # This method is basically a shortcut to instantiate and load the content in just one call.
      #
      # @param file_handler [#read,#write] something able to read/write a string (like File)
      # @param file_path    [String] File path
      # @return [LoginDefs] File with the already loaded content
      def load(file_path: DEFAULT_PATH, file_handler: Yast::TargetFile)
        new(file_path: file_path, file_handler: file_handler).tap(&:load)
      end
    end

    attributes(
      known_attributes.each_with_object({}) { |a, hsh| hsh[a] = a.to_s.upcase }
    )

    # @return [String] File path
    attr_reader :file_path

    # Constructor
    #
    # @param file_handler [#read,#write] something able to read/write a string (like File)
    # @param file_path    [String] File path
    #
    # @see CFA::BaseModel#initialize
    def initialize(file_path: DEFAULT_PATH, file_handler: Yast::TargetFile)
      super(AugeasParser.new("login_defs.lns"), file_path, file_handler: file_handler)
    end

    # Determines whether an attribute has a value
    #
    # @return [Boolean] +true+ if it is defined; +false+ otherwise.
    def present?(attr)
      !public_send(attr).nil?
    end

    # Returns the list of attributes with a value
    #
    # @return [Array<Symbol>] List of attribute names
    # @see #present?
    def present_attributes
      self.class.known_attributes.select { |a| present?(a) }
    end

    # Determines the list of conflicting attributes for two files
    #
    # Two attributes are conflicting when both of them are defined with
    # different values.
    #
    # @param other [BaseModel] The file to compare with
    # @return [Array<Symbol>] List of conflicting attributes
    def conflicts(other)
      conflicting_attrs = present_attributes & other.present_attributes
      conflicting_attrs.reject { |a| public_send(a) == other.public_send(a) }
    end

    # Loads the file content
    #
    # If the file does not exist, consider the file as empty.
    #
    # @see CFA::BaseModel#load
    def load
      super
    rescue Errno::ENOENT # PATH does not exist yet
      self.data = @parser.empty
      @loaded = true
    end

    # Saves the file content
    #
    # It creates the directory if it does not exist.
    #
    # @see CFA::BaseModel#save
    def save
      directory = File.dirname(file_path)
      Yast::Execute.on_target("/usr/bin/mkdir", "--parents", directory) if !Yast::FileUtils.IsDirectory(directory)
      super
    end
  end
end